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

Unity中如何根据输入类型动态创建/移除Inspector字段?

动态创建/移除Inspector字段 & 面向数据的子类扩展方案

问题描述

能不能通过输入类型动态创建和移除Inspector里的字段?这可行吗?

我现在的需求是:给抽象类的每个新子类都对应创建ScriptableObject类。比如现在只能让人类装备剑,之后要加穿戴盔甲的功能,但这时候我就得修改人类的ScriptableObject或者预制体来支持新功能。而且如果我不想让所有人类都能穿盔甲呢?预制体虽然能解决,但我想要更面向数据的方案。

附当前尝试的代码:

foreach (FieldInfo fieldInfo in customType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
  // 这里需要把fieldInfo转换成serializedProperty或者其他可用对象

  EditorGUILayout.PropertyField(/*?*/); // 或者其他替代方法
}

可行方案

1. 自定义Editor实现动态Inspector字段

你的思路方向是对的,但直接用反射的FieldInfo配合EditorGUILayout.PropertyField走不通——Unity的序列化系统只认标记了[SerializeField]的字段,且PropertyField需要的是SerializedProperty而非反射得到的FieldInfo。

正确的实现方式是自定义Editor类,结合SerializedObject来处理:

  • 先确保类中需要显示的字段都标记了[SerializeField](私有字段也可以)
  • 在自定义Editor中遍历序列化属性,按需过滤显示:
[CustomEditor(typeof(YourBaseScriptableObject))]
public class DynamicInspectorEditor : Editor
{
    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        
        // 遍历所有可见的序列化属性
        SerializedProperty prop = serializedObject.GetIterator();
        if (prop.NextVisible(true))
        {
            do
            {
                // 根据目标对象的类型过滤字段
                if (ShouldShowProperty(prop))
                {
                    EditorGUILayout.PropertyField(prop, true);
                }
            } while (prop.NextVisible(false));
        }
        
        serializedObject.ApplyModifiedProperties();
    }
    
    private bool ShouldShowProperty(SerializedProperty prop)
    {
        var targetObj = target as YourBaseScriptableObject;
        // 示例:不同子类显示不同字段
        if (targetObj is HumanWithSword)
        {
            return prop.name != "armor";
        }
        else if (targetObj is HumanWithArmor)
        {
            return prop.name != "sword";
        }
        return true;
    }
}

2. 面向数据的子类扩展:组合式ScriptableObject方案

不想修改原有ScriptableObject又要实现能力差异化,推荐用组合模式,完全基于数据驱动:

  • 先定义抽象的能力数据基类:
public abstract class AbilityData : ScriptableObject { }
  • 为每种能力创建独立的ScriptableObject子类:
[CreateAssetMenu(fileName = "SwordAbility", menuName = "Abilities/Sword")]
public class SwordAbilityData : AbilityData
{
    public float attackDamage;
    public GameObject swordPrefab;
}

[CreateAssetMenu(fileName = "ArmorAbility", menuName = "Abilities/Armor")]
public class ArmorAbilityData : AbilityData
{
    public float defense;
    public GameObject armorPrefab;
}
  • 在人类的基础数据类中,用列表挂载不同能力:
public class HumanData : ScriptableObject
{
    public string humanName;
    public List<AbilityData> abilities;
}

这种方案的优势:

  • 新增能力只需要创建新的AbilityData子类,完全不用修改原有HumanData
  • 不同人类实例可以挂载不同的能力数据,轻松实现“有的人类能穿甲,有的只能拿剑”
  • Inspector会自动识别每个AbilityData的具体字段,无需额外写Editor(要优化显示的话再自定义即可)

3. 进阶:用自定义属性标记控制字段显示

如果需要更灵活的字段控制,可以自定义一个属性标记,比如[ShowOnlyForType(typeof(HumanWithArmor))],然后在自定义Editor中检查字段是否带有该属性,决定是否渲染:

[AttributeUsage(AttributeTargets.Field)]
public class ShowOnlyForTypeAttribute : Attribute
{
    public Type TargetType { get; }
    public ShowOnlyForTypeAttribute(Type targetType) => TargetType = targetType;
}

之后在Editor的ShouldShowProperty方法里,通过反射检查字段是否带有该属性,匹配则显示。

内容的提问来源于stack exchange,提问作者Stefan Carpeliuc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 03:45:30