This commit is contained in:
2025-09-18 18:25:40 +08:00
parent 8ee325cc01
commit ce06ca8571
220 changed files with 165196 additions and 21 deletions

View File

@@ -0,0 +1,564 @@
#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);
}
}