optimiza code
This commit is contained in:
parent
4de5156174
commit
110c3451ea
@ -29,7 +29,7 @@ namespace AlicizaX.UI.Editor
|
||||
{
|
||||
if (CheckCanGenerate(selectedObject, config))
|
||||
{
|
||||
UIScriptGeneratorHelper.GenerateAndAttachScript(selectedObject, config);
|
||||
UIScriptGeneratorHelper.GenerateUIBindScript(selectedObject, config);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using AlicizaX.UI.Runtime;
|
||||
using UnityEditor;
|
||||
@ -21,102 +23,105 @@ namespace AlicizaX.UI.Editor
|
||||
void WriteUIScriptContent(string className, string scriptContent, UIScriptGenerateData scriptGenerateData);
|
||||
|
||||
bool CheckCanGenerate(GameObject targetObject, UIScriptGenerateData scriptGenerateData);
|
||||
|
||||
string GetReferenceNamespace(List<UIBindData> uiBindDatas);
|
||||
|
||||
string GetVariableContent(List<UIBindData> uiBindDatas);
|
||||
}
|
||||
|
||||
public class DefaultIuiGeneratorRuleHelper : IUIGeneratorRuleHelper
|
||||
|
||||
public class DefaultUIGeneratorRuleHelper : IUIGeneratorRuleHelper
|
||||
{
|
||||
public string GetPrivateComponentByNameRule(string regexName, string componentName, EBindType bindType)
|
||||
{
|
||||
string endPrefix = bindType == EBindType.ListCom ? "List" : string.Empty;
|
||||
var endPrefix = bindType == EBindType.ListCom ? "List" : string.Empty;
|
||||
var common = UIGenerateConfiguration.Instance.UIGenerateCommonData;
|
||||
int endNameIndex = componentName.IndexOf(common.ComCheckEndName, StringComparison.Ordinal);
|
||||
|
||||
string componentSuffix = endNameIndex >= 0 ? componentName.Substring(endNameIndex + 1) : componentName;
|
||||
var endNameIndex = componentName.IndexOf(common.ComCheckEndName, StringComparison.Ordinal);
|
||||
|
||||
var componentSuffix = endNameIndex >= 0 ? componentName.Substring(endNameIndex + 1) : componentName;
|
||||
return $"m{regexName}{componentSuffix}{endPrefix}";
|
||||
}
|
||||
|
||||
public string GetPublicComponentByNameRule(string variableName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(variableName)) return variableName;
|
||||
if (variableName.Length > 1)
|
||||
return variableName.Substring(1);
|
||||
return variableName;
|
||||
return variableName.Length > 1 ? variableName.Substring(1) : variableName;
|
||||
}
|
||||
|
||||
public string GetClassGenerateName(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
|
||||
{
|
||||
var config = UIGenerateConfiguration.Instance.UIGenerateCommonData;
|
||||
string prefix = config.GeneratePrefix ?? "ui";
|
||||
var prefix = config.GeneratePrefix ?? "ui";
|
||||
return $"{prefix}_{targetObject.name}";
|
||||
}
|
||||
|
||||
public string GetUIResourceSavePath(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
|
||||
{
|
||||
if (targetObject == null)
|
||||
return $"\"{nameof(targetObject)}\"";
|
||||
if (targetObject == null) return $"\"{nameof(targetObject)}\"";
|
||||
|
||||
var defaultPath = targetObject.name;
|
||||
var assetPath = UIGenerateQuick.GetPrefabAssetPath(targetObject);
|
||||
|
||||
string defaultPath = targetObject.name;
|
||||
string assetPath = UIGenerateQuick.GetPrefabAssetPath(targetObject);
|
||||
if (string.IsNullOrEmpty(assetPath) || !assetPath.StartsWith("Assets/", StringComparison.Ordinal))
|
||||
return defaultPath;
|
||||
|
||||
assetPath = assetPath.Replace('\\', '/');
|
||||
|
||||
switch (scriptGenerateData.LoadType)
|
||||
return scriptGenerateData.LoadType switch
|
||||
{
|
||||
case EUIResLoadType.Resources:
|
||||
{
|
||||
string resourcesRoot = scriptGenerateData.UIPrefabRootPath; // 例如 "Assets/Resources/UI"
|
||||
string relPath = GetResourcesRelativePath(assetPath, resourcesRoot);
|
||||
|
||||
if (relPath == null)
|
||||
{
|
||||
Debug.LogWarning($"[UI生成] 资源 {assetPath} 不在配置的 Resources 根目录下: {resourcesRoot}");
|
||||
return defaultPath;
|
||||
}
|
||||
|
||||
return relPath;
|
||||
}
|
||||
|
||||
case EUIResLoadType.AssetBundle:
|
||||
{
|
||||
string bundleRoot = scriptGenerateData.UIPrefabRootPath; // 例如 "Assets/Bundles/UI"
|
||||
|
||||
try
|
||||
{
|
||||
var defaultPackage = YooAsset.Editor.AssetBundleCollectorSettingData.Setting.GetPackage("DefaultPackage");
|
||||
if (defaultPackage != null && defaultPackage.EnableAddressable)
|
||||
return defaultPath;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (!assetPath.StartsWith(bundleRoot, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Debug.LogWarning($"[UI生成] 资源 {assetPath} 不在配置的 AssetBundle 根目录下: {bundleRoot}");
|
||||
return defaultPath;
|
||||
}
|
||||
|
||||
return Path.ChangeExtension(assetPath, null);
|
||||
}
|
||||
|
||||
default:
|
||||
return defaultPath;
|
||||
}
|
||||
EUIResLoadType.Resources => GetResourcesPath(assetPath, scriptGenerateData, defaultPath),
|
||||
EUIResLoadType.AssetBundle => GetAssetBundlePath(assetPath, scriptGenerateData, defaultPath),
|
||||
_ => defaultPath
|
||||
};
|
||||
}
|
||||
|
||||
private string GetResourcesRelativePath(string assetPath, string resourcesRoot)
|
||||
private static string GetResourcesPath(string assetPath, UIScriptGenerateData scriptGenerateData, string defaultPath)
|
||||
{
|
||||
var resourcesRoot = scriptGenerateData.UIPrefabRootPath;
|
||||
var relPath = GetResourcesRelativePath(assetPath, resourcesRoot);
|
||||
|
||||
if (relPath == null)
|
||||
{
|
||||
Debug.LogWarning($"[UI生成] 资源 {assetPath} 不在配置的 Resources 根目录下: {resourcesRoot}");
|
||||
return defaultPath;
|
||||
}
|
||||
|
||||
return relPath;
|
||||
}
|
||||
|
||||
private static string GetAssetBundlePath(string assetPath, UIScriptGenerateData scriptGenerateData, string defaultPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var defaultPackage = YooAsset.Editor.AssetBundleCollectorSettingData.Setting.GetPackage("DefaultPackage");
|
||||
if (defaultPackage?.EnableAddressable == true)
|
||||
return defaultPath;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略异常,继续处理
|
||||
}
|
||||
|
||||
var bundleRoot = scriptGenerateData.UIPrefabRootPath;
|
||||
if (!assetPath.StartsWith(bundleRoot, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Debug.LogWarning($"[UI生成] 资源 {assetPath} 不在配置的 AssetBundle 根目录下: {bundleRoot}");
|
||||
return defaultPath;
|
||||
}
|
||||
|
||||
return Path.ChangeExtension(assetPath, null);
|
||||
}
|
||||
|
||||
private static string GetResourcesRelativePath(string assetPath, string resourcesRoot)
|
||||
{
|
||||
if (string.IsNullOrEmpty(assetPath) || string.IsNullOrEmpty(resourcesRoot)) return null;
|
||||
|
||||
assetPath = assetPath.Replace('\\', '/');
|
||||
resourcesRoot = resourcesRoot.Replace('\\', '/');
|
||||
|
||||
if (!assetPath.StartsWith(resourcesRoot, StringComparison.OrdinalIgnoreCase))
|
||||
return null;
|
||||
|
||||
string relPath = assetPath.Substring(resourcesRoot.Length).TrimStart('/');
|
||||
var relPath = assetPath.Substring(resourcesRoot.Length).TrimStart('/');
|
||||
return Path.ChangeExtension(relPath, null);
|
||||
}
|
||||
|
||||
@ -126,47 +131,104 @@ namespace AlicizaX.UI.Editor
|
||||
if (scriptContent == null) throw new ArgumentNullException(nameof(scriptContent));
|
||||
if (scriptGenerateData == null) throw new ArgumentNullException(nameof(scriptGenerateData));
|
||||
|
||||
string scriptFolderPath = scriptGenerateData.GenerateHolderCodePath;
|
||||
string scriptFilePath = Path.Combine(scriptFolderPath, className + ".cs");
|
||||
var scriptFolderPath = scriptGenerateData.GenerateHolderCodePath;
|
||||
var scriptFilePath = Path.Combine(scriptFolderPath, $"{className}.cs");
|
||||
|
||||
if (!Directory.Exists(scriptFolderPath))
|
||||
{
|
||||
Directory.CreateDirectory(scriptFolderPath);
|
||||
}
|
||||
Directory.CreateDirectory(scriptFolderPath);
|
||||
|
||||
if (File.Exists(scriptFilePath))
|
||||
if (File.Exists(scriptFilePath) && IsContentUnchanged(scriptFilePath, scriptContent))
|
||||
{
|
||||
string oldText = File.ReadAllText(scriptFilePath, Encoding.UTF8);
|
||||
if (oldText.Equals(scriptContent, StringComparison.Ordinal))
|
||||
{
|
||||
// 文件未变更:标记并等待脚本 reload 去做附加
|
||||
EditorPrefs.SetString("Generate", className);
|
||||
UIScriptGeneratorHelper.CheckHasAttach();
|
||||
return;
|
||||
}
|
||||
UIScriptGeneratorHelper.BindUIScript();
|
||||
return;
|
||||
}
|
||||
|
||||
File.WriteAllText(scriptFilePath, scriptContent, Encoding.UTF8);
|
||||
EditorPrefs.SetString("Generate", className);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
private static bool IsContentUnchanged(string filePath, string newContent)
|
||||
{
|
||||
var oldText = File.ReadAllText(filePath, Encoding.UTF8);
|
||||
return oldText.Equals(newContent, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public bool CheckCanGenerate(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
|
||||
{
|
||||
if (targetObject == null || scriptGenerateData == null) return false;
|
||||
|
||||
string assetPath = UIGenerateQuick.GetPrefabAssetPath(targetObject);
|
||||
var assetPath = UIGenerateQuick.GetPrefabAssetPath(targetObject);
|
||||
if (string.IsNullOrEmpty(assetPath) || !assetPath.StartsWith("Assets/", StringComparison.Ordinal))
|
||||
return false; // 不在 Assets 下
|
||||
return false;
|
||||
|
||||
assetPath = assetPath.Replace('\\', '/');
|
||||
bool result = assetPath.StartsWith(scriptGenerateData.UIPrefabRootPath, StringComparison.OrdinalIgnoreCase);
|
||||
if (!result)
|
||||
var isValidPath = assetPath.StartsWith(scriptGenerateData.UIPrefabRootPath, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (!isValidPath)
|
||||
{
|
||||
Debug.LogWarning($"UI存储位置与配置生成规则不符合 请检查对应配置的UIPrefabRootPath\n[AssetPath]{assetPath}\n[ConfigPath]{scriptGenerateData.UIPrefabRootPath}");
|
||||
}
|
||||
|
||||
return result;
|
||||
return isValidPath;
|
||||
}
|
||||
|
||||
public string GetReferenceNamespace(List<UIBindData> uiBindDatas)
|
||||
{
|
||||
var namespaceSet = new HashSet<string>(StringComparer.Ordinal) { "UnityEngine" };
|
||||
|
||||
if (uiBindDatas?.Any(d => d.BindType == EBindType.ListCom) == true)
|
||||
{
|
||||
namespaceSet.Add("System.Collections.Generic");
|
||||
}
|
||||
|
||||
uiBindDatas?
|
||||
.Where(bindData => bindData?.BindCom?.FirstOrDefault() != null)
|
||||
.Select(bindData => bindData.BindCom[0].GetType().Namespace)
|
||||
.Where(ns => !string.IsNullOrEmpty(ns))
|
||||
.ToList()
|
||||
.ForEach(ns => namespaceSet.Add(ns));
|
||||
|
||||
return string.Join(Environment.NewLine, namespaceSet.Select(ns => $"using {ns};"));
|
||||
}
|
||||
|
||||
public string GetVariableContent(List<UIBindData> uiBindDatas)
|
||||
{
|
||||
if (uiBindDatas == null || uiBindDatas.Count == 0) return string.Empty;
|
||||
|
||||
var variableBuilder = new StringBuilder();
|
||||
var variables = uiBindDatas
|
||||
.Where(b => b != null && !string.IsNullOrEmpty(b.Name))
|
||||
.Select(b => GenerateVariableDeclaration(b))
|
||||
.Where(declaration => !string.IsNullOrEmpty(declaration));
|
||||
|
||||
return string.Join("\n\n", variables);
|
||||
}
|
||||
|
||||
private string GenerateVariableDeclaration(UIBindData bindData)
|
||||
{
|
||||
var variableName = bindData.Name;
|
||||
var publicName = GetPublicComponentByNameRule(variableName);
|
||||
var firstType = bindData.BindCom?.FirstOrDefault()?.GetType();
|
||||
var typeName = firstType?.Name ?? "Component";
|
||||
|
||||
var declaration = new StringBuilder();
|
||||
declaration.AppendLine("\t\t[SerializeField]");
|
||||
|
||||
switch (bindData.BindType)
|
||||
{
|
||||
case EBindType.None:
|
||||
case EBindType.Widget:
|
||||
declaration.AppendLine($"\t\tprivate {typeName} {variableName};");
|
||||
declaration.Append($"\t\tpublic {typeName} {publicName} => {variableName};");
|
||||
break;
|
||||
|
||||
case EBindType.ListCom:
|
||||
var count = Math.Max(0, bindData.BindCom?.Count ?? 0);
|
||||
declaration.AppendLine($"\t\tprivate {typeName}[] {variableName} = new {typeName}[{count}];");
|
||||
declaration.Append($"\t\tpublic {typeName}[] {publicName} => {variableName};");
|
||||
break;
|
||||
}
|
||||
|
||||
return declaration.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
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 UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Callbacks;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AlicizaX.UI.Editor
|
||||
{
|
||||
@ -18,16 +15,15 @@ namespace AlicizaX.UI.Editor
|
||||
{
|
||||
None,
|
||||
Widget,
|
||||
ListCom,
|
||||
ListCom
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
internal class UIBindData
|
||||
public class UIBindData
|
||||
{
|
||||
public string Name;
|
||||
public List<Component> BindCom;
|
||||
public EBindType BindType;
|
||||
public string Name { get; }
|
||||
public List<Component> BindCom { get; }
|
||||
public EBindType BindType { get; }
|
||||
|
||||
public UIBindData(string name, List<Component> bindCom, EBindType bindType = EBindType.None)
|
||||
{
|
||||
@ -46,68 +42,48 @@ namespace AlicizaX.UI.Editor
|
||||
{
|
||||
private static UIGenerateConfiguration _uiGenerateConfiguration;
|
||||
private static IUIGeneratorRuleHelper _uiGeneratorRuleHelper;
|
||||
private static readonly List<UIBindData> _uiBindDatas = new List<UIBindData>();
|
||||
private static readonly HashSet<string> _arrayComponents = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
private static List<UIBindData> _uiBindDatas = new List<UIBindData>();
|
||||
private static HashSet<string> _arrayComponents = new HashSet<string>(StringComparer.Ordinal);
|
||||
private static IUIGeneratorRuleHelper UIGeneratorRuleHelper =>
|
||||
_uiGeneratorRuleHelper ?? InitializeRuleHelper();
|
||||
|
||||
public static IUIGeneratorRuleHelper UIGeneratorRuleHelper
|
||||
private static UIGenerateConfiguration UIConfiguration =>
|
||||
_uiGenerateConfiguration ??= UIGenerateConfiguration.Instance;
|
||||
|
||||
private static IUIGeneratorRuleHelper InitializeRuleHelper()
|
||||
{
|
||||
get
|
||||
var ruleHelperTypeName = UIConfiguration.UIScriptGeneratorRuleHelper;
|
||||
if (string.IsNullOrWhiteSpace(ruleHelperTypeName))
|
||||
{
|
||||
if (_uiGeneratorRuleHelper == null ||
|
||||
(_uiGeneratorRuleHelper != null && !UIConfiguration.UIScriptGeneratorRuleHelper.Equals(_uiGeneratorRuleHelper.GetType().FullName, StringComparison.Ordinal)))
|
||||
{
|
||||
var ruleHelperTypeName = UIConfiguration.UIScriptGeneratorRuleHelper;
|
||||
if (string.IsNullOrWhiteSpace(ruleHelperTypeName))
|
||||
{
|
||||
Debug.LogError("UIScriptGeneratorHelper: UIScriptGeneratorRuleHelper not configured.");
|
||||
return null;
|
||||
}
|
||||
|
||||
Type ruleHelperType = Type.GetType(ruleHelperTypeName);
|
||||
if (ruleHelperType == null)
|
||||
{
|
||||
Debug.LogError($"UIScriptGeneratorHelper: Could not load UI ScriptGeneratorHelper {ruleHelperTypeName}");
|
||||
return null;
|
||||
}
|
||||
|
||||
_uiGeneratorRuleHelper = Activator.CreateInstance(ruleHelperType) as IUIGeneratorRuleHelper;
|
||||
if (_uiGeneratorRuleHelper == null)
|
||||
{
|
||||
Debug.LogError($"UIScriptGeneratorHelper: Failed to instantiate {ruleHelperTypeName} as IUIGeneratorRuleHelper.");
|
||||
}
|
||||
}
|
||||
|
||||
return _uiGeneratorRuleHelper;
|
||||
Debug.LogError("UIScriptGeneratorHelper: UIScriptGeneratorRuleHelper not configured.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static UIGenerateConfiguration UIConfiguration
|
||||
{
|
||||
get
|
||||
var ruleHelperType = Type.GetType(ruleHelperTypeName);
|
||||
if (ruleHelperType == null)
|
||||
{
|
||||
if (_uiGenerateConfiguration == null)
|
||||
{
|
||||
_uiGenerateConfiguration = UIGenerateConfiguration.Instance;
|
||||
}
|
||||
|
||||
return _uiGenerateConfiguration;
|
||||
Debug.LogError($"UIScriptGeneratorHelper: Could not load UI ScriptGeneratorHelper {ruleHelperTypeName}");
|
||||
return null;
|
||||
}
|
||||
|
||||
_uiGeneratorRuleHelper = Activator.CreateInstance(ruleHelperType) as IUIGeneratorRuleHelper;
|
||||
if (_uiGeneratorRuleHelper == null)
|
||||
{
|
||||
Debug.LogError($"UIScriptGeneratorHelper: Failed to instantiate {ruleHelperTypeName} as IUIGeneratorRuleHelper.");
|
||||
}
|
||||
|
||||
return _uiGeneratorRuleHelper;
|
||||
}
|
||||
|
||||
private static string GetUIElementComponentType(string uiName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(uiName)) return string.Empty;
|
||||
foreach (var pair in UIConfiguration.UIElementRegexConfigs ?? Enumerable.Empty<UIEelementRegexData>())
|
||||
{
|
||||
if (string.IsNullOrEmpty(pair?.uiElementRegex)) continue;
|
||||
if (uiName.StartsWith(pair.uiElementRegex, StringComparison.Ordinal))
|
||||
{
|
||||
return pair.componentType ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
return UIConfiguration.UIElementRegexConfigs
|
||||
?.Where(pair => !string.IsNullOrEmpty(pair?.uiElementRegex))
|
||||
.FirstOrDefault(pair => uiName.StartsWith(pair.uiElementRegex, StringComparison.Ordinal))
|
||||
?.componentType ?? string.Empty;
|
||||
}
|
||||
|
||||
private static string[] SplitComponentName(string name)
|
||||
@ -118,43 +94,24 @@ namespace AlicizaX.UI.Editor
|
||||
if (string.IsNullOrEmpty(common?.ComCheckEndName) || !name.Contains(common.ComCheckEndName))
|
||||
return null;
|
||||
|
||||
int endIndex = name.IndexOf(common.ComCheckEndName, StringComparison.Ordinal);
|
||||
var endIndex = name.IndexOf(common.ComCheckEndName, StringComparison.Ordinal);
|
||||
if (endIndex <= 0) return null;
|
||||
|
||||
string comStr = name.Substring(0, endIndex);
|
||||
string split = common.ComCheckSplitName ?? "#";
|
||||
// 使用 string[] 重载并移除空项,防止错误 overload
|
||||
var comStr = name.Substring(0, endIndex);
|
||||
var split = common.ComCheckSplitName ?? "#";
|
||||
|
||||
return comStr.Split(new[] { split }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
private static string GetVariableName(string key, string componentName, EBindType bindType)
|
||||
{
|
||||
var helper = UIGeneratorRuleHelper;
|
||||
if (helper == null)
|
||||
throw new InvalidOperationException("UIGeneratorRuleHelper is not configured.");
|
||||
return helper.GetPrivateComponentByNameRule(key, componentName, bindType);
|
||||
}
|
||||
|
||||
private static void GetBindData(Transform root)
|
||||
private static void CollectBindData(Transform root)
|
||||
{
|
||||
if (root == null) return;
|
||||
|
||||
for (int i = 0; i < root.childCount; ++i)
|
||||
foreach (Transform child in root.Cast<Transform>().Where(child => child != null))
|
||||
{
|
||||
Transform child = root.GetChild(i);
|
||||
if (child == null) continue;
|
||||
|
||||
// 排除关键字
|
||||
if (UIConfiguration.UIGenerateCommonData.ExcludeKeywords != null &&
|
||||
UIConfiguration.UIGenerateCommonData.ExcludeKeywords.Any(k =>
|
||||
!string.IsNullOrEmpty(k) && child.name.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool hasWidget = child.GetComponent<UIHolderObjectBase>() != null;
|
||||
bool isArrayComponent = !string.IsNullOrEmpty(UIConfiguration.UIGenerateCommonData.ArrayComSplitName) &&
|
||||
child.name.StartsWith(UIConfiguration.UIGenerateCommonData.ArrayComSplitName, StringComparison.Ordinal);
|
||||
if (ShouldSkipChild(child)) continue;
|
||||
var hasWidget = child.GetComponent<UIHolderObjectBase>() != null;
|
||||
var isArrayComponent = IsArrayComponent(child.name);
|
||||
|
||||
if (hasWidget)
|
||||
{
|
||||
@ -162,69 +119,78 @@ namespace AlicizaX.UI.Editor
|
||||
}
|
||||
else if (isArrayComponent)
|
||||
{
|
||||
// 提取 array 标识文本(例如 "*Item*0" -> "Item")
|
||||
string splitCode = UIConfiguration.UIGenerateCommonData.ArrayComSplitName;
|
||||
int firstIndex = child.name.IndexOf(splitCode, StringComparison.Ordinal);
|
||||
int lastIndex = child.name.LastIndexOf(splitCode, StringComparison.Ordinal);
|
||||
if (firstIndex < 0 || lastIndex <= firstIndex) continue;
|
||||
|
||||
// 中间文本
|
||||
string text = child.name.Substring(firstIndex + splitCode.Length, lastIndex - (firstIndex + splitCode.Length));
|
||||
if (string.IsNullOrEmpty(text)) continue;
|
||||
|
||||
if (_arrayComponents.Contains(text)) continue;
|
||||
_arrayComponents.Add(text);
|
||||
|
||||
// 在同一个父节点下收集包含该 text 的同级节点
|
||||
List<Transform> arrayComponents = new List<Transform>();
|
||||
for (int j = 0; j < root.childCount; j++)
|
||||
{
|
||||
Transform sibling = root.GetChild(j);
|
||||
if (sibling != null && sibling.name.Contains(text, StringComparison.Ordinal))
|
||||
{
|
||||
arrayComponents.Add(sibling);
|
||||
}
|
||||
}
|
||||
|
||||
CollectArrayComponent(arrayComponents, text);
|
||||
ProcessArrayComponent(child, root);
|
||||
}
|
||||
else // 普通组件/进一步递归
|
||||
else
|
||||
{
|
||||
CollectComponent(child);
|
||||
GetBindData(child);
|
||||
CollectBindData(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldSkipChild(Transform child)
|
||||
{
|
||||
var keywords = UIConfiguration.UIGenerateCommonData.ExcludeKeywords;
|
||||
return keywords?.Any(k =>
|
||||
!string.IsNullOrEmpty(k) &&
|
||||
child.name.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0) == true;
|
||||
}
|
||||
|
||||
private static bool IsArrayComponent(string componentName)
|
||||
{
|
||||
var splitName = UIConfiguration.UIGenerateCommonData.ArrayComSplitName;
|
||||
return !string.IsNullOrEmpty(splitName) &&
|
||||
componentName.StartsWith(splitName, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static void ProcessArrayComponent(Transform child, Transform root)
|
||||
{
|
||||
var splitCode = UIConfiguration.UIGenerateCommonData.ArrayComSplitName;
|
||||
var firstIndex = child.name.IndexOf(splitCode, StringComparison.Ordinal);
|
||||
var lastIndex = child.name.LastIndexOf(splitCode, StringComparison.Ordinal);
|
||||
|
||||
if (firstIndex < 0 || lastIndex <= firstIndex) return;
|
||||
|
||||
var text = child.name.Substring(firstIndex + splitCode.Length, lastIndex - (firstIndex + splitCode.Length));
|
||||
if (string.IsNullOrEmpty(text) || _arrayComponents.Contains(text)) return;
|
||||
|
||||
_arrayComponents.Add(text);
|
||||
|
||||
var arrayComponents = root.Cast<Transform>()
|
||||
.Where(sibling => sibling.name.Contains(text, StringComparison.Ordinal))
|
||||
.ToList();
|
||||
|
||||
CollectArrayComponent(arrayComponents, text);
|
||||
}
|
||||
|
||||
private static void CollectComponent(Transform node)
|
||||
{
|
||||
if (node == null) return;
|
||||
|
||||
string[] componentArray = SplitComponentName(node.name);
|
||||
var componentArray = SplitComponentName(node.name);
|
||||
if (componentArray == null || componentArray.Length == 0) return;
|
||||
|
||||
foreach (var com in componentArray)
|
||||
foreach (var com in componentArray.Where(com => !string.IsNullOrEmpty(com)))
|
||||
{
|
||||
if (string.IsNullOrEmpty(com)) continue;
|
||||
string typeName = GetUIElementComponentType(com);
|
||||
var typeName = GetUIElementComponentType(com);
|
||||
if (string.IsNullOrEmpty(typeName)) continue;
|
||||
|
||||
Component component = node.GetComponent(typeName);
|
||||
if (component != null)
|
||||
{
|
||||
string keyName = GetVariableName(com, node.name, EBindType.None);
|
||||
if (_uiBindDatas.Exists(a => a.Name == keyName))
|
||||
{
|
||||
Debug.LogError($"Duplicate key found: {keyName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
_uiBindDatas.Add(new UIBindData(keyName, component));
|
||||
}
|
||||
else
|
||||
var component = node.GetComponent(typeName);
|
||||
if (component == null)
|
||||
{
|
||||
Debug.LogError($"{node.name} does not have component of type {typeName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var keyName = UIGeneratorRuleHelper.GetPrivateComponentByNameRule(com, node.name, EBindType.None);
|
||||
if (_uiBindDatas.Exists(data => data.Name == keyName))
|
||||
{
|
||||
Debug.LogError($"Duplicate key found: {keyName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
_uiBindDatas.Add(new UIBindData(keyName, component));
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,22 +199,22 @@ namespace AlicizaX.UI.Editor
|
||||
if (node == null) return;
|
||||
|
||||
var common = UIConfiguration.UIGenerateCommonData;
|
||||
if (node.name.IndexOf(common.ComCheckEndName, StringComparison.Ordinal) != -1 &&
|
||||
node.name.IndexOf(common.ComCheckSplitName, StringComparison.Ordinal) != -1)
|
||||
if (node.name.Contains(common.ComCheckEndName, StringComparison.Ordinal) &&
|
||||
node.name.Contains(common.ComCheckSplitName, StringComparison.Ordinal))
|
||||
{
|
||||
Debug.LogWarning($"{node.name} child component cannot contain rule definition symbols!");
|
||||
return;
|
||||
}
|
||||
|
||||
UIHolderObjectBase component = node.GetComponent<UIHolderObjectBase>();
|
||||
var component = node.GetComponent<UIHolderObjectBase>();
|
||||
if (component == null)
|
||||
{
|
||||
Debug.LogError($"{node.name} expected to be a widget but does not have UIHolderObjectBase.");
|
||||
return;
|
||||
}
|
||||
|
||||
string keyName = GetVariableName(string.Empty, node.name, EBindType.Widget);
|
||||
if (_uiBindDatas.Exists(a => a.Name == keyName))
|
||||
var keyName = UIGeneratorRuleHelper.GetPrivateComponentByNameRule(string.Empty, node.name, EBindType.Widget);
|
||||
if (_uiBindDatas.Exists(data => data.Name == keyName))
|
||||
{
|
||||
Debug.LogError($"Duplicate key found: {keyName}");
|
||||
return;
|
||||
@ -259,45 +225,54 @@ namespace AlicizaX.UI.Editor
|
||||
|
||||
private static void CollectArrayComponent(List<Transform> arrayNode, string nodeName)
|
||||
{
|
||||
if (arrayNode == null || arrayNode.Count == 0) return;
|
||||
|
||||
// 从 nodeName(例如 "*Item*0")取出组件描述部分(即名字中 @End 之前那部分)
|
||||
string[] componentArray = SplitComponentName(nodeName);
|
||||
|
||||
// 对 arrayNode 做基于后缀的安全排序:提取 last segment 作为索引(int.TryParse)
|
||||
string splitCode = UIConfiguration.UIGenerateCommonData.ArrayComSplitName;
|
||||
var orderedNodes = arrayNode
|
||||
.Select(n => new { Node = n, RawIndex = ExtractArrayIndex(n.name, splitCode) })
|
||||
.OrderBy(x => x.RawIndex.HasValue ? x.RawIndex.Value : int.MaxValue)
|
||||
.Select(x => x.Node)
|
||||
.ToList();
|
||||
if (arrayNode == null || !arrayNode.Any()) return;
|
||||
|
||||
var componentArray = SplitComponentName(nodeName);
|
||||
if (componentArray == null || componentArray.Length == 0)
|
||||
{
|
||||
Debug.LogWarning($"CollectArrayComponent: {nodeName} has no component definitions.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 准备临时 bind 列表,每个 componentArray 项对应一个 UIBindData
|
||||
List<UIBindData> tempBindDatas = new List<UIBindData>(componentArray.Length);
|
||||
for (int i = 0; i < componentArray.Length; i++)
|
||||
{
|
||||
string keyNamePreview = GetVariableName(componentArray[i], nodeName, EBindType.ListCom);
|
||||
tempBindDatas.Add(new UIBindData(keyNamePreview, new List<Component>(), EBindType.ListCom));
|
||||
}
|
||||
var orderedNodes = OrderArrayNodes(arrayNode);
|
||||
var tempBindDatas = CreateTempBindDatas(componentArray, nodeName);
|
||||
|
||||
// 遍历元素并填充
|
||||
for (int index = 0; index < componentArray.Length; index++)
|
||||
PopulateArrayComponents(componentArray, orderedNodes, tempBindDatas);
|
||||
_uiBindDatas.AddRange(tempBindDatas);
|
||||
}
|
||||
|
||||
private static List<Transform> OrderArrayNodes(List<Transform> arrayNode)
|
||||
{
|
||||
var splitCode = UIConfiguration.UIGenerateCommonData.ArrayComSplitName;
|
||||
return arrayNode
|
||||
.Select(node => new { Node = node, Index = ExtractArrayIndex(node.name, splitCode) })
|
||||
.OrderBy(x => x.Index ?? int.MaxValue)
|
||||
.Select(x => x.Node)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static List<UIBindData> CreateTempBindDatas(string[] componentArray, string nodeName)
|
||||
{
|
||||
return componentArray.Select((com, index) =>
|
||||
{
|
||||
string com = componentArray[index];
|
||||
var keyName = UIGeneratorRuleHelper.GetPrivateComponentByNameRule(com, nodeName, EBindType.ListCom);
|
||||
return new UIBindData(keyName, new List<Component>(), EBindType.ListCom);
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private static void PopulateArrayComponents(string[] componentArray, List<Transform> orderedNodes, List<UIBindData> tempBindDatas)
|
||||
{
|
||||
for (var index = 0; index < componentArray.Length; index++)
|
||||
{
|
||||
var com = componentArray[index];
|
||||
if (string.IsNullOrEmpty(com)) continue;
|
||||
|
||||
string typeName = GetUIElementComponentType(com);
|
||||
var typeName = GetUIElementComponentType(com);
|
||||
if (string.IsNullOrEmpty(typeName)) continue;
|
||||
|
||||
foreach (var node in orderedNodes)
|
||||
{
|
||||
Component component = node.GetComponent(typeName);
|
||||
var component = node.GetComponent(typeName);
|
||||
if (component != null)
|
||||
{
|
||||
tempBindDatas[index].BindCom.Add(component);
|
||||
@ -308,82 +283,20 @@ namespace AlicizaX.UI.Editor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将结果合并到全局绑定数据
|
||||
_uiBindDatas.AddRange(tempBindDatas);
|
||||
}
|
||||
|
||||
private static int? ExtractArrayIndex(string nodeName, string splitCode)
|
||||
{
|
||||
if (string.IsNullOrEmpty(nodeName) || string.IsNullOrEmpty(splitCode)) return null;
|
||||
int last = nodeName.LastIndexOf(splitCode, StringComparison.Ordinal);
|
||||
if (last < 0) return null;
|
||||
string suffix = nodeName.Substring(last + splitCode.Length);
|
||||
if (int.TryParse(suffix, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out int idx))
|
||||
return idx;
|
||||
return null;
|
||||
|
||||
var lastIndex = nodeName.LastIndexOf(splitCode, StringComparison.Ordinal);
|
||||
if (lastIndex < 0) return null;
|
||||
|
||||
var suffix = nodeName.Substring(lastIndex + splitCode.Length);
|
||||
return int.TryParse(suffix, out var idx) ? idx : (int?)null;
|
||||
}
|
||||
|
||||
private static string GetReferenceNamespace()
|
||||
{
|
||||
HashSet<string> namespaceSet = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
namespaceSet.Add("UnityEngine");
|
||||
if (_uiBindDatas.Any(d => d.BindType == EBindType.ListCom))
|
||||
namespaceSet.Add("System.Collections.Generic");
|
||||
|
||||
foreach (var bindData in _uiBindDatas)
|
||||
{
|
||||
var ns = bindData.BindCom?.FirstOrDefault()?.GetType().Namespace;
|
||||
if (!string.IsNullOrEmpty(ns) && !namespaceSet.Contains(ns))
|
||||
{
|
||||
namespaceSet.Add(ns);
|
||||
}
|
||||
}
|
||||
|
||||
return new StringBuilder().Append(string.Join(Environment.NewLine, namespaceSet.Select(n => $"using {n};"))).ToString();
|
||||
}
|
||||
|
||||
private static string GetVariableText(List<UIBindData> uiBindDatas)
|
||||
{
|
||||
if (uiBindDatas == null || uiBindDatas.Count == 0) return string.Empty;
|
||||
|
||||
StringBuilder variableTextBuilder = new StringBuilder();
|
||||
var helper = UIGeneratorRuleHelper;
|
||||
if (helper == null) throw new InvalidOperationException("UIGeneratorRuleHelper is not configured.");
|
||||
|
||||
var result = string.Join("\n\n", uiBindDatas
|
||||
.Where(b => b != null && !string.IsNullOrEmpty(b.Name))
|
||||
.Select(b =>
|
||||
{
|
||||
string variableName = b.Name;
|
||||
string publicName = helper.GetPublicComponentByNameRule(variableName);
|
||||
var firstType = b.BindCom?.FirstOrDefault()?.GetType();
|
||||
string typeName = firstType?.Name ?? "Component";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("\t\t[SerializeField]");
|
||||
|
||||
if (b.BindType == EBindType.None || b.BindType == EBindType.Widget)
|
||||
{
|
||||
sb.AppendLine($"\t\tprivate {typeName} {variableName};");
|
||||
sb.Append($"\t\tpublic {typeName} {publicName} => {variableName};");
|
||||
}
|
||||
else if (b.BindType == EBindType.ListCom)
|
||||
{
|
||||
int count = Math.Max(0, b.BindCom?.Count ?? 0);
|
||||
sb.AppendLine($"\t\tprivate {typeName}[] {variableName} = new {typeName}[{count}];");
|
||||
sb.Append($"\t\tpublic {typeName}[] {publicName} => {variableName};");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}));
|
||||
|
||||
return variableTextBuilder.Append(result).ToString();
|
||||
}
|
||||
|
||||
|
||||
public static void GenerateAndAttachScript(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
|
||||
public static void GenerateUIBindScript(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
|
||||
{
|
||||
if (targetObject == null) throw new ArgumentNullException(nameof(targetObject));
|
||||
if (scriptGenerateData == null) throw new ArgumentNullException(nameof(scriptGenerateData));
|
||||
@ -395,61 +308,59 @@ namespace AlicizaX.UI.Editor
|
||||
}
|
||||
|
||||
var ruleHelper = UIGeneratorRuleHelper;
|
||||
if (ruleHelper == null)
|
||||
{
|
||||
Debug.LogError("UIGeneratorRuleHelper not available, abort.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ruleHelper.CheckCanGenerate(targetObject, scriptGenerateData))
|
||||
if (ruleHelper == null || !ruleHelper.CheckCanGenerate(targetObject, scriptGenerateData))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EditorPrefs.SetInt("InstanceId", targetObject.GetInstanceID());
|
||||
_uiBindDatas.Clear();
|
||||
_arrayComponents.Clear();
|
||||
InitializeGenerationContext(targetObject);
|
||||
|
||||
string className = ruleHelper.GetClassGenerateName(targetObject, scriptGenerateData);
|
||||
var className = ruleHelper.GetClassGenerateName(targetObject, scriptGenerateData);
|
||||
if (string.IsNullOrEmpty(className))
|
||||
{
|
||||
Debug.LogError("Generated className is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
GetBindData(targetObject.transform);
|
||||
CollectBindData(targetObject.transform);
|
||||
GenerateScript(targetObject, className, scriptGenerateData, ruleHelper);
|
||||
}
|
||||
|
||||
string uiTemplateText = File.ReadAllText(UIGlobalPath.TemplatePath);
|
||||
uiTemplateText = uiTemplateText.Replace("#ReferenceNameSpace#", GetReferenceNamespace());
|
||||
uiTemplateText = uiTemplateText.Replace("#ClassNameSpace#", scriptGenerateData.NameSpace);
|
||||
uiTemplateText = uiTemplateText.Replace("#ClassName#", className);
|
||||
uiTemplateText = uiTemplateText.Replace("#TagName#", ruleHelper.GetUIResourceSavePath(targetObject, scriptGenerateData));
|
||||
uiTemplateText = uiTemplateText.Replace("#LoadType#", scriptGenerateData.LoadType.ToString());
|
||||
uiTemplateText = uiTemplateText.Replace("#Variable#", GetVariableText(_uiBindDatas));
|
||||
private static void InitializeGenerationContext(GameObject targetObject)
|
||||
{
|
||||
EditorPrefs.SetInt("InstanceId", targetObject.GetInstanceID());
|
||||
_uiBindDatas.Clear();
|
||||
_arrayComponents.Clear();
|
||||
}
|
||||
|
||||
ruleHelper.WriteUIScriptContent(className, uiTemplateText, scriptGenerateData);
|
||||
private static void GenerateScript(GameObject targetObject, string className, UIScriptGenerateData scriptGenerateData, IUIGeneratorRuleHelper ruleHelper)
|
||||
{
|
||||
var templateText = File.ReadAllText(UIGlobalPath.TemplatePath);
|
||||
var processedText = ProcessTemplateText(targetObject, templateText, className, scriptGenerateData, ruleHelper);
|
||||
|
||||
ruleHelper.WriteUIScriptContent(className, processedText, scriptGenerateData);
|
||||
EditorPrefs.SetString("Generate", className);
|
||||
}
|
||||
|
||||
private static string ProcessTemplateText(GameObject targetObject, string templateText, string className, UIScriptGenerateData scriptGenerateData, IUIGeneratorRuleHelper ruleHelper)
|
||||
{
|
||||
return templateText
|
||||
.Replace("#ReferenceNameSpace#", ruleHelper.GetReferenceNamespace(_uiBindDatas))
|
||||
.Replace("#ClassNameSpace#", scriptGenerateData.NameSpace)
|
||||
.Replace("#ClassName#", className)
|
||||
.Replace("#TagName#", ruleHelper.GetUIResourceSavePath(targetObject, scriptGenerateData))
|
||||
.Replace("#LoadType#", scriptGenerateData.LoadType.ToString())
|
||||
.Replace("#Variable#", ruleHelper.GetVariableContent(_uiBindDatas));
|
||||
}
|
||||
|
||||
[DidReloadScripts]
|
||||
public static void CheckHasAttach()
|
||||
public static void BindUIScript()
|
||||
{
|
||||
if (!EditorPrefs.HasKey("Generate"))
|
||||
return;
|
||||
EditorPrefs.DeleteKey("Generate");
|
||||
_uiBindDatas.Clear();
|
||||
_arrayComponents.Clear();
|
||||
if (!EditorPrefs.HasKey("Generate")) return;
|
||||
|
||||
string className = EditorPrefs.GetString("Generate");
|
||||
int instanceId = EditorPrefs.GetInt("InstanceId", -1);
|
||||
|
||||
if (instanceId == -1)
|
||||
{
|
||||
Debug.LogWarning("CheckHasAttach: InstanceId missing.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject targetObject = EditorUtility.InstanceIDToObject(instanceId) as GameObject;
|
||||
var className = EditorPrefs.GetString("Generate");
|
||||
var instanceId = EditorPrefs.GetInt("InstanceId", -1);
|
||||
var targetObject = EditorUtility.InstanceIDToObject(instanceId) as GameObject;
|
||||
|
||||
if (targetObject == null)
|
||||
{
|
||||
@ -457,12 +368,20 @@ namespace AlicizaX.UI.Editor
|
||||
return;
|
||||
}
|
||||
|
||||
_uiBindDatas.Clear();
|
||||
_arrayComponents.Clear();
|
||||
|
||||
GetBindData(targetObject.transform);
|
||||
|
||||
CollectBindData(targetObject.transform);
|
||||
BindScriptPropertyField(targetObject, className);
|
||||
CleanupContext();
|
||||
Debug.Log($"Generate {className} Successfully attached to game object");
|
||||
}
|
||||
|
||||
private static void CleanupContext()
|
||||
{
|
||||
EditorPrefs.DeleteKey("Generate");
|
||||
_uiBindDatas.Clear();
|
||||
_arrayComponents.Clear();
|
||||
}
|
||||
|
||||
private static void BindScriptPropertyField(GameObject targetObject, string scriptClassName)
|
||||
@ -470,98 +389,98 @@ namespace AlicizaX.UI.Editor
|
||||
if (targetObject == null) throw new ArgumentNullException(nameof(targetObject));
|
||||
if (string.IsNullOrEmpty(scriptClassName)) throw new ArgumentNullException(nameof(scriptClassName));
|
||||
|
||||
Type scriptType = null;
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
// 跳过典型的编辑器专用程序集(但只在程序集名字结束或包含 ".Editor" 时跳过)
|
||||
var asmName = assembly.GetName().Name ?? string.Empty;
|
||||
if (asmName.EndsWith(".Editor", StringComparison.OrdinalIgnoreCase) ||
|
||||
asmName.Equals("UnityEditor", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Type[] types= assembly.GetTypes();
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (!type.IsClass || type.IsAbstract) continue;
|
||||
if (type.Name.Equals(scriptClassName, StringComparison.Ordinal) ||
|
||||
type.Name.Contains(scriptClassName, StringComparison.Ordinal))
|
||||
{
|
||||
scriptType = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (scriptType != null) break;
|
||||
}
|
||||
|
||||
var scriptType = FindScriptType(scriptClassName);
|
||||
if (scriptType == null)
|
||||
{
|
||||
Debug.LogError($"Could not find the class: {scriptClassName}");
|
||||
return;
|
||||
}
|
||||
|
||||
Component component = targetObject.GetOrAddComponent(scriptType);
|
||||
var component = targetObject.GetOrAddComponent(scriptType);
|
||||
BindFieldsToComponents(component, scriptType);
|
||||
}
|
||||
|
||||
FieldInfo[] fields = scriptType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
foreach (FieldInfo field in fields)
|
||||
private static Type FindScriptType(string scriptClassName)
|
||||
{
|
||||
return AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(asm => !asm.GetName().Name.EndsWith(".Editor") &&
|
||||
!asm.GetName().Name.Equals("UnityEditor"))
|
||||
.SelectMany(asm => asm.GetTypes())
|
||||
.FirstOrDefault(type => type.IsClass && !type.IsAbstract &&
|
||||
type.Name.Equals(scriptClassName, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private static void BindFieldsToComponents(Component component, Type scriptType)
|
||||
{
|
||||
var fields = scriptType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
foreach (var field in fields.Where(field => !string.IsNullOrEmpty(field.Name)))
|
||||
{
|
||||
if (string.IsNullOrEmpty(field.Name)) continue;
|
||||
var componentInObjects = _uiBindDatas.Find(data => data.Name == field.Name)?.BindCom;
|
||||
if (componentInObjects == null)
|
||||
var components = _uiBindDatas.Find(data => data.Name == field.Name)?.BindCom;
|
||||
if (components == null)
|
||||
{
|
||||
Debug.LogError($"Field {field.Name} did not find matching component binding");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (field.FieldType.IsArray)
|
||||
SetFieldValue(field, components, component);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetFieldValue(FieldInfo field, IReadOnlyList<Component> components, Component targetComponent)
|
||||
{
|
||||
if (field.FieldType.IsArray)
|
||||
{
|
||||
SetArrayFieldValue(field, components, targetComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSingleFieldValue(field, components, targetComponent);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetArrayFieldValue(FieldInfo field, IReadOnlyList<Component> components, Component targetComponent)
|
||||
{
|
||||
var elementType = field.FieldType.GetElementType();
|
||||
if (elementType == null)
|
||||
{
|
||||
Debug.LogError($"Field {field.Name} has unknown element type.");
|
||||
return;
|
||||
}
|
||||
|
||||
var array = Array.CreateInstance(elementType, components.Count);
|
||||
for (var i = 0; i < components.Count; i++)
|
||||
{
|
||||
if (components[i] == null) continue;
|
||||
|
||||
if (elementType.IsInstanceOfType(components[i]))
|
||||
{
|
||||
Type elementType = field.FieldType.GetElementType();
|
||||
if (elementType == null)
|
||||
{
|
||||
Debug.LogError($"Field {field.Name} has unknown element type.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Array array = Array.CreateInstance(elementType, componentInObjects.Count);
|
||||
for (int i = 0; i < componentInObjects.Count; i++)
|
||||
{
|
||||
Component comp = componentInObjects[i];
|
||||
if (comp == null) continue;
|
||||
|
||||
if (elementType.IsInstanceOfType(comp))
|
||||
{
|
||||
array.SetValue(comp, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Element {i} type mismatch for field {field.Name}, expected {elementType.Name}, actual {comp.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
field.SetValue(component, array);
|
||||
array.SetValue(components[i], i);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (componentInObjects.Count > 0)
|
||||
{
|
||||
var first = componentInObjects[0];
|
||||
if (first == null) continue;
|
||||
|
||||
if (field.FieldType.IsInstanceOfType(first))
|
||||
{
|
||||
field.SetValue(component, first);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Field {field.Name} type mismatch, cannot assign value. Field expects {field.FieldType.Name}, actual {first.GetType().Name}");
|
||||
}
|
||||
}
|
||||
Debug.LogError($"Element {i} type mismatch for field {field.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
field.SetValue(targetComponent, array);
|
||||
}
|
||||
|
||||
private static void SetSingleFieldValue(FieldInfo field, IReadOnlyList<Component> components, Component targetComponent)
|
||||
{
|
||||
if (components.Count == 0) return;
|
||||
|
||||
var firstComponent = components[0];
|
||||
if (firstComponent == null) return;
|
||||
|
||||
if (field.FieldType.IsInstanceOfType(firstComponent))
|
||||
{
|
||||
field.SetValue(targetComponent, firstComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Field {field.Name} type mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
public static class PrefabChecker
|
||||
@ -569,9 +488,7 @@ namespace AlicizaX.UI.Editor
|
||||
public static bool IsEditingPrefabAsset(GameObject go)
|
||||
{
|
||||
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
if (prefabStage == null)
|
||||
return false;
|
||||
return prefabStage.IsPartOfPrefabContents(go);
|
||||
return prefabStage?.IsPartOfPrefabContents(go) == true;
|
||||
}
|
||||
|
||||
public static bool IsPrefabAsset(GameObject go)
|
||||
@ -579,12 +496,11 @@ namespace AlicizaX.UI.Editor
|
||||
if (go == null) return false;
|
||||
|
||||
var assetType = PrefabUtility.GetPrefabAssetType(go);
|
||||
if (assetType == PrefabAssetType.Regular ||
|
||||
assetType == PrefabAssetType.Variant ||
|
||||
assetType == PrefabAssetType.Model)
|
||||
return true;
|
||||
var isRegularPrefab = assetType == PrefabAssetType.Regular ||
|
||||
assetType == PrefabAssetType.Variant ||
|
||||
assetType == PrefabAssetType.Model;
|
||||
|
||||
return IsEditingPrefabAsset(go);
|
||||
return isRegularPrefab || IsEditingPrefabAsset(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AlicizaX.UI.Runtime
|
||||
@ -38,19 +40,67 @@ namespace AlicizaX.UI.Runtime
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGet(RuntimeTypeHandle handle, out UIMetaInfo info)
|
||||
{
|
||||
return _typeHandleMap.TryGetValue(handle, out info);
|
||||
if (_typeHandleMap.TryGetValue(handle, out info))
|
||||
return true;
|
||||
|
||||
var t = Type.GetTypeFromHandle(handle);
|
||||
|
||||
if (TryReflectAndRegister(t, out info))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGet(string type, out UIMetaInfo info)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGet(string typeName, out UIMetaInfo info)
|
||||
{
|
||||
RuntimeTypeHandle typeHandle;
|
||||
if (_stringHandleMap.TryGetValue(type, out typeHandle))
|
||||
if (_stringHandleMap.TryGetValue(typeName, out var handle))
|
||||
return TryGet(handle, out info);
|
||||
|
||||
|
||||
var type = AlicizaX.Utility.Assembly.GetType(typeName);
|
||||
|
||||
if (type != null && TryReflectAndRegister(type, out info))
|
||||
return true;
|
||||
|
||||
info = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static bool TryReflectAndRegister(Type uiType, out UIMetaInfo info)
|
||||
{
|
||||
Log.Warning($"[UI] UI未注册[{uiType.FullName}] 反射进行缓存");
|
||||
Type baseType = uiType;
|
||||
Type? holderType = baseType.GetGenericArguments()[0];
|
||||
|
||||
UILayer layer = UILayer.UI;
|
||||
bool fullScreen = false;
|
||||
int cacheTime = 0;
|
||||
|
||||
var cad = CustomAttributeData.GetCustomAttributes(uiType)
|
||||
.FirstOrDefault(a => a.AttributeType.Name == nameof(WindowAttribute));
|
||||
|
||||
if (cad != null)
|
||||
{
|
||||
var args = cad.ConstructorArguments;
|
||||
if (args.Count > 0) layer = (UILayer)(args[0].Value ?? UILayer.UI);
|
||||
if (args.Count > 1) fullScreen = (bool)(args[1].Value ?? false);
|
||||
if (args.Count > 2) cacheTime = (int)(args[2].Value ?? 0);
|
||||
}
|
||||
|
||||
return _typeHandleMap.TryGetValue(typeHandle, out info);
|
||||
if (holderType != null)
|
||||
{
|
||||
Register(uiType, holderType, layer, fullScreen, cacheTime);
|
||||
info = _typeHandleMap[uiType.TypeHandle];
|
||||
return true;
|
||||
}
|
||||
|
||||
info = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
@ -32,6 +34,37 @@ namespace AlicizaX.UI.Runtime
|
||||
}
|
||||
|
||||
public static bool TryGet(RuntimeTypeHandle handle, out UIResInfo info)
|
||||
=> _typeHandleMap.TryGetValue(handle, out info);
|
||||
{
|
||||
if (_typeHandleMap.TryGetValue(handle, out info))
|
||||
return true;
|
||||
|
||||
var t = Type.GetTypeFromHandle(handle);
|
||||
|
||||
if (TryReflectAndRegister(t, out info))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static bool TryReflectAndRegister(Type holderType, out UIResInfo info)
|
||||
{
|
||||
var cad = CustomAttributeData.GetCustomAttributes(holderType)
|
||||
.FirstOrDefault(a => a.AttributeType.Name == nameof(UIResAttribute));
|
||||
string resLocation = string.Empty;
|
||||
EUIResLoadType resLoadType = EUIResLoadType.AssetBundle;
|
||||
if (cad != null)
|
||||
{
|
||||
var args = cad.ConstructorArguments;
|
||||
if (args.Count > 0) resLocation = (string)(args[0].Value ?? string.Empty);
|
||||
if (args.Count > 1) resLoadType = (EUIResLoadType)(args[1].Value ?? EUIResLoadType.AssetBundle);
|
||||
Register(holderType, resLocation, resLoadType);
|
||||
info = _typeHandleMap[holderType.TypeHandle];
|
||||
return true;
|
||||
}
|
||||
|
||||
info = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user