564 lines
21 KiB
C#
564 lines
21 KiB
C#
#if UNITY_EDITOR
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEditor;
|
||
using System.Text.RegularExpressions;
|
||
using System.IO;
|
||
using SkierFramework;
|
||
using System;
|
||
using System.Linq;
|
||
using Newtonsoft.Json;
|
||
using YooAsset.Editor;
|
||
|
||
public class UICreateWindow : EditorWindow
|
||
{
|
||
#region MenuItem
|
||
|
||
[MenuItem("Assets/Create/CreateUI")]
|
||
private static void CopyUI()
|
||
{
|
||
if (Selection.activeObject == null || !(Selection.activeObject is GameObject))
|
||
{
|
||
Debug.LogError("请选择UI预制体!");
|
||
return;
|
||
}
|
||
|
||
UICreateWindow.OpenWindow().uiPrefab = Selection.activeObject as GameObject;
|
||
}
|
||
|
||
[MenuItem("Tools/UI管理")]
|
||
public static UICreateWindow OpenWindow()
|
||
{
|
||
var window = GetWindow<UICreateWindow>("UI管理");
|
||
if (window == null)
|
||
{
|
||
window = CreateWindow<UICreateWindow>("UI管理");
|
||
}
|
||
|
||
window.name = "UI管理";
|
||
window.Focus();
|
||
return window;
|
||
}
|
||
|
||
#endregion
|
||
|
||
private string _packageName;
|
||
private string _mInput;
|
||
|
||
private Vector2 _scroll;
|
||
private Vector2 _scroll2;
|
||
private Vector2 _scroll3;
|
||
private string _uiViewTemplate = "UIViewTemplate";
|
||
private string _uiConfig = "UIConfig";
|
||
private string _uiType = "UIType";
|
||
private string _saveUIPath = "SaveUIPath";
|
||
private string _uiName;
|
||
public GameObject uiPrefab;
|
||
private Dictionary<string, string> _uiNames = new Dictionary<string, string>();
|
||
private Dictionary<string, UIConfigJson> _uiJsonDatas = new Dictionary<string, UIConfigJson>();
|
||
|
||
private bool _isWindow = true;
|
||
private UILayer _layer = UILayer.NormalLayer;
|
||
|
||
private IEnumerable<Type> uiViews;
|
||
|
||
private void OnEnable()
|
||
{
|
||
TryGetPath(ref _uiViewTemplate, _uiViewTemplate, ".txt");
|
||
TryGetPath(ref _uiType, _uiType, ".cs");
|
||
TryGetPath(ref _uiConfig, _uiConfig, ".json");
|
||
_saveUIPath = PlayerPrefs.GetString(_saveUIPath, "Assets/Scripts");
|
||
|
||
_uiJsonDatas.Clear();
|
||
_uiNames.Clear();
|
||
// string[] strs = Enum.GetNames(typeof(UIType));
|
||
uiViews = ReflectionHelper.GetAllUIViewTypes();
|
||
foreach (var uiView in uiViews)
|
||
{
|
||
string str = uiView.Name;
|
||
if (str.Equals("Max")) continue;
|
||
|
||
var jsonData = GetUIJson(str);
|
||
if (jsonData == null || string.IsNullOrEmpty(jsonData.path)) continue;
|
||
var scriptPath = GetUIScript(str, true);
|
||
_uiNames.AddOrUpdate(str, scriptPath);
|
||
}
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
EditorGUILayout.LabelField("通过[Tools/UI管理]可以打开");
|
||
_scroll = EditorGUILayout.BeginScrollView(_scroll);
|
||
{
|
||
EditorGUILayout.HelpBox("UI基础文件", MessageType.Info);
|
||
{
|
||
_packageName = StringInputFild("PackageName", "UIPackageName");
|
||
PathField("UI模板文件.txt", ref _uiViewTemplate, nameof(_uiViewTemplate), ".txt");
|
||
PathField("UI配置文件.json", ref _uiConfig, nameof(_uiConfig), ".json");
|
||
PathField("UIType.cs", ref _uiType, nameof(_uiType), ".cs");
|
||
}
|
||
EditorGUILayout.Space(10);
|
||
EditorGUILayout.BeginHorizontal();
|
||
{
|
||
_scroll2 = EditorGUILayout.BeginScrollView(_scroll2, "box", GUILayout.Width(position.width * 0.4f - 6));
|
||
{
|
||
EditorGUILayout.HelpBox("已创建的UI", MessageType.Info);
|
||
_mInput = EditorGUILayout.TextField(_mInput, EditorStyles.toolbarSearchField, GUILayout.Height(20));
|
||
//string[] strs = Enum.GetNames(typeof(UIType));
|
||
foreach (var uiView in uiViews)
|
||
{
|
||
string str = uiView.Name;
|
||
if (str.Equals("Max")) continue;
|
||
if (!string.IsNullOrEmpty(_mInput) && !str.Contains(_mInput)) continue;
|
||
|
||
var jsonData = GetUIJson(str);
|
||
var scriptPath = GetUIScript(str);
|
||
if (jsonData == null || string.IsNullOrEmpty(jsonData.path) ||
|
||
string.IsNullOrEmpty(scriptPath)) continue;
|
||
|
||
var defaultColor = GUI.color;
|
||
if (str.Equals(_uiName))
|
||
{
|
||
GUI.color = Color.yellow;
|
||
}
|
||
|
||
EditorGUILayout.BeginHorizontal("box");
|
||
if (GUILayout.Button("选中"))
|
||
{
|
||
uiPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(jsonData.path);
|
||
}
|
||
|
||
EditorGUILayout.ObjectField(AssetDatabase.LoadAssetAtPath<GameObject>(jsonData.path),
|
||
typeof(GameObject), true);
|
||
EditorGUILayout.ObjectField(AssetDatabase.LoadAssetAtPath<TextAsset>(scriptPath),
|
||
typeof(TextAsset), true);
|
||
EditorGUILayout.EndHorizontal();
|
||
GUI.color = defaultColor;
|
||
}
|
||
|
||
if (GUILayout.Button("标记资源"))
|
||
{
|
||
Mask();
|
||
}
|
||
}
|
||
EditorGUILayout.EndScrollView();
|
||
_scroll3 = EditorGUILayout.BeginScrollView(_scroll3, "box", GUILayout.Width(position.width * 0.6f - 6));
|
||
{
|
||
EditorGUILayout.HelpBox("UI操作", MessageType.Info);
|
||
uiPrefab = EditorGUILayout.ObjectField("UI预制体", uiPrefab, typeof(GameObject), true) as GameObject;
|
||
if (uiPrefab != null)
|
||
{
|
||
_uiName = uiPrefab.name;
|
||
var uiScriptPath = GetUIScript(_uiName);
|
||
if (string.IsNullOrEmpty(uiScriptPath))
|
||
{
|
||
if (GUILayout.Button($"选择创建路径:{_saveUIPath}"))
|
||
{
|
||
var newPath = EditorUtility.OpenFolderPanel("UI生成路径", _saveUIPath, "");
|
||
_saveUIPath = newPath.Replace(Application.dataPath, "Assets");
|
||
PlayerPrefs.SetString(nameof(_saveUIPath), _saveUIPath);
|
||
}
|
||
|
||
if (uiPrefab != null)
|
||
{
|
||
EditorGUILayout.TextField("UI生成路径", $"{_saveUIPath}/{_uiName}.cs");
|
||
}
|
||
|
||
_isWindow = EditorGUILayout.Toggle("是否为窗口", _isWindow);
|
||
_layer = (UILayer)EditorGUILayout.EnumPopup("UILayer设置", _layer);
|
||
|
||
var defaultColor = GUI.color;
|
||
GUI.color = Color.green;
|
||
if (GUILayout.Button("创建UI"))
|
||
{
|
||
// 生成代码
|
||
string str = Regex.Replace(File.ReadAllText(_uiViewTemplate), "UIXXXView", _uiName);
|
||
UIControlData uiControlData = uiPrefab.GetComponent<UIControlData>();
|
||
if (uiControlData != null)
|
||
{
|
||
uiControlData.CopyCodeToClipBoardPrivate();
|
||
}
|
||
|
||
str = Regex.Replace(str, "//UIControlData",
|
||
uiControlData != null ? UnityEngine.GUIUtility.systemCopyBuffer : "");
|
||
string newPath = $"{_saveUIPath}/{_uiName}.cs";
|
||
File.WriteAllText(newPath, str);
|
||
|
||
var jsonData = new UIConfigJson
|
||
{
|
||
uiType = _uiName,
|
||
path = AssetDatabase.GetAssetPath(uiPrefab),
|
||
isWindow = _isWindow,
|
||
uiLayer = _layer.ToString(),
|
||
};
|
||
|
||
_uiJsonDatas.Add(_uiName, jsonData);
|
||
_uiNames.Add(_uiName, newPath);
|
||
SaveJson();
|
||
|
||
// 生成UIType
|
||
var newStr = Regex.Replace(File.ReadAllText(_uiType), "Max,", $"{_uiName},\n\t\tMax,");
|
||
File.Delete(_uiType);
|
||
File.WriteAllText(_uiType, newStr);
|
||
|
||
Debug.Log("生成成功:" + newPath);
|
||
|
||
AssetDatabase.SaveAssets();
|
||
AssetDatabase.Refresh();
|
||
}
|
||
|
||
GUI.color = defaultColor;
|
||
}
|
||
else
|
||
{
|
||
var jsonData = GetUIJson(_uiName);
|
||
if (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(uiPrefab))
|
||
{
|
||
// 预制体资源就是自身
|
||
jsonData.path = UnityEditor.AssetDatabase.GetAssetPath(uiPrefab);
|
||
}
|
||
|
||
EditorGUILayout.ObjectField("已创建脚本",
|
||
AssetDatabase.LoadAssetAtPath(uiScriptPath, typeof(TextAsset)), typeof(TextAsset),
|
||
true);
|
||
jsonData.isWindow = EditorGUILayout.Toggle("是否为窗口", jsonData.isWindow);
|
||
Enum.TryParse(jsonData.uiLayer, out UILayer layer);
|
||
jsonData.uiLayer = EditorGUILayout.EnumPopup("UILayer设置", layer).ToString();
|
||
|
||
var defaultColor = GUI.color;
|
||
GUI.color = Color.green;
|
||
if (GUILayout.Button("保存设置"))
|
||
{
|
||
SaveJson();
|
||
AssetDatabase.SaveAssets();
|
||
AssetDatabase.Refresh();
|
||
}
|
||
|
||
GUI.color = defaultColor;
|
||
|
||
GUI.color = Color.red;
|
||
if (GUILayout.Button("删除UI脚本"))
|
||
{
|
||
if (EditorUtility.DisplayDialog("是否确认删除",
|
||
$"请确认是否删除:\n{uiScriptPath}\n同时会清除Json,UIType中相关数据", "确定", "取消"))
|
||
{
|
||
// 清除UIType中指定类型
|
||
var uiTypeStr = File.ReadAllText(_uiType);
|
||
int index = uiTypeStr.IndexOf(_uiName);
|
||
int leftIndex = uiTypeStr.Substring(0, index).LastIndexOf(',') + 1;
|
||
int rightIndex = uiTypeStr.Substring(index, uiTypeStr.Length - index).IndexOf(',') +
|
||
index + 1;
|
||
var newStr = uiTypeStr.Substring(0, leftIndex) +
|
||
uiTypeStr.Substring(rightIndex, uiTypeStr.Length - rightIndex);
|
||
File.Delete(_uiType);
|
||
File.WriteAllText(_uiType, newStr);
|
||
// 清除UIConfig中的指定类型
|
||
_uiJsonDatas.Remove(_uiName);
|
||
_uiNames.Remove(_uiName);
|
||
SaveJson();
|
||
// 删除文件
|
||
File.Delete(uiScriptPath);
|
||
|
||
AssetDatabase.SaveAssets();
|
||
AssetDatabase.Refresh();
|
||
_uiNames.Remove(_uiName);
|
||
}
|
||
}
|
||
|
||
GUI.color = defaultColor;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
_uiName = "";
|
||
}
|
||
}
|
||
EditorGUILayout.EndScrollView();
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
EditorGUILayout.EndScrollView();
|
||
}
|
||
|
||
private void SaveJson()
|
||
{
|
||
List<UIConfigJson> list = new List<UIConfigJson>();
|
||
foreach (var name in _uiNames.Keys)
|
||
{
|
||
if (_uiJsonDatas.TryGetValue(name, out var data))
|
||
{
|
||
list.Add(data);
|
||
}
|
||
}
|
||
|
||
File.Delete(_uiConfig);
|
||
File.WriteAllText(_uiConfig, JsonConvert.SerializeObject(list, Formatting.Indented));
|
||
}
|
||
|
||
private void TryGetPath(ref string path, string pathName, string endsWith)
|
||
{
|
||
path = PlayerPrefs.GetString(pathName);
|
||
if (!File.Exists(path))
|
||
{
|
||
string[] ids = AssetDatabase.FindAssets(pathName);
|
||
if (ids != null)
|
||
{
|
||
foreach (var id in ids)
|
||
{
|
||
var str = AssetDatabase.GUIDToAssetPath(id);
|
||
if (str.EndsWith(endsWith))
|
||
{
|
||
path = str;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private string GetUIScript(string name, bool tryFind = false)
|
||
{
|
||
if (_uiNames.TryGetValue(name, out string path))
|
||
{
|
||
return path;
|
||
}
|
||
|
||
if (!tryFind)
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
if (GetUIJson(name) == null) return string.Empty;
|
||
|
||
string[] ids = AssetDatabase.FindAssets(name);
|
||
if (ids != null)
|
||
{
|
||
foreach (var id in ids)
|
||
{
|
||
var str = AssetDatabase.GUIDToAssetPath(id);
|
||
if (str.EndsWith(".cs"))
|
||
{
|
||
return str;
|
||
}
|
||
}
|
||
}
|
||
|
||
return string.Empty;
|
||
}
|
||
|
||
private UIConfigJson GetUIJson(string name)
|
||
{
|
||
if (_uiJsonDatas.Count == 0)
|
||
{
|
||
try
|
||
{
|
||
var json = File.ReadAllText(_uiConfig);
|
||
var list = JsonConvert.DeserializeObject<List<UIConfigJson>>(json);
|
||
foreach (var item in list)
|
||
{
|
||
_uiJsonDatas.AddOrUpdate(item.uiType, item);
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError(e);
|
||
}
|
||
}
|
||
|
||
if (_uiJsonDatas.TryGetValue(name, out var jsonData))
|
||
{
|
||
return jsonData;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private string StringInputFild(string name, string endsWith)
|
||
{
|
||
string value = PlayerPrefs.GetString(name);
|
||
if (string.IsNullOrEmpty(value))
|
||
{
|
||
if (!string.IsNullOrEmpty(endsWith))
|
||
{
|
||
PlayerPrefs.SetString(name, endsWith);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
endsWith = value;
|
||
}
|
||
|
||
return EditorGUILayout.TextField(name, endsWith);
|
||
}
|
||
|
||
private void PathField(string name, ref string path, string pathName, string endsWith)
|
||
{
|
||
var obj = EditorGUILayout.ObjectField(name, AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset)),
|
||
typeof(TextAsset), true);
|
||
if (obj != null)
|
||
{
|
||
var newPath = AssetDatabase.GetAssetPath(obj);
|
||
if (newPath.EndsWith(endsWith) && !newPath.Equals(path))
|
||
{
|
||
path = newPath;
|
||
PlayerPrefs.SetString(pathName, path);
|
||
}
|
||
}
|
||
}
|
||
|
||
#region Mask
|
||
|
||
public void Mask()
|
||
{
|
||
// //清空主包旧数据
|
||
AssetBundleCollectorPackage assetBundleCollectorPackage = null;
|
||
foreach (var package in AssetBundleCollectorSettingData.Setting.Packages)
|
||
{
|
||
if (package.PackageName == _packageName)
|
||
{
|
||
assetBundleCollectorPackage = package;
|
||
}
|
||
}
|
||
|
||
if (assetBundleCollectorPackage == null)
|
||
{
|
||
var setting = YooAsset.Editor.AssetBundleCollectorSettingData.Setting;
|
||
setting.ShowPackageView = true;
|
||
setting.UniqueBundleName = true;
|
||
//创建Package文件
|
||
assetBundleCollectorPackage = YooAsset.Editor.AssetBundleCollectorSettingData.CreatePackage(_packageName);
|
||
}
|
||
|
||
//检测Packages是否存在TestPackage
|
||
assetBundleCollectorPackage.PackageName = _packageName;
|
||
assetBundleCollectorPackage.EnableAddressable = false;
|
||
assetBundleCollectorPackage.IncludeAssetGUID = true;
|
||
assetBundleCollectorPackage.AutoCollectShaders = true;
|
||
assetBundleCollectorPackage.IgnoreRuleName = "NormalIgnoreRule";
|
||
|
||
//标记json配置文件
|
||
if (!string.IsNullOrEmpty(_uiConfig))
|
||
{
|
||
var collectorGroup = ReflectionHelper.RemoveGroup(assetBundleCollectorPackage, "Config");
|
||
var guid = ReflectionHelper.GetGUIDByRelativePath(_uiConfig);
|
||
AssetBundleCollector collector = new AssetBundleCollector()
|
||
{
|
||
CollectPath = _uiConfig,
|
||
CollectorGUID = guid,
|
||
CollectorType = ECollectorType.MainAssetCollector,
|
||
AddressRuleName = nameof(AddressByFolderAndFileName),
|
||
};
|
||
YooAsset.Editor.AssetBundleCollectorSettingData.CreateCollector(collectorGroup, collector);
|
||
}
|
||
|
||
//标记预制体
|
||
foreach (var data in _uiJsonDatas)
|
||
{
|
||
var collectorGroup = ReflectionHelper.RemoveGroup(assetBundleCollectorPackage, "Prefab");
|
||
|
||
|
||
var guid = ReflectionHelper.GetGUIDByRelativePath(data.Value.path);
|
||
AssetBundleCollector collector = new AssetBundleCollector()
|
||
{
|
||
CollectPath = data.Value.path,
|
||
CollectorGUID = guid,
|
||
CollectorType = ECollectorType.MainAssetCollector,
|
||
AddressRuleName = nameof(AddressByFolderAndFileName),
|
||
AssetTags = data.Value.uiLayer.ToString(),
|
||
};
|
||
YooAsset.Editor.AssetBundleCollectorSettingData.CreateCollector(collectorGroup, collector);
|
||
}
|
||
|
||
YooAsset.Editor.AssetBundleCollectorSettingData.SaveFile();
|
||
|
||
Debug.Log("MarkAsset Successful");
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
#endif
|
||
|
||
|
||
static class ReflectionHelper
|
||
{
|
||
public static IEnumerable<T> CreateAllInstancesOf<T>()
|
||
{
|
||
return typeof(ReflectionHelper).Assembly.GetTypes() //获取当前类库下所有类型
|
||
.Where(t => typeof(T).IsAssignableFrom(t)) //获取间接或直接继承t的所有类型
|
||
.Where(t => !t.IsAbstract && t.IsClass) //获取非抽象类 排除接口继承
|
||
.Select(t => (T)Activator.CreateInstance(t)); //创造实例,并返回结果(项目需求,可删除)
|
||
}
|
||
|
||
// 新增方法:专门用于查找UIView类型的所有实现类
|
||
public static IEnumerable<Type> GetAllUIViewTypes()
|
||
{
|
||
// 查找所有程序集中的UIView类型实现
|
||
var uiviewType = typeof(UIView);
|
||
List<Type> result = new List<Type>();
|
||
|
||
// 遍历所有已加载的程序集
|
||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||
{
|
||
try
|
||
{
|
||
result.AddRange(assembly.GetTypes()
|
||
.Where(t => uiviewType.IsAssignableFrom(t))
|
||
.Where(t => !t.IsAbstract && t.IsClass));
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// 忽略无法访问的程序集
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据相对路径获取资源的GUID
|
||
/// </summary>
|
||
/// <param name="relativePath">相对于Assets目录的路径,例如:"Assets/UI/Prefabs/LoginPanel.prefab"</param>
|
||
/// <returns>资源的GUID字符串,如果路径无效则返回空字符串</returns>
|
||
public static string GetGUIDByRelativePath(string relativePath)
|
||
{
|
||
// 检查路径是否为空
|
||
if (string.IsNullOrEmpty(relativePath))
|
||
{
|
||
Debug.LogError("相对路径不能为空");
|
||
return string.Empty;
|
||
}
|
||
|
||
// 检查路径是否存在
|
||
if (!AssetDatabase.IsValidFolder(Path.GetDirectoryName(relativePath)) &&
|
||
!System.IO.File.Exists(Path.Combine(Application.dataPath, relativePath.Replace("Assets/", ""))))
|
||
{
|
||
Debug.LogError(string.Format("路径不存在: {0}", relativePath));
|
||
return string.Empty;
|
||
}
|
||
|
||
// 获取GUID
|
||
string guid = AssetDatabase.AssetPathToGUID(relativePath);
|
||
|
||
if (string.IsNullOrEmpty(guid))
|
||
{
|
||
Debug.LogWarning(string.Format("无法获取路径的GUID: {0}", relativePath));
|
||
}
|
||
|
||
return guid;
|
||
}
|
||
|
||
public static AssetBundleCollectorGroup RemoveGroup(AssetBundleCollectorPackage package, string groupName)
|
||
{
|
||
//删除的Group
|
||
var groupToDelete = package.Groups.FirstOrDefault(g => g.GroupName == groupName);
|
||
|
||
if (groupToDelete == null)
|
||
{
|
||
UnityEngine.Debug.LogError($"在Package中未找到Group '{groupName}'!");
|
||
}
|
||
|
||
// 3. 从Package的Groups集合中删除该Group
|
||
package.Groups.Remove(groupToDelete);
|
||
return YooAsset.Editor.AssetBundleCollectorSettingData.CreateGroup(package, groupName);
|
||
}
|
||
} |