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

能否修改Unity MonoBehaviour默认字段序列化规则?自定义属性控制序列化

修改Unity MonoBehaviour默认序列化规则的实现方案

可以实现你的需求,但Unity的默认序列化规则无法直接修改,你可以通过以下两种方案达成目标:

方案1:手动接管序列化逻辑(运行时+编辑器兼容)

通过实现ISerializationCallbackReceiver接口,完全自定义序列化逻辑,只处理带有你标记的自定义属性的字段:

步骤1:定义自定义序列化标记属性

using System;

[AttributeUsage(AttributeTargets.Field)]
public class CustomSerializeAttribute : Attribute { }

步骤2:实现自定义序列化的基类

让你的MonoBehaviour继承这个基类,或者直接在目标类中实现接口逻辑:

using UnityEngine;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;

public class CustomSerializedBehaviour : MonoBehaviour, ISerializationCallbackReceiver
{
    // Unity会自动序列化这个列表,用来存储我们需要保留的字段数据
    [SerializeField]
    private List<SerializedFieldData> _serializedData = new List<SerializedFieldData>();

    public void OnBeforeSerialize()
    {
        _serializedData.Clear();
        // 获取当前类的所有实例字段(包括私有、公有)
        var fields = GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

        foreach (var field in fields)
        {
            // 只处理带有CustomSerialize属性的字段
            if (field.GetCustomAttribute<CustomSerializeAttribute>() != null)
            {
                var value = field.GetValue(this);
                _serializedData.Add(new SerializedFieldData
                {
                    FieldName = field.Name,
                    FieldValue = value
                });
            }
        }
    }

    public void OnAfterDeserialize()
    {
        var fields = GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

        foreach (var data in _serializedData)
        {
            var targetField = fields.FirstOrDefault(f => f.Name == data.FieldName);
            if (targetField != null && targetField.GetCustomAttribute<CustomSerializeAttribute>() != null)
            {
                targetField.SetValue(this, data.FieldValue);
            }
        }
    }

    // 辅助序列化类,必须标记[Serializable]才能被Unity处理
    [System.Serializable]
    private class SerializedFieldData
    {
        public string FieldName;
        public object FieldValue;
        // 注意:如果要序列化自定义复杂类,需要给该类标记[Serializable],或者将数据转为Json/字节数组存储
    }
}

注意:这种方式下,Unity的默认序列化会被你接管,Inspector默认不会显示自定义序列化的字段。如果需要在编辑器中编辑这些字段,需要额外编写对应的Editor脚本,通过SerializedObject读取_serializedData并绘制UI。

方案2:编辑器自动添加[NonSerialized](仅编辑器阶段处理)

如果你想保留Unity默认序列化逻辑,只是自动给未标记自定义属性的字段添加[NonSerialized],可以用编辑器后处理器实现:

步骤1:同样先定义自定义属性

(和方案1的CustomSerializeAttribute完全一致)

步骤2:编写编辑器后处理脚本

using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;
using System.Text.RegularExpressions;

public class SerializationAutoProcessor
{
    [PostProcessScene(0)]
    public static void AutoAddNonSerialized()
    {
        // 遍历Assets目录下所有C#脚本,可根据需求限制目录范围
        var scriptPaths = Directory.GetFiles(Application.dataPath, "*.cs", SearchOption.AllDirectories);

        foreach (var path in scriptPaths)
        {
            var content = File.ReadAllText(path);
            // 正则匹配未标记[NonSerialized]或[CustomSerialize]的字段,需根据你的代码风格调整正则
            var pattern = @"(?<!\[NonSerialized\]\s*)(?<!\[CustomSerialize\]\s*)\s*(public|private|protected|internal)\s+\w+\s+\w+;";
            
            content = Regex.Replace(content, pattern, match =>
            {
                return $"[NonSerialized]\n{match.Value}";
            }, RegexOptions.Multiline);

            File.WriteAllText(path, content);
        }

        AssetDatabase.Refresh();
    }
}

注意:这种方式是直接修改脚本文件,建议先备份代码,并且添加额外的判断逻辑(比如只处理继承自MonoBehaviour的类),避免误改其他脚本。每次编译后都会执行,可能覆盖手动修改的内容,需谨慎使用。

额外说明

  • ISerializationCallbackReceiver接口只能让你在序列化前后执行自定义逻辑,无法直接修改Unity的默认序列化规则,所以方案1是通过接管逻辑实现需求。
  • 如果选择方案1,对于复杂类型(比如自定义类、集合),需要确保类型本身是Unity可序列化的(标记[Serializable]),或者自己实现序列化逻辑(比如转为Json字符串存储)。

内容的提问来源于stack exchange,提问作者Иван Соловьев

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 15:00:35