470 lines
18 KiB
C#
470 lines
18 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using AlicizaX.UI.Editor;
|
|
using AlicizaX.UI.Runtime;
|
|
using Sirenix.Utilities.Editor;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEditor.Callbacks;
|
|
using UnityEngine.UI;
|
|
|
|
namespace AlicizaX.UI.Editor
|
|
{
|
|
enum EBindType
|
|
{
|
|
None,
|
|
Widget,
|
|
ListCom,
|
|
}
|
|
|
|
[Serializable]
|
|
class UIBindData
|
|
{
|
|
public string Name;
|
|
public List<Component> BindCom;
|
|
public EBindType BindType;
|
|
|
|
public UIBindData(string name, List<Component> bindCom, EBindType bindType = EBindType.None)
|
|
{
|
|
Name = name;
|
|
BindCom = bindCom;
|
|
BindType = bindType;
|
|
}
|
|
|
|
public UIBindData(string name, Component bindCom, EBindType bindType = EBindType.None)
|
|
{
|
|
Name = name;
|
|
BindCom = new List<Component>() { bindCom };
|
|
BindType = bindType;
|
|
}
|
|
}
|
|
|
|
|
|
public static class UIScriptGeneratorHelper
|
|
{
|
|
private static UIGenerateConfiguration _uiGenerateConfiguration;
|
|
|
|
static UIGenerateConfiguration UIGenerateConfiguration
|
|
{
|
|
get
|
|
{
|
|
if (_uiGenerateConfiguration == null)
|
|
{
|
|
_uiGenerateConfiguration = UIGenerateConfiguration.Instance;
|
|
}
|
|
|
|
return _uiGenerateConfiguration;
|
|
}
|
|
}
|
|
|
|
private static string GetVerType(string uiName)
|
|
{
|
|
foreach (var pair in UIGenerateConfiguration.UIElementRegexConfigs)
|
|
{
|
|
if (uiName.StartsWith(pair.uiElementRegex))
|
|
{
|
|
return pair.componentType;
|
|
}
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
private static string[] SplitComName(string name)
|
|
{
|
|
bool hasCom = name.Contains(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName);
|
|
if (!hasCom) return null;
|
|
|
|
string comStr = name.Substring(0, name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName));
|
|
return comStr.Split(UIGenerateConfiguration.UIGenerateCommonData.ComCheckSplitName);
|
|
}
|
|
|
|
|
|
private static string GetKeyName(string key, string componentName)
|
|
{
|
|
return $"{key}{componentName.Substring(componentName.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName) + 1)}";
|
|
}
|
|
|
|
private static List<UIBindData> UIBindDatas = new List<UIBindData>();
|
|
private static string GenerateNameSpace = string.Empty;
|
|
|
|
private static List<string> ArrayComs = new List<string>();
|
|
|
|
private static void GetBindData(Transform root)
|
|
{
|
|
for (int i = 0; i < root.childCount; ++i)
|
|
{
|
|
Transform child = root.GetChild(i);
|
|
|
|
bool hasWdiget = child.GetComponent<UIHolderObjectBase>() != null;
|
|
|
|
if (UIGenerateConfiguration.UIGenerateCommonData.ExcludeKeywords.Any(k => child.name.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0)) continue;
|
|
|
|
bool isArrayComs = child.name.StartsWith(UIGenerateConfiguration.UIGenerateCommonData.ArrayComSplitName);
|
|
if (hasWdiget)
|
|
{
|
|
CollectWidget(child);
|
|
}
|
|
else if (isArrayComs)
|
|
{
|
|
string splitCode = UIGenerateConfiguration.UIGenerateCommonData.ArrayComSplitName;
|
|
int lastIndex = child.name.LastIndexOf(splitCode);
|
|
string text = child.name.Substring(child.name.IndexOf(splitCode) + 1, lastIndex - 1);
|
|
if (ArrayComs.Contains(text)) continue;
|
|
ArrayComs.Add(text);
|
|
List<Transform> arrayComs = new List<Transform>();
|
|
for (int j = 0; j < root.childCount; j++)
|
|
{
|
|
if (root.GetChild(j).name.Contains(text))
|
|
{
|
|
arrayComs.Add(root.GetChild(j));
|
|
}
|
|
}
|
|
|
|
CollectArrayComponent(arrayComs, text);
|
|
}
|
|
else if (!isArrayComs && !hasWdiget)
|
|
{
|
|
CollectComponent(child);
|
|
GetBindData(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void CollectComponent(Transform node)
|
|
{
|
|
string[] comArray = SplitComName(node.name);
|
|
if (comArray != null)
|
|
{
|
|
foreach (var com in comArray)
|
|
{
|
|
string typeName = GetVerType(com);
|
|
if (string.IsNullOrEmpty(typeName)) continue;
|
|
|
|
Component component = node.GetComponent(typeName);
|
|
if (component != null)
|
|
{
|
|
string keyName = GetKeyName(com, node.name);
|
|
if (UIBindDatas.Exists(a => a.Name == keyName))
|
|
{
|
|
Debug.LogError($"Duplicate key found: {keyName}");
|
|
continue;
|
|
}
|
|
|
|
UIBindDatas.Add(new UIBindData(keyName, component));
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"{node.name} does not have component of type {typeName}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void CollectWidget(Transform node)
|
|
{
|
|
if (node.name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName) != -1 && node.name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckSplitName) != -1)
|
|
{
|
|
Debug.LogWarning($"{node.name} 子组件不能包含规则定义符号!");
|
|
return;
|
|
}
|
|
|
|
UIHolderObjectBase component = node.GetComponent<UIHolderObjectBase>();
|
|
string keyName = node.name;
|
|
if (UIBindDatas.Exists(a => a.Name == keyName))
|
|
{
|
|
Debug.LogError($"Duplicate key found: {keyName}");
|
|
return;
|
|
}
|
|
|
|
UIBindDatas.Add(new UIBindData(keyName, component, EBindType.Widget));
|
|
}
|
|
|
|
private static void CollectArrayComponent(List<Transform> arrayNode, string nodeName)
|
|
{
|
|
string[] comArray = SplitComName(nodeName);
|
|
arrayNode = arrayNode.OrderBy(s => int.Parse(s.name.Split('*').Last())).ToList();
|
|
List<UIBindData> tempBindDatas = new List<UIBindData>(comArray.Length);
|
|
|
|
if (comArray != null)
|
|
{
|
|
int index = 0;
|
|
foreach (var com in comArray)
|
|
{
|
|
foreach (var node in arrayNode)
|
|
{
|
|
string typeName = GetVerType(com);
|
|
if (string.IsNullOrEmpty(typeName)) continue;
|
|
|
|
Component component = node.GetComponent(typeName);
|
|
if (component != null)
|
|
{
|
|
string keyName = GetKeyName(com, nodeName) + "List";
|
|
if (tempBindDatas.Count - 1 < index) tempBindDatas.Add(new UIBindData(keyName, new List<Component>(), EBindType.ListCom));
|
|
tempBindDatas[index].BindCom.Add(component);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"{node.name} does not have component of type {typeName}");
|
|
}
|
|
}
|
|
|
|
index++;
|
|
}
|
|
}
|
|
|
|
UIBindDatas.AddRange(tempBindDatas.ToArray());
|
|
}
|
|
|
|
|
|
private static string GetRefrenceNameSpace()
|
|
{
|
|
StringBuilder refrenceNameSpaceBuilder = new StringBuilder();
|
|
HashSet<string> nameSpaces = new HashSet<string>();
|
|
nameSpaces.Add("UnityEngine");
|
|
refrenceNameSpaceBuilder.Append($"using UnityEngine;\n");
|
|
foreach (var bindData in UIBindDatas)
|
|
{
|
|
string nameSpace = bindData.BindCom.FirstOrDefault().GetType().Namespace;
|
|
if (bindData.BindType == EBindType.ListCom)
|
|
{
|
|
if (!nameSpaces.Contains("using System.Collections.Generic;"))
|
|
{
|
|
refrenceNameSpaceBuilder.Append("using System.Collections.Generic;\n");
|
|
}
|
|
}
|
|
|
|
if (!nameSpaces.Contains(nameSpace) && !string.IsNullOrEmpty(nameSpace))
|
|
{
|
|
nameSpaces.Add(nameSpace);
|
|
refrenceNameSpaceBuilder.Append($"using {nameSpace};\n");
|
|
}
|
|
}
|
|
|
|
return refrenceNameSpaceBuilder.ToString();
|
|
}
|
|
|
|
private static string GetVarText(List<UIBindData> uiBindDatas)
|
|
{
|
|
StringBuilder varTextBuilder = new StringBuilder();
|
|
foreach (var bindData in uiBindDatas)
|
|
{
|
|
var varName = bindData.Name;
|
|
varTextBuilder.Append("\t\t[SerializeField]\n");
|
|
varTextBuilder.Append("\t\t[ReadOnly]\n");
|
|
varTextBuilder.Append("\t\t[HideLabel]\n");
|
|
|
|
if (bindData.BindType == EBindType.None)
|
|
{
|
|
varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} m{varName};\n");
|
|
varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} {varName} => m{varName};\n\n");
|
|
}
|
|
else if (bindData.BindType == EBindType.ListCom)
|
|
{
|
|
varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} [] m{varName} = new {bindData.BindCom.FirstOrDefault().GetType().Name}[{bindData.BindCom.Count}];\n");
|
|
varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} [] {varName} => m{varName};\n\n");
|
|
}
|
|
else if (bindData.BindType == EBindType.Widget)
|
|
{
|
|
varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} m{varName};\n");
|
|
varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} {varName} => m{varName};\n\n");
|
|
}
|
|
}
|
|
|
|
return varTextBuilder.ToString();
|
|
}
|
|
|
|
private static string GenerateScript(string className)
|
|
{
|
|
StringBuilder scriptBuilder = new StringBuilder();
|
|
scriptBuilder.Append(GetRefrenceNameSpace());
|
|
|
|
scriptBuilder.Append("using Sirenix.OdinInspector;\n");
|
|
scriptBuilder.Append("using AlicizaX.UI.Runtime;\n");
|
|
scriptBuilder.Append($"namespace {GenerateNameSpace}\n");
|
|
scriptBuilder.Append("{\n");
|
|
scriptBuilder.Append($"\t#Attribute#\n");
|
|
scriptBuilder.Append($"\tpublic class {className} : UIHolderObjectBase\n");
|
|
scriptBuilder.Append("\t{\n");
|
|
|
|
scriptBuilder.Append($"\t\tpublic const string ResTag = #Tag#;\n");
|
|
|
|
scriptBuilder.Append("\t\t#region Generated by Script Tool\n\n");
|
|
scriptBuilder.Append(GetVarText(UIBindDatas));
|
|
scriptBuilder.Append("\n\t\t#endregion\n");
|
|
|
|
scriptBuilder.Append("\t}\n");
|
|
scriptBuilder.Append("}\n");
|
|
|
|
return scriptBuilder.ToString();
|
|
}
|
|
|
|
public static void GenerateAndAttachScript(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
|
|
{
|
|
UIBindDatas.Clear();
|
|
GenerateNameSpace = scriptGenerateData.NameSpace;
|
|
ArrayComs.Clear();
|
|
|
|
string className = $"{UIGenerateConfiguration.UIGenerateCommonData.GeneratePrefix}_{targetObject.name}";
|
|
|
|
string scriptSavePath = Path.Combine(scriptGenerateData.GenerateHolderCodePath, className + ".cs");
|
|
|
|
|
|
GetBindData(targetObject.transform);
|
|
|
|
string scriptContent = GenerateScript(className);
|
|
string TagName = $"\"{targetObject.name}\"";
|
|
if (scriptGenerateData.LoadType == EUIResLoadType.Resources)
|
|
{
|
|
string matchWords = string.Empty;
|
|
UIGenerateConfiguration.UIGenerateCommonData.CombineWords.Any(t =>
|
|
{
|
|
if (targetObject.name.IndexOf(t.Key) >= 0)
|
|
{
|
|
matchWords = t.Value;
|
|
}
|
|
|
|
return targetObject.name.IndexOf(t.Key) >= 0;
|
|
});
|
|
string finalPath = Path.Combine(scriptGenerateData.UIPrefabRootPath.Replace("Assets/", "").Replace("Resources/", ""), matchWords);
|
|
string didc = Path.Combine(scriptGenerateData.UIPrefabRootPath, matchWords);
|
|
if (!Directory.Exists(didc))
|
|
{
|
|
Directory.CreateDirectory(didc);
|
|
}
|
|
|
|
finalPath = Utility.Path.GetRegularPath(finalPath);
|
|
TagName = $"\"{finalPath}/{targetObject.name}\"";
|
|
}
|
|
|
|
scriptContent = scriptContent.Replace("#Tag#", TagName);
|
|
|
|
|
|
//#Attribute#
|
|
string uiAttribute = $"[UIRes({className}.ResTag, EUIResLoadType.{scriptGenerateData.LoadType.ToString()})]";
|
|
|
|
scriptContent = scriptContent.Replace("#Attribute#", uiAttribute);
|
|
|
|
|
|
if (File.Exists(scriptSavePath))
|
|
{
|
|
string oldText = File.ReadAllText(scriptSavePath);
|
|
if (oldText.Equals(scriptContent))
|
|
{
|
|
EditorPrefs.SetString("Generate", className);
|
|
CheckHasAttach();
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
File.WriteAllText(scriptSavePath, scriptContent);
|
|
EditorPrefs.SetString("Generate", className);
|
|
AssetDatabase.Refresh();
|
|
}
|
|
|
|
|
|
[DidReloadScripts]
|
|
private static void CheckHasAttach()
|
|
{
|
|
bool has = EditorPrefs.HasKey("Generate");
|
|
if (has)
|
|
{
|
|
UIBindDatas.Clear();
|
|
ArrayComs.Clear();
|
|
var className = EditorPrefs.GetString("Generate");
|
|
var targetObject = UIGenerateWindow.GetTargetObject();
|
|
EditorPrefs.DeleteKey("Generate");
|
|
GetBindData(targetObject.transform);
|
|
AttachScriptToGameObject(targetObject, className);
|
|
Debug.Log($"Generate {className} Successfully attached to game object");
|
|
UIGenerateWindow.CloseWindow();
|
|
}
|
|
}
|
|
|
|
|
|
private static void AttachScriptToGameObject(GameObject targetObject, string scriptClassName)
|
|
{
|
|
Type scriptType = null;
|
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
|
{
|
|
if (assembly.FullName.Contains("Editor")) continue;
|
|
var types = assembly.GetTypes();
|
|
foreach (var type in types)
|
|
{
|
|
if (type.IsClass && !type.IsAbstract && type.Name.Contains(scriptClassName))
|
|
{
|
|
scriptType = type;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (scriptType != null)
|
|
{
|
|
Component component = targetObject.GetOrAddComponent(scriptType);
|
|
FieldInfo[] fields = scriptType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
foreach (FieldInfo field in fields)
|
|
{
|
|
List<Component> componentInObjects = UIBindDatas.Find(data => "m" + data.Name == field.Name)?.BindCom;
|
|
if (componentInObjects != null)
|
|
{
|
|
if (field.FieldType.IsArray)
|
|
{
|
|
// 获取数组元素类型
|
|
Type elementType = field.FieldType.GetElementType();
|
|
// 创建对应类型的数组
|
|
Array array = Array.CreateInstance(elementType, componentInObjects.Count);
|
|
|
|
for (int i = 0; i < componentInObjects.Count; i++)
|
|
{
|
|
Component comp = componentInObjects[i];
|
|
// 检查元素类型是否匹配
|
|
if (elementType.IsInstanceOfType(comp))
|
|
{
|
|
array.SetValue(comp, i);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"元素 {i} 类型不匹配,期望 {elementType.Name},实际为 {comp.GetType().Name}");
|
|
// 处理错误,如跳过或终止赋值
|
|
}
|
|
}
|
|
|
|
field.SetValue(component, array);
|
|
}
|
|
else
|
|
{
|
|
// 非数组字段取第一个元素
|
|
if (componentInObjects.Count > 0)
|
|
{
|
|
// 同样检查类型兼容性
|
|
if (field.FieldType.IsInstanceOfType(componentInObjects[0]))
|
|
{
|
|
field.SetValue(component, componentInObjects[0]);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"字段 {field.Name} 类型不匹配,无法赋值");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"字段 {field.Name} 未找到匹配的组件绑定");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"Could not find the class: {scriptClassName}");
|
|
}
|
|
}
|
|
}
|
|
}
|