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
相关产品推荐
相关产品推荐

