Unity自定义编辑器正确序列化方法及两种实现的差异疑问
测试脚本
public class Test : MonoBehaviour { public int something; }
1. 不使用SerializedProperty直接设置值的实现
[CustomEditor(typeof(Test))] public class TestEditor : Editor { private Test test; private void OnEnable() { test = target as Test; } public override void OnInspectorGUI() { test.something = EditorGUILayout.IntField("Something", test.something); if (GUI.changed) EditorUtility.SetDirty(this); } }
现象:修改后的值在编辑器运行时可正常显示,但重启Unity后数值会被重置,未被序列化写入磁盘。
2. 使用SerializedProperty的实现
[CustomEditor(typeof(Test))] public class TestEditor : Editor { public override void OnInspectorGUI() { serializedObject.Update(); var something = serializedObject.FindProperty("something"); something.intValue = EditorGUILayout.IntField("Something", something.intValue); serializedObject.ApplyModifiedProperties(); } }
现象:所有功能正常,重启Unity后数值也能正常保留。
问题解答
问题1:为什么第一种写法可在编辑器运行时保存值,但无法序列化写入磁盘?是否是代码存在错误?
代码确实存在错误,问题出在EditorUtility.SetDirty(this)这一行:你把当前TestEditor编辑器实例标记为了脏对象,而不是你实际修改了字段的test(也就是target)实例。
即便你把参数改成test,这种写法依然存在隐患:直接修改C#实例的字段只会修改内存中的值,Unity的序列化系统不会自动感知到修改需要同步到磁盘文件,只有正确触发了序列化流程的修改才会被持久化存储。编辑器运行时你看到的是内存中修改后的值,重启后Unity从磁盘重新读取资源,没有写入的修改自然就丢失了。
问题2:两种实现方式的核心差异是什么?
两种写法的核心差异是是否走Unity官方封装的序列化中间层:
- 第一种是直接操作原生C#对象的内存值,所有和编辑器相关的配套逻辑(脏标记、序列化写入、撤销重做、多对象编辑、Prefab覆盖标记)都需要开发者手动实现,很容易出现疏漏。
- 第二种的
SerializedObject和SerializedProperty是Unity对序列化逻辑的封装层,serializedObject.Update()会把最新的磁盘序列化数据同步到内存层,你修改SerializedProperty的过程中,Unity会自动记录撤销栈、处理多对象同步,最后调用ApplyModifiedProperties()时会自动把修改同步到原生C#实例,同时标记对象为脏、触发序列化写入磁盘的流程,所有配套逻辑不需要手动处理。
问题3:第二种使用SerializedProperty的写法是不是自定义编辑器中正确的序列化实现方式?
是,这是Unity官方推荐的自定义Inspector标准实现方式,除了能保证序列化正常持久化之外,还能自动兼容原生Inspector的所有基础功能:撤销重做、多选对象批量编辑、Prefab属性覆盖标记、Prefab变体属性继承等,没有特殊需求的情况下都应该优先用这种写法实现自定义编辑器。
内容的提问来源于stack exchange,提问作者Logarius
相关产品推荐
相关产品推荐

