在Scriptable Object中声明动态枚举类型变量的技术问询
适配所有枚举类型的Scriptable Variable实现方案
直接在Unity里动态声明让Inspector自动识别的枚举字段是行不通的——Inspector依赖编译期的字段元数据,没办法动态生成可编辑的字段。不过用反射+自定义Editor可以完全实现你要的功能,核心思路是用字符串存储枚举类型,用int存储枚举值,再通过自定义Editor层做类型转换和界面适配。
1. 核心ScriptableObject类
这个类负责存储枚举类型标识、初始值和当前值,通过属性封装反射转换逻辑:
using UnityEngine; [CreateAssetMenu(fileName = "New Enum Variable", menuName = "Variables/Enum Variable")] public class ScriptableVariable_Enum : ScriptableObject { [SerializeField] private string enumTypeName; [SerializeField] private int initialValue; [SerializeField] private int currentValue; // 获取当前选中的枚举类型 public System.Type EnumType => !string.IsNullOrEmpty(enumTypeName) ? System.Type.GetType(enumTypeName) : null; // 封装初始值的枚举转换 public System.Enum InitialValue => EnumType != null ? (System.Enum)System.Enum.ToObject(EnumType, initialValue) : null; // 封装当前值的枚举转换与赋值 public System.Enum CurrentValue { get => EnumType != null ? (System.Enum)System.Enum.ToObject(EnumType, currentValue) : null; set { if (EnumType != null && value.GetType() == EnumType) { currentValue = (int)(object)value; } } } private void OnEnable() { // Play模式启动时重置当前值为初始值 if (Application.isPlaying) { currentValue = initialValue; } } }
2. 自定义Editor实现Inspector适配
通过自定义Editor,用反射遍历所有枚举类型生成选择下拉框,再把存储的int值转换成枚举下拉框展示给用户:
using UnityEditor; using UnityEngine; using System.Linq; [CustomEditor(typeof(ScriptableVariable_Enum))] public class ScriptableVariable_EnumEditor : Editor { private ScriptableVariable_Enum targetVar; private System.Type selectedEnumType; private void OnEnable() { targetVar = (ScriptableVariable_Enum)target; selectedEnumType = targetVar.EnumType; } public override void OnInspectorGUI() { // 枚举类型选择区 EditorGUILayout.LabelField("枚举类型", EditorStyles.boldLabel); var allEnums = System.AppDomain.CurrentDomain.GetAssemblies() .SelectMany(asm => asm.GetTypes()) .Where(t => t.IsEnum) .ToList(); string[] enumOptions = allEnums.Select(t => t.FullName).ToArray(); int currentIndex = selectedEnumType != null ? enumOptions.ToList().IndexOf(selectedEnumType.FullName) : -1; int newIndex = EditorGUILayout.Popup(currentIndex, enumOptions); // 切换枚举类型时重置默认值 if (newIndex != currentIndex && newIndex >= 0) { selectedEnumType = allEnums[newIndex]; targetVar.enumTypeName = selectedEnumType.FullName; var firstEnumValue = System.Enum.GetValues(selectedEnumType).GetValue(0); targetVar.initialValue = (int)(object)firstEnumValue; targetVar.currentValue = (System.Enum)firstEnumValue; EditorUtility.SetDirty(targetVar); } // 已选择枚举类型时,显示初始值和当前值的枚举下拉框 if (selectedEnumType != null) { EditorGUILayout.Space(); EditorGUILayout.LabelField("初始值", EditorStyles.boldLabel); var currentInitial = System.Enum.ToObject(selectedEnumType, targetVar.initialValue); var newInitial = EditorGUILayout.EnumPopup(currentInitial); if (!newInitial.Equals(currentInitial)) { targetVar.initialValue = (int)(object)newInitial; EditorUtility.SetDirty(targetVar); } EditorGUILayout.Space(); EditorGUILayout.LabelField("当前值", EditorStyles.boldLabel); var currentValue = System.Enum.ToObject(selectedEnumType, targetVar.currentValue); var newValue = EditorGUILayout.EnumPopup(currentValue); if (!newValue.Equals(currentValue)) { targetVar.currentValue = (System.Enum)newValue; EditorUtility.SetDirty(targetVar); } } if (GUI.changed) { serializedObject.ApplyModifiedProperties(); } } }
关键注意事项
- 类型稳定性:当前用
FullName存储枚举类型,若枚举重命名或移动程序集,会导致类型无法识别,可改用AssemblyQualifiedName存储更完整的类型标识(代价是字符串更长)。 - 运行时校验:给
CurrentValue赋值时,要确保传入的枚举类型与选中的类型一致,否则赋值会被忽略。 - 性能考量:Editor里遍历所有程序集的枚举类型只在Inspector绘制时执行,不会影响运行时性能。
内容的提问来源于stack exchange,提问作者Christoph
相关产品推荐
相关产品推荐

