# Unity 如何使用序列化

> More details about how serialization works in Unity.

## 保存和加载##saving-and-loading

Unity 使用序列化技术从设备内存加载和保存[场景](/engine/6000.0/manual/working-in-unity/working-with-scenes/creating-scenes.md)、[资源](/engine/6000.0/manual/working-in-unity/assets-and-media/asset-workflow.md)和 [AssetBundle](/engine/6000.0/manual/working-in-unity/assets-and-media/asset-workflow/asset-bundles-intro.md)。这包括保存在您自己的脚本 API 对象（如 [MonoBehaviour](/engine/6000.0/script-reference/unity-engine/mono-behaviour.md) 组件和 [ScriptableObjects](/engine/6000.0/manual/scripting/get-started/fundamental-unity-types/class-scriptable-object.md)）中的数据。

Unity 编辑器中的许多功能都建立在核心序列化系统之上。对于序列化要特别注意的两点是 [Inspector 窗口](/engine/6000.0/manual/unity-editor/using-the-editor/using-the-inspector.md)和热重载。

### Inspector 窗口##the-inspector-window

Inspector 窗口显示被检查对象的序列化字段的值。当更改 Inspector 中的值时，Inspector 会更新序列化数据并触发反序列化，以更新检查的对象。

这对于内置的 Unity 对象以及脚本对象（如 MonoBehaviour 派生类）也适用。

在 Inspector 窗口中查看或更改值时，Unity 不会调用任何 C# 属性的 getter 和 setter。相反，Unity 会直接访问序列化的后备字段。

### 热重载##hot-reloading

脚本代码的热重载是在资源数据库刷新过程中执行的。它指的是在编辑器运行时直接重新加载和应用代码更改，而无需重新启动编辑器。有关更多信息，请参阅[刷新资源数据库](/engine/6000.0/manual/working-in-unity/assets-and-media/asset-workflow/asset-database/refreshing.md)和[热重载](/engine/6000.0/manual/working-in-unity/assets-and-media/asset-workflow/asset-database/refreshing.md#hotreloading)。

**注意**：热重载是一种特殊的序列化情况。与 Unity 中的其他序列化情况不同，私有字段在重新加载时会默认序列化，即使它们没有 [SerializeField](/engine/6000.0/script-reference/unity-engine/serialize-field.md) 属性也是如此。

重新加载脚本时：

1. Unity 会在所有加载的脚本中序列化并存储所有变量。

2. Unity 会将它们恢复为序列化前的原始值。

   * Unity 会恢复满足序列化要求的所有变量（**包括私有变量**），即使变量没有 `[SerializeField]` 属性也是如此。在某些情况下，需要防止 Unity 恢复私有变量：例如，如果您希望从脚本重新加载后引用为 null。在这种情况下，请使用 [`[field: NonSerialized]`](ScriptRef:NonSerialized) 属性。
   * Unity 绝不会恢复静态变量，因此对于 Unity 重新加载脚本后需要保留的状态，不要使用静态变量，因为会在重载过程中被丢弃。

## 预制件##prefabs

[预制件](/engine/6000.0/manual/working-in-unity/working-with-gameobjects/prefabs.md)是一个或多个[游戏对象](/engine/6000.0/manual/working-in-unity/working-with-gameobjects/game-objects.md)或[组件](/engine/6000.0/manual/working-in-unity/working-with-gameobjects/unity-components/components.md)的序列化的数据。预制件实例包含对预制件源及其一系列修改的引用。这些修改是 Unity 为了创建特定预制件实例而需要对预制件源进行的操作。

只有在 Unity 编辑器中编辑项目时，预制件实例才存在。Unity 编辑器从两组序列化数据（预制件源和预制件实例的修改）进行游戏对象的实例化。

## 实例化##instantiation

对场景中存在的任何对象（例如预制件或游戏对象）调用 [`Instantiate`](/engine/6000.0/script-reference/unity-engine/object/instantiate.md) 时：

1. Unity 都会对其进行序列化。在运行时和在 Editor 中都是如此。从 `UnityEngine.Object` 派生的一切均可被 Unity 序列化。
2. Unity 创建一个新的游戏对象并将数据反序列化到新的游戏对象上。
3. Unity 在另一个不同的变体中运行相同的序列化代码，以报告其引用的其他 `UnityEngine.Objects`。它会检查所有引用的 `UnityEngine.Objects` 以确定它们是否是 Unity 要实例化的数据的一部分。如果引用指向外部，例如纹理，则 Unity 会保持该引用不变。如果引用指向内部，例如子游戏对象，则 Unity 会修补对相应副本的引用。

## 卸载未使用的资源##unloading-unused-assets

`EditorUtility.UnloadUnusedAssetsImmediate` 是本机 Unity 垃圾回收器，相较于标准 C# 垃圾回收器有不同的作用。该垃圾回收器在您加载场景之后运行，检查其不再引用的对象（如纹理），并安全卸载它们。本机 Unity 垃圾回收器在一个变体中运行序列化程序，而在此变体中，对象会报告对外部 `UnityEngine.Objects` 的所有引用。这就是一个场景中使用的纹理，在下一个场景中，垃圾回收器在卸载它们的方式。

## 编辑器和运行时序列化之间的差异##differences-between-editor-and-runtime-serialization

大多数序列化发生在编辑器中而反序列化则在运行时进行。Unity 仅在编辑器中序列化某些功能，而在编辑器和运行时都可以序列化其他功能：

| **功能**                                                                                                                                  | **Editor**                                         | **运行时**             |
| --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------------------- |
| **二进制格式的资源**                                                                                                                            | 支持读/写                                              | 支持读                 |
| **YAML 格式的资源**                                                                                                                          | 支持读/写                                              | 不支持                 |
| **保存场景、预制件和其他资源**                                                                                                                       | 支持，除非处于播放模式                                        | 不支持                 |
| **使用 [JsonUtility](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/json-serialization.md) 进行单个对象的序列化** | JsonUtility 提供读/写支持。使用 EditorJsonUtility 支持其他类型的对象 | JsonUtility 提供读/写支持 |
| **[SerializeReference](/engine/6000.0/script-reference/unity-engine/serialize-reference.md)**                                           | 受支持                                                | 受支持                 |
| **[ISerializationCallbackReceiver](/engine/6000.0/script-reference/unity-engine/i-serialization-callback-receiver.md)**                 | 受支持                                                | 受支持                 |
| **[FormerlySerializedAs](/engine/6000.0/script-reference/unity-engine-serialization/formerly-serialized-as-attribute.md)**              | 受支持                                                | 不支持                 |

对象可以具有只有编辑器能序列化的其他字段，例如在 UNITY\_EDITOR [脚本符号](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-compilation/conditional-compilation/platform-dependent-compilation.md)中声明字段时：

```lang-C#
public class SerializeRules : MonoBehaviour
{
#if UNITY_EDITOR
public int m_intEditorOnly;
#endif
}
```

在上一个示例中，`m_intEditorOnly` 字段仅在编辑器中序列化，未包含在构建中。这使您能够通过在构建中省略仅在编辑器中需要的数据来节省内存。任何使用该字段的代码也需要进行条件编译，例如在 `#if UNITY_EDITOR` 代码块中，以便该类可以在构建时编译。

编辑器不支持具有 Unity 仅在运行时序列化的字段的对象（例如，在 UNITY\_STANDALONE 指令中声明字段时）。

## 其他资源##additional-resources

* [序列化规则](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/rules.md)
* [JSONSerialization](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/json-serialization.md)
* [序列化错误](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/errors.md)
* [序列化最佳实践](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/best-practices.md)
