#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("UI管理"); if (window == null) { window = CreateWindow("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 _uiNames = new Dictionary(); private Dictionary _uiJsonDatas = new Dictionary(); private bool _isWindow = true; private UILayer _layer = UILayer.NormalLayer; private IEnumerable 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(jsonData.path); } EditorGUILayout.ObjectField(AssetDatabase.LoadAssetAtPath(jsonData.path), typeof(GameObject), true); EditorGUILayout.ObjectField(AssetDatabase.LoadAssetAtPath(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(); 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 list = new List(); 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>(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 CreateAllInstancesOf() { 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 GetAllUIViewTypes() { // 查找所有程序集中的UIView类型实现 var uiviewType = typeof(UIView); List result = new List(); // 遍历所有已加载的程序集 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; } /// /// 根据相对路径获取资源的GUID /// /// 相对于Assets目录的路径,例如:"Assets/UI/Prefabs/LoginPanel.prefab" /// 资源的GUID字符串,如果路径无效则返回空字符串 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); } }