版本:Unity 6 (6000.0)
语言:English
脚本序列化
自定义序列化

序列化规则

Unity 中的序列化器专门设计为在运行时高效地运行。因此,Unity 中的序列化行为与其他编程环境中的序列化有所不同。Unity 中的序列化器直接在 C# 类的字段上工作,而不是它们的属性,因此 Unity 仅在满足特定条件时才序列化你的字段。以下部分概述了如何在 Unity 中使用字段序列化。

要使用字段序列化,必须确保字段

  • public,或具有 SerializeField 属性
    • 注意:在某些情况下,private 字段会被序列化,请参考 热重载
  • 不是 static
  • 不是 const
  • 不是 readonly
  • 具有可以序列化的字段类型
    • 原始数据类型(int、float、double、bool、string 等)
    • 枚举类型(32 位或更小)
    • 固定大小的缓冲区
    • Unity 内置类型,例如 Vector2、Vector3、Rect、Matrix4x4、Color、AnimationCurve
    • 具有 Serializable 属性的自定义结构体
    • 对从 UnityEngine.Object 派生的对象的引用
    • 具有 Serializable 属性的自定义类。(请参考 自定义类的序列化)。
    • 上述字段类型的数组
    • 上述字段类型的 List<T>

注意:Unity 不支持对多级类型(多维数组、交错数组、字典和嵌套容器类型)进行序列化。如果你想序列化这些类型,你有两种选择

自定义类的序列化

为了让 Unity 序列化自定义类,必须确保类

当你将 UnityEngine.Object 派生类的实例分配给一个字段,而 Unity 保存该字段时,Unity 将该字段序列化为对该实例的引用。Unity 会独立地序列化该实例本身,因此在将多个字段分配给该实例时,不会对其进行复制。但是对于不从 UnityEngine.Object 派生的自定义类,Unity 会将该实例的状态直接包含在引用它们的 MonoBehaviour 或 ScriptableObject 的序列化数据中。这可以通过两种方式实现:内联和通过[SerializeReference]

  • 内联序列化:默认情况下,当你未在引用该类的字段上指定 [SerializeReference] 时,Unity 会按值内联序列化自定义类。这意味着,如果你将对自定义类实例的引用存储在多个不同的字段中,它们在序列化时会变成单独的对象。然后,当 Unity 反序列化这些字段时,它们会包含具有相同数据的不同的独立对象。
  • [SerializeReference] 序列化:如果你确实指定了 [SerializeReference],Unity 会将该对象建立为托管引用。宿主对象仍然将对象直接存储在其序列化数据中,但存储在专门的注册表部分。

[SerializeReference] 会增加一些开销,但支持以下情况

  • 字段可以为空。内联序列化不能表示 null,而是用具有未分配字段的内联对象替换 null。
  • 对同一对象的多个引用。如果你将对自定义类实例的引用存储在多个不同的字段中,而不使用 [SerializeReference],那么它们在序列化时会变成单独的对象。
  • 图和循环数据(例如,一个对象引用自身)。内联类序列化不支持 null 或共享引用,因此数据中的任何循环都可能导致意外结果,例如奇怪的 Inspector一个 Unity 窗口,显示有关当前选定 GameObject、资源或项目设置的信息,允许你检查和编辑这些值。 更多信息
    查看 术语表
    行为、控制台错误或无限循环。
  • 多态性。如果你创建一个从父类派生的类,并将其分配给使用父类作为类型的字段,而不使用 [SerializeReference],Unity 仅序列化属于父类的字段。当 Unity 反序列化类实例时,它会实例化父类而不是派生类。
  • 当数据结构需要一个稳定的标识符来指向特定对象,而不对对象的数组位置进行硬编码或搜索整个数组时。请参考 Serialization.ManagedReferenceUtility.SetManagedReferenceIdForObject

注意:内联序列化效率更高,除非你专门需要 [SerializeReference] 支持的某个功能,否则应该使用内联序列化。有关如何使用 [SerializeReference] 的完整详细信息,请参考 SerializeReference 文档。

属性的序列化

Unity 通常不序列化属性,除非在以下情况下

  • 如果属性具有显式备份字段,Unity 会根据常规序列化规则对其进行序列化。例如
public int MyInt
{
get => m_backing;
private set => m_backing = value;
}
[SerializeField] private int m_backing;
  • Unity 在热重载期间仅序列化具有自动生成字段的属性。
public int MyInt { get; set; }

如果你不希望 Unity 序列化具有自动生成字段的属性,请使用 [field: NonSerialized] 属性。

其他资源

脚本序列化
自定义序列化