能否修改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,提问作者Иван Соловьев
相关产品推荐
相关产品推荐

