You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义序列化字段进入Unity游戏模式后值丢失,求解决方案

问题

实现了自定义序列化字段,能在Unity Inspector面板显示并设置值,但切换到游戏模式后字段值会被重置。不想让模块继承MonoBehaviour,也不想为了初始化字段重载无关的函数与属性。

相关代码:

using System;
using UnityEditor;
using UnityEngine;
using Helpers.Utils;
using System.Reflection;
using System.Collections.Generic;

namespace GameEnviroment.Assembling
{
    public class GAssembly : MonoBehaviour
    {
        private readonly static List<FieldInfo> GSerializedModulesFields = new();

        public static LabModule LabModule { get; private set; } = new LabModule();
        public static EvalModule EvalModule { get; private set; } = new EvalModule();
        public static AdsModule AdsModule { get; private set; } = new AdsModule();

        private readonly static List<GModule> Modules = new()
        {
            LabModule, EvalModule, AdsModule
        };

        private void Awake()
        {
            DontDestroyOnLoad(this);
        }

#if UNITY_EDITOR
        [CustomEditor(typeof(GAssembly))]
        internal sealed class GAssemblyInspector : Editor
        {
            public override void OnInspectorGUI()
            {
                DrawDefaultInspector();
                
                serializedObject.Update();

                if (Modules == null) return;

                for (int i = 0; i < GSerializedModulesFields.Count; i++)
                {
                    if (i == 0 || GSerializedModulesFields[i].DeclaringType.Name != GSerializedModulesFields[i - 1].DeclaringType.Name)
                    {
                        GUILayout.Space(10f);
                        GUILayout.Label(GUtility.HandleFieldName(GSerializedModulesFields[i].DeclaringType.Name, '-', false, '_'), EditorStyles.boldLabel);
                        GUILayout.Space(5f);
                    }

                    if (GUtility.GetFinalBaseType(GSerializedModulesFields[i].FieldType) == typeof(UnityEngine.Object))
                    {
                        GModule module = Modules.Find(m => m.GetType() == GSerializedModulesFields[i].DeclaringType);

                        GSerializedModulesFields[i].SetValue(module, EditorGUILayout.ObjectField(
                            label: GUtility.HandleFieldName(GSerializedModulesFields[i].Name, exclude: '_'),
                            obj: GSerializedModulesFields[i].GetValue(module) as UnityEngine.Object,
                            objType: GSerializedModulesFields[i].FieldType,
                            allowSceneObjects: true));
                    }
                    
                    else if (GSerializedModulesFields[i].FieldType == typeof(float))
                    {
                        GModule module = Modules.Find(m => m.GetType() == GSerializedModulesFields[i].DeclaringType);

                        GSerializedModulesFields[i].SetValue(module, EditorGUILayout.FloatField(
                            label: GUtility.HandleFieldName(GSerializedModulesFields[i].Name, exclude: '_'),
                            value: Convert.ToSingle(GSerializedModulesFields[i].GetValue(module))));
                    }

                    // else if (GSerializedModulesFields[i].FieldType == typeof(string))
                    // {
                    // ...
                    // }
                    //
                    // else if ....
                }

                serializedObject.ApplyModifiedProperties();
            }
        }
#endif
        [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = true)]
        internal sealed class SerializeFieldAttribute : Attribute
        {
            public SerializeFieldAttribute() { }

            public static void InitializeFields(Type targetObject)
            {
                IEnumerable<FieldInfo> fields = GUtility.GetFieldsWithAttribute<SerializeFieldAttribute>(targetObject);
                foreach (FieldInfo field in fields)
                {
                    GSerializedModulesFields.Add(field);
                }
            }
        }
    }
}

添加了GAssembly.SerializeField属性的字段能正常显示和设置,但进入游戏模式后值全部丢失。推测是游戏启动时Unity会序列化字段,而我的字段未被序列化保存。尝试过给模块类添加Serializable属性、给字段加UnityEngine.SerializeField属性,但均无效。请问需要自行实现序列化系统,还是可以利用Unity自带的序列化系统解决该问题?


解决方案

不用自己实现序列化系统,完全可以靠Unity自带的序列化机制解决,核心问题出在静态字段不被Unity序列化,你的自定义Inspector只是在编辑时修改了内存中的值,没有把数据持久化到GAssembly的序列化数据里。

具体修改步骤:

  1. 将静态模块实例改为可序列化的非静态字段
    把原来的静态属性改成带[SerializeField]的非私有字段(或私有字段加[SerializeField]),同时保留静态属性供外部访问,还要给每个模块类加上[Serializable]特性:

    [SerializeField] private LabModule _labModule = new LabModule();
    [SerializeField] private EvalModule _evalModule = new EvalModule();
    [SerializeField] private AdsModule _adsModule = new AdsModule();
    
    // 保留静态访问入口
    public static LabModule LabModule => Instance._labModule;
    public static EvalModule EvalModule => Instance._evalModule;
    public static AdsModule AdsModule => Instance._adsModule;
    
    private static GAssembly Instance { get; set; }
    
    private void Awake()
    {
        Instance = this;
        DontDestroyOnLoad(this);
    }
    

    模块类示例:

    [Serializable]
    public class LabModule : GModule
    {
        // 用Unity自带的[SerializeField]标记需要序列化的字段
        [SerializeField] private float _testFloat;
        [SerializeField] private GameObject _testPrefab;
        // ...其他业务逻辑
    }
    
  2. 简化自定义Inspector逻辑
    现在模块已经是Unity序列化的字段,直接用EditorGUILayout.PropertyField就能自动渲染子字段,还能自动处理序列化持久化:

    #if UNITY_EDITOR
    [CustomEditor(typeof(GAssembly))]
    internal sealed class GAssemblyInspector : Editor
    {
        public override void OnInspectorGUI()
        {
            serializedObject.Update();
    
            // 获取模块的序列化属性
            var labModuleProp = serializedObject.FindProperty("_labModule");
            var evalModuleProp = serializedObject.FindProperty("_evalModule");
            var adsModuleProp = serializedObject.FindProperty("_adsModule");
    
            // 分模块显示
            DrawModuleSection("Lab Module", labModuleProp);
            DrawModuleSection("Eval Module", evalModuleProp);
            DrawModuleSection("Ads Module", adsModuleProp);
    
            serializedObject.ApplyModifiedProperties();
        }
    
        private void DrawModuleSection(string title, SerializedProperty moduleProp)
        {
            GUILayout.Space(10f);
            GUILayout.Label(title, EditorStyles.boldLabel);
            GUILayout.Space(5f);
            EditorGUI.indentLevel++;
            // true表示自动展开所有子字段
            EditorGUILayout.PropertyField(moduleProp, true);
            EditorGUI.indentLevel--;
        }
    }
    #endif
    
  3. 移除自定义的序列化逻辑
    删掉自定义的SerializeFieldAttribute和GSerializedModulesFields相关的反射代码,直接用Unity自带的[SerializeField]标记需要序列化的字段即可。

这样修改后,Inspector中设置的值会被Unity自动序列化保存,进入游戏模式后不会重置,同时模块不需要继承MonoBehaviour,完全符合你的需求。


内容的提问来源于stack exchange,提问作者Kyzlyk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 07:55:17