You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何枚举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中正常显示和保存。

内容的提问来源于stack exchange,提问作者Daniel Famakin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 17:01:05