Unity Editor嵌套ScriptableObject内部字段无法序列化问题咨询
Unity 默认序列化规则中,嵌套的ScriptableObject引用仅会序列化资源ID,Inspector默认仅展示引用选择器,不会展开内部字段;且直接修改引用SO的字段会改动原始资源,无法实现同一份SO模板在不同上下文下的差异化配置。
方案1:中间包装类(最推荐,符合复用需求)
直接定义可序列化的包装结构,将公共模板引用和差异化字段分开存储,差异化字段序列化在上层ScriptableExpressionCreator中,不会修改原始SO资源,无需自定义编辑器即可原生支持Inspector编辑。
代码示例:
[System.Serializable] public class ExpressionBuilderEntry { // 公共模板,所有引用位置共用MultipleExpressionBuilder等配置 public ScriptableExpressionBuilder builderTemplate; // 当前上下文独有的delay值,仅保存在当前Creator资源中 public float overrideDelay; // 可选:封装取值逻辑,支持默认用模板delay的 fallback 逻辑 public float GetFinalDelay() { // 例:overrideDelay设为-1时用模板默认值,否则用自定义值 return overrideDelay < 0 ? builderTemplate.delay : overrideDelay; } }
修改ScriptableExpressionCreator的列表定义:
public class ScriptableExpressionCreator : ScriptableObject { public List<ExpressionBuilderEntry> builderEntries; }
业务逻辑中取值时,直接调用entry.GetFinalDelay()即可,原始SO的MultipleExpressionBuilder配置可通过entry.builderTemplate.multipleExpressionBuilder正常读取。
方案2:自定义Inspector内嵌编辑(适合复杂编辑需求)
如果需要在Creator的Inspector中直接展示ScriptableExpressionBuilder的完整字段,同时支持覆写存储,可以为ScriptableExpressionCreator实现自定义Editor,动态创建每个SO的内嵌Inspector,同时自己维护覆写字段的存储逻辑,避免修改原始SO资源。
核心编辑器代码示例(放在Editor目录下):
[CustomEditor(typeof(ScriptableExpressionCreator))] public class ExpressionCreatorEditor : Editor { private SerializedProperty _entriesProp; private readonly Dictionary<ScriptableExpressionBuilder, Editor> _cachedEditors = new(); private void OnEnable() { _entriesProp = serializedObject.FindProperty("builderEntries"); } public override void OnInspectorGUI() { serializedObject.Update(); for (int i = 0; i < _entriesProp.arraySize; i++) { var entryProp = _entriesProp.GetArrayElementAtIndex(i); var templateProp = entryProp.FindPropertyRelative("builderTemplate"); var delayProp = entryProp.FindPropertyRelative("overrideDelay"); EditorGUILayout.PropertyField(templateProp); if (templateProp.objectReferenceValue is ScriptableExpressionBuilder builder) { EditorGUI.indentLevel++; // 显示覆写delay字段 EditorGUILayout.PropertyField(delayProp); // 可选:展示模板的只读公共配置 if (!_cachedEditors.TryGetValue(builder, out var editor)) { Editor.CreateCachedEditor(builder, null, ref editor); _cachedEditors[builder] = editor; } using (new EditorGUI.DisabledScope(true)) { editor.OnInspectorGUI(); } EditorGUI.indentLevel--; } EditorGUILayout.Space(); } serializedObject.ApplyModifiedProperties(); } }
不推荐方案:取消ScriptableObject继承
如果不需要公共复用ScriptableExpressionBuilder资源,可以将其改为普通可序列化类,去掉ScriptableObject继承并加[Serializable]标记,上层列表会自动展开所有字段序列化,但无法实现同一份模板多场景复用的需求。
内容的提问来源于stack exchange,提问作者rootpanthera

