Unity中SerializeReference数组添加元素时断言失败问题求助
问题分析与解决方案
断言错误根源
你遇到的IsInSyncWithParentSerializedObject()断言失败,是因为在属性绘制流程中直接修改序列化属性并强制提交(ApplyModifiedProperties()),破坏了Unity序列化系统的同步状态:
CreatePropertyGUI执行时,父SerializedObject可能处于未完成的编辑状态,强制提交会导致子属性与父对象的序列化状态不同步,触发断言检查失败。- 直接赋值
managedReferenceValue后未通过Undo系统记录操作,也会导致序列化流程异常。
修复与功能实现代码
1. 基类与派生类定义(补充你缺失的部分)
namespace Test { public abstract class BaseClass { public string baseCommonField; } [System.Serializable] public class DerivedClass1 : BaseClass { public int derivedIntField; } [System.Serializable] public class DerivedClass2 : BaseClass { public float derivedFloatField; } }
2. 修正后的编辑器PropertyDrawer代码
namespace Editor { using System; using System.Linq; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; using Test; [CustomPropertyDrawer(typeof(BaseClass), true)] public class SubclassPropertyDrawer : PropertyDrawer { public override VisualElement CreatePropertyGUI(SerializedProperty property) { var root = new VisualElement(); // 1. 创建派生类选择下拉框 var classOptions = GetDerivedClassNames(); var classSelector = new PopupField<string>(classOptions, GetCurrentClassName(property)) { label = "选择派生类" }; classSelector.RegisterValueChangedCallback(evt => { var targetType = Type.GetType($"Test.{evt.newValue}"); if (targetType == null) return; // 用Undo记录操作,确保序列化同步与可撤销 Undo.RecordObject(property.serializedObject.targetObject, "切换派生类类型"); property.managedReferenceValue = Activator.CreateInstance(targetType); EditorUtility.SetDirty(property.serializedObject.targetObject); property.serializedObject.Update(); }); root.Add(classSelector); // 2. 初始化空引用(避免数组新增元素时的null状态) if (property.managedReferenceValue == null) { Undo.RecordObject(property.serializedObject.targetObject, "初始化派生类实例"); property.managedReferenceValue = Activator.CreateInstance(typeof(DerivedClass1)); EditorUtility.SetDirty(property.serializedObject.targetObject); property.serializedObject.Update(); } // 3. 绘制派生类字段 var foldout = new Foldout { text = property.displayName }; var propertyField = new PropertyField(property, string.Empty); foldout.Add(propertyField); root.Add(foldout); return root; } // 获取所有可序列化的BaseClass派生类名称 private string[] GetDerivedClassNames() { return AppDomain.CurrentDomain.GetAssemblies() .SelectMany(assembly => assembly.GetTypes()) .Where(type => type.IsSubclassOf(typeof(BaseClass)) && !type.IsAbstract && type.IsSerializable) .Select(type => type.Name) .ToArray(); } // 获取当前引用的类名称 private string GetCurrentClassName(SerializedProperty property) { return property.managedReferenceValue?.GetType().Name ?? string.Empty; } } }
关键修复点说明
- 序列化同步修复:
- 移除
ApplyModifiedProperties(),改用Undo.RecordObject记录修改操作,配合EditorUtility.SetDirty标记对象需要保存,最后用property.serializedObject.Update()刷新状态,完全符合Unity编辑器的序列化工作流程,避免同步异常。
- 移除
- 派生类选择功能:
- 通过反射自动获取所有可序列化的
BaseClass派生类,生成下拉选项,实现一键切换派生类类型。
- 通过反射自动获取所有可序列化的
- 属性绘制优化:
- 移除了不可靠的枚举器遍历子属性逻辑,直接用
PropertyField绘制整个序列化属性,Unity会自动处理不同派生类的字段显示,代码更简洁可靠。
- 移除了不可靠的枚举器遍历子属性逻辑,直接用
注意事项
- 所有派生类必须标记
[Serializable]特性,否则Unity无法序列化它们。 - 基类
BaseClass可以是抽象类,但需确保派生类无抽象成员且可实例化。 [CustomPropertyDrawer(typeof(BaseClass), true)]中的true表示该Drawer自动应用于所有派生类,无需重复编写。
内容的提问来源于stack exchange,提问作者Kousen
相关产品推荐
相关产品推荐

