Files
Continentis/Assets/Scripts/ScriptExtensions/UModAssistance/Editor/DataEditor.cs
SoulliesOfficial 76157e3cb1 继续
2025-10-24 09:11:22 -04:00

250 lines
9.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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<string>
public partial class DataEditor : Editor
{
private string _pickerTargetListName;
private Dictionary<string, Object> _assetCache;
protected virtual void OnEnable()
{
// 每次选中新对象时,都清空缓存
_assetCache = new Dictionary<string, Object>();
}
protected void DrawCharacterListGUI<T>(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<T>(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<T>(null, false, searchFilter, GUI.skin.GetHashCode());
}
EditorGUI.indentLevel--;
}
}
protected T FindObjectData<T>(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<T>(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<Tuple<Type, string>, (string[] paths, Type[] types)> _typeCache =
new Dictionary<Tuple<Type, string>, (string[], Type[])>();
/// <summary>
/// 绘制一个用于选择指定基类的所有子类的下拉菜单
/// </summary>
/// <param name="classNameProp">存储类名的字符串属性</param>
/// <param name="label">在Inspector中显示的标签</param>
/// <param name="baseType">要查找的基类 (例如 typeof(CardLogicBase))</param>
/// <param name="namespaceToRemove">可选参数,用于从路径中移除特定的命名空间部分 (例如 ".Cards")</param>
/// <returns>如果值被用户改变则返回true</returns>
protected bool DrawTypeSelectorGUI(SerializedProperty classNameProp, string label, Type baseType,
out Type returnedType, string namespacePrefix = null, string namespaceToRemove = null)
{
// --- 核心修改 2使用包含 namespaceToRemove 的复合键 ---
var cacheKey = new Tuple<Type, string>(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<Type, string> cacheKey)
{
List<(string path, Type type)> typeList = new List<(string, Type)>();
IEnumerable<Type> 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<T>(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