如何枚举PartBase子类实现可扩展的编辑器下拉选择?
解决方案:自动识别PartBase子类并实现无枚举/switch的部件添加
核心思路
通过反射自动收集所有继承自PartBase的非抽象子类,在编辑器中动态生成下拉选择项,选中后直接创建实例并添加到Whole的部件列表中,彻底摆脱枚举和switch分支的维护。
步骤1:调整Whole类,添加非泛型的部件添加方法
为了避免直接反射调用泛型方法的复杂性,给Whole类新增一个接收Type参数的AddPart方法,内部处理实例创建和添加逻辑:
[CreateAssetMenu] [Serializable] // 确保列表能在Inspector显示 public class Whole : ScriptableObject { [SerializeField] private List<PartBase> parts = new(); // 原泛型方法保留,兼容原有逻辑 public void AddPart<T>() where T : PartBase, new() { parts.Add(new T()); EditorUtility.SetDirty(this); } // 新增非泛型方法,供编辑器反射调用 public void AddPart(Type partType) { if (typeof(PartBase).IsAssignableFrom(partType) && !partType.IsAbstract && partType.GetConstructor(Type.EmptyTypes) != null) { var part = Activator.CreateInstance(partType) as PartBase; parts.Add(part); EditorUtility.SetDirty(this); // 标记资产为脏,确保修改能保存 } } } // 确保PartBase子类都标记[Serializable],否则无法在Inspector显示 [Serializable] public abstract class PartBase : ScriptableObject { } // 示例子类,用户只需创建类似脚本即可 [Serializable] public class BarPart : PartBase { } [Serializable] public class BazPart : PartBase { }
步骤2:编写自定义Editor实现动态下拉选择
通过Unity的自定义Editor扩展,在Whole的Inspector面板中添加动态生成的下拉菜单和添加按钮:
using System.Linq; using System.Reflection; using UnityEditor; using UnityEngine; [CustomEditor(typeof(Whole))] public class WholeEditor : Editor { private Type[] _availablePartTypes; private int _selectedPartIndex = -1; private void OnEnable() { // 收集所有符合条件的PartBase子类: // 1. 继承自PartBase 2. 非抽象类 3. 有无参构造函数 _availablePartTypes = Assembly.GetAssembly(typeof(PartBase)) .GetTypes() .Where(t => t.IsSubclassOf(typeof(PartBase)) && !t.IsAbstract && t.GetConstructor(Type.EmptyTypes) != null) .ToArray(); } public override void OnInspectorGUI() { base.OnInspectorGUI(); var wholeTarget = target as Whole; if (wholeTarget == null) return; // 生成下拉选项(显示类名) string[] partNames = _availablePartTypes.Select(t => t.Name).ToArray(); _selectedPartIndex = EditorGUILayout.Popup("选择要添加的部件", _selectedPartIndex, partNames); // 添加按钮逻辑 using (new EditorGUI.DisabledScope(_selectedPartIndex < 0)) { if (GUILayout.Button("添加部件")) { wholeTarget.AddPart(_availablePartTypes[_selectedPartIndex]); // 刷新Inspector显示 serializedObject.Update(); serializedObject.ApplyModifiedProperties(); } } } }
关键优势与注意事项
- 零维护成本:用户只需创建继承自
PartBase的新脚本(记得加[Serializable]),无需修改任何枚举或原有代码,新部件会自动出现在下拉菜单中。 - 兼容性:保留了原有的泛型
AddPart<T>方法,不影响原有逻辑的使用。 - 注意点:
- 如果子类位于其他Assembly中(比如用户自己的Assembly),需要调整
Assembly.GetAssembly(typeof(PartBase))为对应的Assembly获取方式,例如typeof(BarPart).Assembly。 - 所有
PartBase子类必须有无参构造函数(符合原泛型方法的new()约束),否则会被过滤掉。 - 必须给
PartBase及其子类添加[Serializable]属性,否则部件列表无法在Inspector中正常显示和保存。
- 如果子类位于其他Assembly中(比如用户自己的Assembly),需要调整
内容的提问来源于stack exchange,提问作者Daniel Famakin
相关产品推荐
相关产品推荐

