#if UNITY_EDITOR using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor; using UnityEngine; using UnityEngine.Events; using Object = UnityEngine.Object; namespace SLSFramework.UModAssistance { #region List选择器,通过类型查找资产,将其名称存储在列表中 public partial class DataEditor : Editor { private string _pickerTargetListName; private Dictionary _assetCache; protected virtual void OnEnable() { // 每次选中新对象时,都清空缓存 _assetCache = new Dictionary(); } protected void DrawCharacterListGUI(SerializedProperty listProperty, string searchFilter = "") where T : Object { if (string.IsNullOrEmpty(searchFilter)) { searchFilter = $"t:{typeof(T).Name}"; } listProperty.isExpanded = EditorGUILayout.Foldout(listProperty.isExpanded, listProperty.displayName, false); if (listProperty.isExpanded) { EditorGUI.indentLevel++; for (int i = 0; i < listProperty.arraySize; i++) { SerializedProperty elementNameProp = listProperty.GetArrayElementAtIndex(i); EditorGUILayout.BeginHorizontal(); EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(elementNameProp, GUIContent.none); bool valueChanged = EditorGUI.EndChangeCheck(); string assetName = elementNameProp.stringValue; if (valueChanged || !_assetCache.TryGetValue(assetName, out Object foundAsset)) { foundAsset = FindObjectData(assetName, searchFilter); _assetCache[assetName] = foundAsset; // 将搜索结果(即使是null)存入缓存 } if (foundAsset != null) { EditorGUI.BeginDisabledGroup(true); EditorGUILayout.ObjectField(foundAsset, typeof(T), false, GUILayout.Width(150)); EditorGUI.EndDisabledGroup(); } else { EditorGUILayout.LabelField(new GUIContent(EditorGUIUtility.IconContent("console.warnicon").image, "资产未找到或名称不匹配"), GUILayout.Width(20)); } if (GUILayout.Button("-", GUILayout.Width(20))) { _assetCache.Remove(elementNameProp.stringValue); listProperty.DeleteArrayElementAtIndex(i); i--; } EditorGUILayout.EndHorizontal(); } if (GUILayout.Button("Add by Search...")) { _pickerTargetListName = listProperty.propertyPath; EditorGUIUtility.ShowObjectPicker(null, false, searchFilter, GUI.skin.GetHashCode()); } EditorGUI.indentLevel--; } } protected T FindObjectData(string assetName, string searchFilter) where T : Object { if (string.IsNullOrEmpty(assetName)) return null; string[] guids = AssetDatabase.FindAssets($"{assetName} {searchFilter}"); if (guids.Length > 0) { return AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guids[0])); } return null; } protected void HandleObjectPicker() { if (Event.current.commandName == "ObjectSelectorUpdated" && EditorGUIUtility.GetObjectPickerControlID() == GUI.skin.GetHashCode()) { Object pickedObject = EditorGUIUtility.GetObjectPickerObject(); if (pickedObject != null) { SerializedProperty targetListProp = serializedObject.FindProperty(_pickerTargetListName); if (targetListProp != null) { bool exists = false; for (int i = 0; i < targetListProp.arraySize; i++) { if (targetListProp.GetArrayElementAtIndex(i).stringValue == pickedObject.name) { exists = true; break; } } if (!exists) { targetListProp.InsertArrayElementAtIndex(targetListProp.arraySize); targetListProp.GetArrayElementAtIndex(targetListProp.arraySize - 1).stringValue = pickedObject.name; } } _pickerTargetListName = null; } } } } #endregion #region Type选择器,将获取的类型名存入一个string中 public partial class DataEditor { private static Dictionary, (string[] paths, Type[] types)> _typeCache = new Dictionary, (string[], Type[])>(); /// /// 绘制一个用于选择指定基类的所有子类的下拉菜单 /// /// 存储类名的字符串属性 /// 在Inspector中显示的标签 /// 要查找的基类 (例如 typeof(CardLogicBase)) /// 可选参数,用于从路径中移除特定的命名空间部分 (例如 ".Cards") /// 如果值被用户改变,则返回true protected bool DrawTypeSelectorGUI(SerializedProperty classNameProp, string label, Type baseType, out Type returnedType, string namespacePrefix = null, string namespaceToRemove = null) { // --- 核心修改 2:使用包含 namespaceToRemove 的复合键 --- var cacheKey = new Tuple(baseType, namespaceToRemove ?? string.Empty); if (!_typeCache.ContainsKey(cacheKey)) { CacheDerivedTypes(baseType, namespacePrefix, namespaceToRemove, cacheKey); } var (paths, types) = _typeCache[cacheKey]; int currentIndex = -1; for (int i = 0; i < types.Length; i++) { if (types[i].Name == classNameProp.stringValue) { currentIndex = i; break; } } int newIndex = EditorGUILayout.Popup(label, currentIndex, paths); if (newIndex != currentIndex) { classNameProp.stringValue = (newIndex >= 0 && newIndex < types.Length) ? types[newIndex].Name : string.Empty; returnedType = types[newIndex]; return true; } returnedType = null; return false; } private void CacheDerivedTypes(Type baseType, string namespacePrefix, string namespaceToRemove, Tuple cacheKey) { List<(string path, Type type)> typeList = new List<(string, Type)>(); IEnumerable types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(assembly => assembly.GetTypes()) .Where(t => baseType.IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface && t != baseType); foreach (var type in types) { string path = "Uncategorized/" + type.Name; if (type.Namespace != null && type.Namespace.StartsWith(namespacePrefix)) { string formattedNamespace = type.Namespace.Substring(namespacePrefix.Length); // --- 核心修改 3:使用传入的参数来替换硬编码 --- if (!string.IsNullOrEmpty(namespaceToRemove)) { formattedNamespace = formattedNamespace.Replace("." + namespaceToRemove, ""); } formattedNamespace = formattedNamespace.Replace('.', '/'); if (formattedNamespace.StartsWith("/")) formattedNamespace = formattedNamespace.Substring(1); path = formattedNamespace + "/" + type.Name; } typeList.Add((path, type)); } typeList.Sort((a, b) => a.path.CompareTo(b.path)); _typeCache[cacheKey] = (typeList.Select(t => t.path).ToArray(), typeList.Select(t => t.type).ToArray()); } } #endregion #region 绘制一个按钮,点击后调用指定方法 public partial class DataEditor { protected void DrawMethodButton(string buttonLabel, string functionName) where T : Object { if (GUILayout.Button(buttonLabel)) { T invoker = target as T; MethodInfo method = typeof(T).GetMethod(functionName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { method.Invoke(invoker, null); } else { Debug.LogWarning($"Method '{functionName}' not found in type '{typeof(T).Name}'."); } } } } #endregion } #endif