using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using AlicizaX.UI.Runtime; using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.SceneManagement; using UnityEngine; namespace AlicizaX.UI.Editor { public enum EBindType { None, Widget, ListCom } [Serializable] public class UIBindData { public UIBindData(string name, List objs, Type componentType = null, EBindType bindType = EBindType.None) { Name = name; Objs = objs ?? new List(); BindType = bindType; ComponentType = componentType; ResolvedComponentTypes = new List(); if (componentType != null && Objs.Count > 0) { for (var i = 0; i < Objs.Count; i++) { ResolvedComponentTypes.Add(componentType); } } } public UIBindData(string name, GameObject obj, Type componentType = null, EBindType bindType = EBindType.None) : this(name, new List { obj }, componentType, bindType) { } public string Name { get; } public List Objs { get; set; } public EBindType BindType { get; } public Type ComponentType { get; private set; } public List ResolvedComponentTypes { get; } public bool IsGameObject => ComponentType == typeof(GameObject); public string TypeName => ComponentType?.FullName ?? string.Empty; public Type GetFirstOrDefaultType() => ComponentType; public void SetComponentType(Type componentType) { ComponentType = componentType; } public void AddResolvedObject(GameObject obj, Type componentType) { if (obj == null) { return; } Objs.Add(obj); ResolvedComponentTypes.Add(componentType ?? ComponentType); } public Type GetResolvedComponentType(int index) { if (index >= 0 && index < ResolvedComponentTypes.Count && ResolvedComponentTypes[index] != null) { return ResolvedComponentTypes[index]; } return ComponentType; } } public static class UIScriptGeneratorHelper { private const string GenerateTypeNameKey = "AlicizaX.UI.Generate.TypeName"; private const string GenerateInstanceIdKey = "AlicizaX.UI.Generate.InstanceId"; private const string GenerateAssetPathKey = "AlicizaX.UI.Generate.AssetPath"; private static UIGenerateConfiguration _uiGenerateConfiguration; private static IUIIdentifierFormatter _identifierFormatter; private static IUIResourcePathResolver _resourcePathResolver; private static IUIScriptCodeEmitter _scriptCodeEmitter; private static IUIScriptFileWriter _scriptFileWriter; private static readonly List _uiBindDatas = new List(); private static readonly HashSet _arrayComponents = new HashSet(StringComparer.Ordinal); private static readonly Dictionary _componentTypeCache = new Dictionary(StringComparer.Ordinal); private static UIGenerateConfiguration UIConfiguration => _uiGenerateConfiguration ??= UIGenerateConfiguration.Instance; private static IUIIdentifierFormatter IdentifierFormatter => ResolveConfiguredService( ref _identifierFormatter, UIConfiguration.UIIdentifierFormatterTypeName, typeof(DefaultUIIdentifierFormatter), nameof(IUIIdentifierFormatter)); private static IUIResourcePathResolver ResourcePathResolver => ResolveConfiguredService( ref _resourcePathResolver, UIConfiguration.UIResourcePathResolverTypeName, typeof(DefaultUIResourcePathResolver), nameof(IUIResourcePathResolver)); private static IUIScriptCodeEmitter ScriptCodeEmitter => ResolveConfiguredService( ref _scriptCodeEmitter, UIConfiguration.UIScriptCodeEmitterTypeName, typeof(DefaultUIScriptCodeEmitter), nameof(IUIScriptCodeEmitter)); private static IUIScriptFileWriter ScriptFileWriter => ResolveConfiguredService( ref _scriptFileWriter, UIConfiguration.UIScriptFileWriterTypeName, typeof(DefaultUIScriptFileWriter), nameof(IUIScriptFileWriter)); private static T ResolveConfiguredService(ref T cachedService, string configuredTypeName, Type defaultType, string serviceName) where T : class { var resolvedTypeName = string.IsNullOrWhiteSpace(configuredTypeName) ? defaultType.FullName : configuredTypeName; if (cachedService != null && cachedService.GetType().FullName == resolvedTypeName) { return cachedService; } var configuredType = AlicizaX.Utility.Assembly.GetType(resolvedTypeName); if (configuredType == null || !typeof(T).IsAssignableFrom(configuredType)) { if (!string.Equals(resolvedTypeName, defaultType.FullName, StringComparison.Ordinal)) { Debug.LogError( $"UIScriptGeneratorHelper: Could not load {serviceName} type '{resolvedTypeName}'. Falling back to {defaultType.FullName}."); } configuredType = defaultType; } cachedService = Activator.CreateInstance(configuredType, true) as T; if (cachedService != null) { return cachedService; } if (configuredType != defaultType) { Debug.LogError( $"UIScriptGeneratorHelper: Failed to instantiate {resolvedTypeName} as {serviceName}. Falling back to {defaultType.FullName}."); cachedService = Activator.CreateInstance(defaultType, true) as T; } if (cachedService == null) { Debug.LogError($"UIScriptGeneratorHelper: Failed to instantiate fallback {defaultType.FullName} as {serviceName}."); } return cachedService; } private static bool EnsureGenerationStrategyReady() { return IdentifierFormatter != null && ResourcePathResolver != null && ScriptCodeEmitter != null && ScriptFileWriter != null; } private static bool EnsureVariableGenerationStrategyReady() { return IdentifierFormatter != null && ScriptCodeEmitter != null; } private static Type ResolveUIElementComponentType(string uiName) { if (string.IsNullOrEmpty(uiName)) { return null; } var componentTypeName = UIConfiguration.UIElementRegexConfigs ?.Where(pair => !string.IsNullOrEmpty(pair?.uiElementRegex)) .FirstOrDefault(pair => uiName.StartsWith(pair.uiElementRegex, StringComparison.Ordinal)) ?.componentType; if (string.IsNullOrWhiteSpace(componentTypeName)) { return null; } if (_componentTypeCache.TryGetValue(componentTypeName, out var componentType)) { return componentType; } componentType = ResolveConfiguredComponentType(componentTypeName); if (componentType == null) { Debug.LogError($"UIScriptGeneratorHelper: Could not resolve component type '{componentTypeName}' for '{uiName}'."); return null; } _componentTypeCache[componentTypeName] = componentType; return componentType; } private static Type ResolveConfiguredComponentType(string componentTypeName) { if (string.IsNullOrWhiteSpace(componentTypeName)) { return null; } if (componentTypeName == nameof(GameObject)) { return typeof(GameObject); } Type componentType = AlicizaX.Utility.Assembly.GetType(componentTypeName); if (componentType != null && typeof(Component).IsAssignableFrom(componentType)) { return componentType; } return AlicizaX.Utility.Assembly.GetTypes() .Where(type => type != null && !type.IsAbstract && !type.IsInterface) .Where(type => typeof(Component).IsAssignableFrom(type)) .Where(type => string.Equals(type.FullName, componentTypeName, StringComparison.Ordinal) || string.Equals(type.Name, componentTypeName, StringComparison.Ordinal)) .OrderBy(type => string.Equals(type.FullName, componentTypeName, StringComparison.Ordinal) ? 0 : 1) .ThenBy(type => string.Equals(type.Namespace, "UnityEngine.UI", StringComparison.Ordinal) ? 0 : 1) .ThenBy(type => type.FullName, StringComparer.Ordinal) .FirstOrDefault(); } private static string[] SplitComponentName(string name) { if (string.IsNullOrEmpty(name)) return null; var common = UIConfiguration.UIGenerateCommonData; if (string.IsNullOrEmpty(common?.ComCheckEndName) || !name.Contains(common.ComCheckEndName)) return null; var endIndex = name.IndexOf(common.ComCheckEndName, StringComparison.Ordinal); if (endIndex <= 0) return null; var comStr = name.Substring(0, endIndex); var split = common.ComCheckSplitName ?? "#"; return comStr.Split(new[] { split }, StringSplitOptions.RemoveEmptyEntries); } private static bool TryResolveActualComponentType(GameObject source, Type componentType, out Type actualComponentType) { actualComponentType = null; if (source == null || componentType == null) { return false; } if (componentType == typeof(GameObject)) { actualComponentType = typeof(GameObject); return true; } var component = ResolveAssignableComponent(source, componentType); if (component == null) { return false; } actualComponentType = component.GetType(); return actualComponentType != null; } private static Type ResolveBindComponentType(Type configuredType, IEnumerable actualComponentTypes) { if (configuredType == typeof(GameObject)) { return typeof(GameObject); } var resolvedTypes = actualComponentTypes? .Where(type => type != null) .Distinct() .ToList(); if (resolvedTypes == null || resolvedTypes.Count == 0) { return configuredType; } if (resolvedTypes.Count == 1) { return resolvedTypes[0]; } return FindCommonComponentBaseType(resolvedTypes) ?? configuredType; } private static Type FindCommonComponentBaseType(IReadOnlyList componentTypes) { if (componentTypes == null || componentTypes.Count == 0) { return null; } var candidateType = componentTypes[0]; while (candidateType != null && candidateType != typeof(object)) { if (typeof(Component).IsAssignableFrom(candidateType) && componentTypes.All(type => candidateType.IsAssignableFrom(type))) { return candidateType; } candidateType = candidateType.BaseType; } return null; } private static void CollectBindData(Transform root) { if (root == null) return; foreach (Transform child in root.Cast().Where(child => child != null)) { if (ShouldSkipChild(child)) continue; var hasWidget = child.GetComponent() != null; var isArrayComponent = IsArrayComponent(child.name); if (hasWidget) { CollectWidget(child); } else if (isArrayComponent) { ProcessArrayComponent(child, root); } else { CollectComponent(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; if (!TryGetArrayComponentGroupName(child.name, splitCode, out var groupName)) { return; } var groupKey = $"{root.GetInstanceID()}::{groupName}"; if (!_arrayComponents.Add(groupKey)) { return; } var arrayComponents = root.Cast() .Where(sibling => IsArrayGroupMember(sibling.name, splitCode, groupName)) .ToList(); CollectArrayComponent(arrayComponents, groupName); } private static void CollectComponent(Transform node) { if (node == null) return; var componentArray = SplitComponentName(node.name); if (componentArray == null || componentArray.Length == 0) return; foreach (var com in componentArray.Where(com => !string.IsNullOrEmpty(com))) { var componentType = ResolveUIElementComponentType(com); if (componentType == null) { continue; } if (!TryResolveActualComponentType(node.gameObject, componentType, out var actualComponentType)) { Debug.LogError($"{node.name} does not have component of type {componentType.FullName}"); continue; } var keyName = GetPrivateComponentName(com, node.name, EBindType.None); if (_uiBindDatas.Exists(data => data.Name == keyName)) { Debug.LogError($"Duplicate key found: {keyName}"); continue; } _uiBindDatas.Add(new UIBindData(keyName, node.gameObject, actualComponentType)); } } private static void CollectWidget(Transform node) { if (node == null) return; var common = UIConfiguration.UIGenerateCommonData; 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; } var component = node.GetComponent(); if (component == null) { Debug.LogError($"{node.name} expected to be a widget but does not have UIHolderObjectBase."); return; } var keyName = GetPrivateComponentName(string.Empty, node.name, EBindType.Widget); if (_uiBindDatas.Exists(data => data.Name == keyName)) { Debug.LogError($"Duplicate key found: {keyName}"); return; } _uiBindDatas.Add(new UIBindData(keyName, component.gameObject, component.GetType(), EBindType.Widget)); } private static void CollectArrayComponent(List arrayNode, string nodeName) { 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; } var orderedNodes = OrderArrayNodes(arrayNode); var tempBindDatas = CreateTempBindDatas(componentArray, nodeName); PopulateArrayComponents(componentArray, orderedNodes, tempBindDatas); _uiBindDatas.AddRange(tempBindDatas); } private static List OrderArrayNodes(List 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 CreateTempBindDatas(string[] componentArray, string nodeName) { return componentArray.Select(com => { var keyName = GetPrivateComponentName(com, nodeName, EBindType.ListCom); return new UIBindData(keyName, new List(), null, EBindType.ListCom); }).ToList(); } private static void PopulateArrayComponents(string[] componentArray, List orderedNodes, List tempBindDatas) { for (var index = 0; index < componentArray.Length; index++) { var com = componentArray[index]; if (string.IsNullOrEmpty(com)) continue; var componentType = ResolveUIElementComponentType(com); if (componentType == null) continue; foreach (var node in orderedNodes) { if (TryResolveActualComponentType(node.gameObject, componentType, out var actualComponentType)) { tempBindDatas[index].AddResolvedObject(node.gameObject, actualComponentType); } else { Debug.LogError($"{node.name} does not have component of type {componentType.FullName}"); } } tempBindDatas[index].SetComponentType( ResolveBindComponentType(componentType, tempBindDatas[index].ResolvedComponentTypes)); } } private static bool TryGetArrayComponentGroupName(string nodeName, string splitCode, out string groupName) { groupName = null; if (string.IsNullOrEmpty(nodeName) || string.IsNullOrEmpty(splitCode)) { return false; } var firstIndex = nodeName.IndexOf(splitCode, StringComparison.Ordinal); var lastIndex = nodeName.LastIndexOf(splitCode, StringComparison.Ordinal); if (firstIndex < 0 || lastIndex <= firstIndex) { return false; } groupName = nodeName.Substring(firstIndex + splitCode.Length, lastIndex - (firstIndex + splitCode.Length)); return !string.IsNullOrEmpty(groupName); } private static bool IsArrayGroupMember(string nodeName, string splitCode, string groupName) { return TryGetArrayComponentGroupName(nodeName, splitCode, out var candidateGroupName) && candidateGroupName.Equals(groupName, StringComparison.Ordinal) && ExtractArrayIndex(nodeName, splitCode).HasValue; } private static int? ExtractArrayIndex(string nodeName, string splitCode) { if (string.IsNullOrEmpty(nodeName) || string.IsNullOrEmpty(splitCode)) 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; } public static void GenerateUIBindScript(GameObject targetObject, UIScriptGenerateData scriptGenerateData) { if (targetObject == null) throw new ArgumentNullException(nameof(targetObject)); if (scriptGenerateData == null) throw new ArgumentNullException(nameof(scriptGenerateData)); if (!PrefabChecker.IsPrefabAsset(targetObject)) { Debug.LogWarning("Please save the UI as a prefab asset before generating bindings."); return; } if (!EnsureGenerationStrategyReady()) { return; } InitializeGenerationContext(targetObject); CollectBindData(targetObject.transform); var generationContext = new UIGenerationContext(targetObject, scriptGenerateData, _uiBindDatas) { AssetPath = UIGenerateQuick.GetPrefabAssetPath(targetObject), ClassName = GetClassGenerateName(targetObject, scriptGenerateData) }; if (!CheckCanGenerate(generationContext)) { CleanupContext(); return; } var validationResult = ValidateGeneration(generationContext); if (!validationResult.IsValid) { Debug.LogError(validationResult.Message); CleanupContext(); return; } GenerateScript(generationContext); } public static void CopyVariableContentToClipboard(GameObject targetObject) { if (targetObject == null) throw new ArgumentNullException(nameof(targetObject)); if (!EnsureVariableGenerationStrategyReady()) { return; } string variableContent; try { ResetCollectedBindData(); CollectBindData(targetObject.transform); variableContent = ScriptCodeEmitter.GetVariableContent(_uiBindDatas, GetPublicComponentName); } finally { ResetCollectedBindData(); } EditorGUIUtility.systemCopyBuffer = variableContent ?? string.Empty; if (string.IsNullOrEmpty(variableContent)) { Debug.LogWarning($"UI生成绑定未找到可复制的属性内容,已清空剪贴板: {targetObject.name}"); return; } Debug.Log($"UI生成绑定属性已复制到剪贴板: {targetObject.name}"); } private static void InitializeGenerationContext(GameObject targetObject) { EditorPrefs.SetInt(GenerateInstanceIdKey, targetObject.GetInstanceID()); var assetPath = UIGenerateQuick.GetPrefabAssetPath(targetObject); if (!string.IsNullOrEmpty(assetPath)) { EditorPrefs.SetString(GenerateAssetPathKey, assetPath); } ResetCollectedBindData(); } private static UIGenerationValidationResult ValidateGeneration(UIGenerationContext context) { if (context.TargetObject == null) { return UIGenerationValidationResult.Fail("UI generation target is null."); } if (context.ScriptGenerateData == null) { return UIGenerationValidationResult.Fail("UI generation config is null."); } if (string.IsNullOrWhiteSpace(context.ClassName)) { return UIGenerationValidationResult.Fail("Generated class name is empty."); } if (!File.Exists(UIGlobalPath.TemplatePath)) { return UIGenerationValidationResult.Fail($"UI template file not found: {UIGlobalPath.TemplatePath}"); } return UIGenerationValidationResult.Success(); } private static void GenerateScript(UIGenerationContext context) { var templateText = File.ReadAllText(UIGlobalPath.TemplatePath); var processedText = ProcessTemplateText(context, templateText); EditorPrefs.SetString(GenerateTypeNameKey, context.FullTypeName); WriteScriptContent(context, processedText); } private static string ProcessTemplateText(UIGenerationContext context, string templateText) { return templateText .Replace("#ReferenceNameSpace#", GetReferenceNamespace(context)) .Replace("#ClassNameSpace#", context.ScriptGenerateData.NameSpace) .Replace("#ClassName#", context.ClassName) .Replace("#TagName#", GetResourceSavePath(context)) .Replace("#LoadType#", context.ScriptGenerateData.LoadType.ToString()) .Replace("#Variable#", GetVariableContent(context)); } private static string GetClassGenerateName(GameObject targetObject, UIScriptGenerateData scriptGenerateData) { return IdentifierFormatter.GetClassName(targetObject); } private static bool CheckCanGenerate(UIGenerationContext context) { return ResourcePathResolver.CanGenerate(context.TargetObject, context.ScriptGenerateData); } private static string GetPrivateComponentName(string regexName, string componentName, EBindType bindType) { return IdentifierFormatter.GetPrivateComponentName(regexName, componentName, bindType); } private static string GetPublicComponentName(string variableName) { return IdentifierFormatter.GetPublicComponentName(variableName); } private static string GetResourceSavePath(UIGenerationContext context) { return ResourcePathResolver.GetResourcePath(context.TargetObject, context.ScriptGenerateData); } private static string GetReferenceNamespace(UIGenerationContext context) { return ScriptCodeEmitter.GetReferenceNamespaces(_uiBindDatas); } private static string GetVariableContent(UIGenerationContext context) { return ScriptCodeEmitter.GetVariableContent(_uiBindDatas, GetPublicComponentName); } private static void WriteScriptContent(UIGenerationContext context, string scriptContent) { ScriptFileWriter.Write(context.TargetObject, context.ClassName, scriptContent, context.ScriptGenerateData); } [DidReloadScripts] public static void BindUIScript() { if (!EditorPrefs.HasKey(GenerateTypeNameKey)) return; var scriptTypeName = EditorPrefs.GetString(GenerateTypeNameKey); var targetObject = ResolveGenerationTarget(); if (targetObject == null) { Debug.LogWarning("UI script generation attachment object missing."); CleanupContext(); return; } if (!EnsureGenerationStrategyReady()) { CleanupContext(); return; } ResetCollectedBindData(); var bindSucceeded = false; CollectBindData(targetObject.transform); try { bindSucceeded = BindScriptPropertyField(targetObject, scriptTypeName); } catch (Exception exception) { Debug.LogException(exception); } finally { CleanupContext(); } if (!bindSucceeded) { return; } EditorUtility.SetDirty(targetObject); Debug.Log($"Generate {scriptTypeName} successfully attached to game object."); } private static GameObject ResolveGenerationTarget() { var instanceId = EditorPrefs.GetInt(GenerateInstanceIdKey, -1); var instanceTarget = EditorUtility.InstanceIDToObject(instanceId) as GameObject; if (instanceTarget != null) { return instanceTarget; } var assetPath = EditorPrefs.GetString(GenerateAssetPathKey, string.Empty); return string.IsNullOrEmpty(assetPath) ? null : AssetDatabase.LoadAssetAtPath(assetPath); } private static void CleanupContext() { EditorPrefs.DeleteKey(GenerateTypeNameKey); EditorPrefs.DeleteKey(GenerateInstanceIdKey); EditorPrefs.DeleteKey(GenerateAssetPathKey); ResetCollectedBindData(); } private static void ResetCollectedBindData() { _uiBindDatas.Clear(); _arrayComponents.Clear(); _componentTypeCache.Clear(); } private static bool BindScriptPropertyField(GameObject targetObject, string scriptTypeName) { if (targetObject == null) throw new ArgumentNullException(nameof(targetObject)); if (string.IsNullOrEmpty(scriptTypeName)) throw new ArgumentNullException(nameof(scriptTypeName)); var scriptType = FindScriptType(scriptTypeName); if (scriptType == null) { Debug.LogError($"Could not find the class: {scriptTypeName}"); return false; } var targetHolder = targetObject.GetOrAddComponent(scriptType); return BindFieldsToComponents(targetHolder, scriptType); } private static Type FindScriptType(string scriptTypeName) { var resolvedType = AlicizaX.Utility.Assembly.GetType(scriptTypeName); if (resolvedType != null) { return resolvedType; } return AppDomain.CurrentDomain.GetAssemblies() .Where(assembly => !assembly.GetName().Name.EndsWith(".Editor", StringComparison.Ordinal) && !assembly.GetName().Name.Equals("UnityEditor", StringComparison.Ordinal)) .SelectMany(assembly => assembly.GetTypes()) .FirstOrDefault(type => type.IsClass && !type.IsAbstract && type.FullName.Equals(scriptTypeName, StringComparison.Ordinal)); } private static bool BindFieldsToComponents(Component targetHolder, Type scriptType) { var fields = scriptType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Where(field => field.GetCustomAttributes(typeof(SerializeField), false).Length > 0 ); var isSuccessful = true; foreach (var field in fields.Where(field => !string.IsNullOrEmpty(field.Name))) { var bindData = _uiBindDatas.Find(data => data.Name == field.Name); if (bindData == null) { Debug.LogError($"Field {field.Name} did not find matching component binding."); isSuccessful = false; continue; } if (!SetFieldValue(field, bindData, targetHolder)) { isSuccessful = false; } } return isSuccessful; } private static bool SetFieldValue(FieldInfo field, UIBindData bindData, Component targetComponent) { if (field.FieldType.IsArray) { return SetArrayFieldValue(field, bindData, targetComponent); } return SetSingleFieldValue(field, bindData, targetComponent); } private static bool SetArrayFieldValue(FieldInfo field, UIBindData bindData, Component targetComponent) { var elementType = field.FieldType.GetElementType(); if (elementType == null) { Debug.LogError($"Field {field.Name} has unknown element type."); return false; } var components = bindData.Objs; var array = Array.CreateInstance(elementType, components.Count); var isSuccessful = true; for (var i = 0; i < components.Count; i++) { if (components[i] == null) continue; var componentObject = ResolveBoundObject(components[i], bindData.GetResolvedComponentType(i)); if (componentObject != null && elementType.IsInstanceOfType(componentObject)) { array.SetValue(componentObject, i); } else { Debug.LogError($"Element {i} type mismatch for field {field.Name}"); isSuccessful = false; } } field.SetValue(targetComponent, array); return isSuccessful; } private static bool SetSingleFieldValue(FieldInfo field, UIBindData bindData, Component targetComponent) { if (bindData.Objs.Count == 0) { return false; } var firstComponent = ResolveBoundObject(bindData.Objs[0], bindData.GetResolvedComponentType(0)); if (firstComponent == null) { return false; } if (!field.FieldType.IsInstanceOfType(firstComponent)) { Debug.LogError($"Field {field.Name} type mismatch"); return false; } field.SetValue(targetComponent, firstComponent); return true; } private static object ResolveBoundObject(GameObject source, Type componentType) { if (source == null || componentType == null) { return null; } return componentType == typeof(GameObject) ? source : ResolveAssignableComponent(source, componentType); } private static Component ResolveAssignableComponent(GameObject source, Type componentType) { if (source == null || componentType == null || !typeof(Component).IsAssignableFrom(componentType)) { return null; } Component component = source.GetComponent(componentType); if (component != null) { return component; } Component[] components = source.GetComponents(); for (int i = 0; i < components.Length; i++) { Component candidate = components[i]; if (candidate != null && componentType.IsInstanceOfType(candidate)) { return candidate; } } return null; } public static class PrefabChecker { public static bool IsEditingPrefabAsset(GameObject go) { var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); return prefabStage?.IsPartOfPrefabContents(go) == true; } public static bool IsPrefabAsset(GameObject go) { if (go == null) return false; var assetType = PrefabUtility.GetPrefabAssetType(go); var isRegularPrefab = assetType == PrefabAssetType.Regular || assetType == PrefabAssetType.Variant || assetType == PrefabAssetType.Model; return isRegularPrefab || IsEditingPrefabAsset(go); } } } }