Files
plugin-library/Assets/UI/Scripts/UIFramework/Editor/UICreateWindow.cs
2025-09-18 18:25:40 +08:00

564 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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同时会清除JsonUIType中相关数据", "确定", "取消"))
{
// 清除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);
}
}