Unity可序列化类的Nullable Int字段无法在Inspector显示的解决办法问询
解决Unity Inspector中可空值类型(int?)不显示的问题
Unity的内置序列化系统对.NET的可空值类型(像int?这种)支持得不太好,这就是为啥你的Cost字段在Inspector里看不到的原因——Unity自带的序列化器不会自动处理可空类型的序列化和界面渲染。下面给你几种靠谱的解决办法,按推荐程度排好了:
方法1:使用可序列化的包装类+自定义属性绘制器(首推)
这种方法既能保留可空类型的逻辑,又能在Inspector里获得和原生字段差不多的友好编辑体验。
步骤1:写个可序列化的可空int包装类
先定义一个专门用来封装可空int的类,把可空值拆成Unity能序列化的普通字段:
using System; using UnityEngine; [Serializable] public class NullableInt { [SerializeField] private int _value; [SerializeField] private bool _hasValue; // 对外暴露的可空值属性,用起来和原生int?一样 public int? Value { get => _hasValue ? _value : null; set { _hasValue = value.HasValue; _value = value ?? default; } } // 加个隐式转换,让代码里赋值取值更顺手 public static implicit operator int?(NullableInt nullableInt) => nullableInt.Value; public static implicit operator NullableInt(int? value) => new NullableInt { Value = value }; }
步骤2:替换原有的可空字段
修改你的CellType类,把int? Cost换成NullableInt Cost就行:
[Serializable] public class CellType { [SerializeField] public NullableInt Cost; [SerializeField] public Color MaterialColor; }
步骤3:自定义Inspector绘制器(可选但强烈推荐)
默认情况下,Inspector会显示_hasValue和_value两个字段,看着有点乱。我们可以写个自定义属性绘制器,把它改成带勾选框的数值输入框——勾选就表示有值,不勾选就是null:
using UnityEditor; using UnityEngine; [CustomPropertyDrawer(typeof(NullableInt))] public class NullableIntDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { // 拿到包装类里的两个序列化字段 var hasValueProp = property.FindPropertyRelative("_hasValue"); var valueProp = property.FindPropertyRelative("_value"); EditorGUI.BeginProperty(position, label, property); // 先绘制字段的前缀标签 position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); // 把区域拆成两部分:左边放勾选框,右边放数值输入框 var toggleRect = new Rect(position.x, position.y, 20, position.height); var valueRect = new Rect(position.x + 25, position.y, position.width - 25, position.height); // 绘制勾选框,控制是否有值 hasValueProp.boolValue = EditorGUI.Toggle(toggleRect, hasValueProp.boolValue); // 没勾选的时候灰掉输入框,避免误操作 EditorGUI.BeginDisabledGroup(!hasValueProp.boolValue); valueProp.intValue = EditorGUI.IntField(valueRect, valueProp.intValue); EditorGUI.EndDisabledGroup(); EditorGUI.EndProperty(); } }
做完这步,你的Inspector里Cost字段就会变成一个带勾选框的输入框,体验和原生字段完全一致。
方法2:用特殊标记值替代可空类型(简单但不优雅)
如果不想写额外的类和绘制器,可以用一个特殊的int值来代表“无值”,比如int.MinValue:
[Serializable] public class CellType { [SerializeField] public int Cost = int.MinValue; // 用MinValue表示null [SerializeField] public Color MaterialColor; // 对外提供可空的访问属性,方便代码里判断 public int? CostValue => Cost == int.MinValue ? null : Cost; }
这种方法的缺点是你得在代码里时刻记得判断这个特殊值,一不小心就容易出bug,适合简单场景临时用用。
方法3:利用SerializeReference(Unity 2019.3+)
Unity 2019.3及以上版本支持[SerializeReference]特性,能序列化引用类型,我们可以用这个来实现可空值:
[Serializable] public class NullableIntWrapper : ISerializationCallbackReceiver { public int? Value; [SerializeField] private int _serializedValue; [SerializeField] private bool _hasValue; // 序列化前把可空值拆成普通字段 public void OnBeforeSerialize() { _hasValue = Value.HasValue; _serializedValue = Value ?? default; } // 反序列化后把普通字段还原成可空值 public void OnAfterDeserialize() { Value = _hasValue ? _serializedValue : null; } }
然后在CellType里用这个类:
[Serializable] public class CellType { [SerializeReference] public NullableIntWrapper Cost; [SerializeField] public Color MaterialColor; }
不过这种方法需要手动处理序列化回调,而且SerializeReference本来是给多态类型用的,用来处理简单的可空值有点大材小用了。
内容的提问来源于stack exchange,提问作者Question3r
相关产品推荐
相关产品推荐

