Serialization is the automatic process of transforming data structures or GameObject states into a format that Unity can store and reconstruct later.
How you organize data in your Unity project affects how Unity serializes that data, which can have a significant impact on the performance of your project. This page outlines serialization in Unity and how to optimize your project for it.
This document covers the following topics:
Serializers in Unity are specifically designed to operate efficiently at runtime. Because of this, serialization in Unity behaves differently to serialization in other programming environments. Serializers in Unity work directly on the fields of your C# classes rather than their properties, so there are rules that your fields must conform to to be serialized. The following section outlines how to use field serialization in Unity.
To use field serialization you must ensure that the field:
List<T>
of a field type mentioned aboveNote: Unity doesn’t support serialization of multilevel types (multidimensional arrays, jagged arrays, dictionaries, and nested container types). If you want to serialize these, you have two options:
For Unity to serialize a custom class, you must ensure the class:
When you assign an instance of a UnityEngine.Object
-derived class to a field and Unity saves that field, Unity serializes the field as a reference to that instance. Unity serializes the instance itself independently, so it isn’t duplicated when multiple fields are assigned to the instance. But for custom classes which don’t derive from UnityEngine.Object
, Unity includes the state of the instance directly in the serialized data of the MonoBehaviour or ScriptableObject that references them. There are two ways that this can happen: inline and by [SerializeReference]
.
[SerializeReference]
on the field that references the class. This means that if you store a reference to an instance of a custom class in several different fields, they become separate objects when serialized. Then, when Unity deserializes the fields, they contain different distinct objects with identical data.[SerializeReference]
serialization: If you do specify [SerializeReference]
, Unity establishes the object as a managed reference. The host object still stores the objects directly in its serialized data, but in a dedicated registry section.[SerializeReference]
adds some overhead but supports the following cases:
[SerializeReference]
, then they become separate objects when serialized.[SerializeReference]
Unity only serializes the fields that belong to the parent class. When Unity deserializes the class instance, it instantiates the parent class instead of the derived class.Note: Inline serialization is more efficient, and you should use it unless you specifically need one of the features that [SerializeReference]
supports. For full details on how to use [SerializeReference]
, see the SerializeReference documentation.
Unity doesn’t normally serialize properties except in the following situations:
public int MyInt
{
get => m_backing;
private set => m_backing = value;
}
[SerializeField] private int m_backing;
public int MyInt { get; set; }
Sometimes you might want to serialize something that Unity’s serializer doesn’t support (for example, a C# Dictionary). The best approach is to implement the ISerializationCallbackReceiver interface in your class. This allows you to implement callbacks that are invoked at key points during serialization and deserialization:
OnBeforeSerialize()
callback. Inside this callback is where you can transform your data into something Unity understands. For example, to serialize a C# Dictionary, copy the data from the Dictionary into an array of keys and an array of values.OnBeforeSerialize()
callback is complete, Unity serializes the arrays.OnAfterDeserialize()
callback. Inside this callback is where you can transform the data back into a form that’s convenient for the object in memory. For example, use the key and value arrays to repopulate the C# Dictionary.Unity uses serialization to load and save scenes, Assets, and AssetBundles to and from your device’s memory. This includes data saved in your own scripting API objects such as MonoBehaviour components and ScriptableObjects.
Many of the features in the Unity Editor are built on top of the core serialization system. Two things to be particularly aware of with serialization are the Inspector window, and hot reloading.
The Inspector window shows the value of the serialized fields of the inspected objects. When you change a value in the Inspector, the Inspector updates the serialized data and triggers a deserialization that updates the inspected object.
The same applies for both built-in Unity objects, and scripting objects such as MonoBehaviour-derived classes.
Unity doesn’t call any C# property getters and setters when you view or change values in the Inspector window; instead, Unity accesses the serialized backing field directly.
Hot reloading is where you create or edit scripts while the Editor is open and apply the script behaviors immediately. You don’t have to restart the Editor for changes to take effect.
When you change and save a script, Unity hot reloads all the script data that’s loaded at the time. Unity stores all serializable variables in all loaded scripts, then reloads those scripts and restores the serialized variables. Hot reloading discards all data that isn’t serializable, so you won’t be able to access the data afterward.
This affects all Editor windows and all MonoBehaviours in the project. Unlike other cases of serialization, Unity serializes private fields by default when reloading, even if they don’t have the ‘SerializeField’ attribute.
When Unity reloads scripts:
[SerializeField]
attribute. Sometimes, you need to prevent Unity from restoring private variables, for example, if you want a reference to be null after reloading from scripts. In this case, use the [field: NonSerialized]
attribute.A Prefab is the serialized data of one or more GameObjects or components. A Prefab instance contains a reference to both the Prefab source and a list of modifications to it. The modifications are what Unity needs to do to the Prefab source to create that particular Prefab instance.
The Prefab instance only exists while you edit your project in the Unity Editor. The Unity Editor instantiates a GameObject from its two sets of serialization data: the Prefab source and the Prefab instance’s modifications.
When you call Instantiate
on anything that exists in a scene, such as a Prefab or a GameObject:
UnityEngine.Object
.UnityEngine.Objects
it references. It checks all referenced UnityEngine.Objects
to see if they’re part of the data Unity instantiates. If the reference points to something external, such as a Texture, Unity keeps that reference as it is. If the reference points to something internal, such as a child GameObject, Unity patches the reference to the corresponding copy.EditorUtility.UnloadUnusedAssetsImmediate
is the native Unity garbage collector and has a different purpose to the standard C# garbage collector. It runs after you load a scene and checks for objects (like Textures) that it no longer references and unloads them safely. The native Unity garbage collector runs the serializer in a variation in which objects report all references to external UnityEngine.Objects
. This is how Textures that one scene uses, the garbage collector unloads in the next.
Most serialization happens in the Editor, whereas deserialization is the focus at runtime. Unity serializes some features only in the Editor, while it can serialize other features in both the Editor and at runtime:
功能 | Editor | Runtime |
---|---|---|
Assets in Binary Format | Read/write supported | Read supported |
Assets in YAML format | Read/write supported | 不支持 |
Saving scenes, prefabs and other assets | Supported, unless in Play mode | 不支持 |
Serialization of individual objects with JsonUtility | Read/write support with JsonUtility. Support for additional types of objects with EditorJsonUtility |
Read/write support with JsonUtility |
SerializeReference | 受支持 | 受支持 |
ISerializationCallbackReceiver | 受支持 | 受支持 |
FormerlySerializedAs | 受支持 | 不支持 |
Objects can have additional fields that only the Editor serializes, such as when you declare fields within the UNITY_EDITOR scripting symbol:
public class SerializeRules : MonoBehaviour
{
#if UNITY_EDITOR
public int m_intEditorOnly;
#endif
}
In the above example, the m_intEditorOnly
field is only serialized in the editor and isn’t included in the build. This allows you to save memory by omitting data that’s only required in the Editor from your build. Any code that uses that field would also need to be conditionally compiled, for example within #if UNITY_EDITOR blocks, so that the class can compile at build time.
The Editor doesn’t support objects with fields that Unity only serializes at runtime, (for example, when you declare fields within the UNITY_STANDALONE directive).
Script serialization can cause errors. Fixes to some of these are listed below.
Calling Scripting API such as GameObject.Find
inside a MonoBehaviour constructor or field initializer triggers this error.
To fix this, make the call to the Scripting API in MonoBehaviour.Start
instead of in the constructor.
Calling Scripting API such as GameObject.Find from within the constructor of a class marked with System.Serializable
triggers this error.
要解决此问题,请编辑代码,确保不会在任何序列化对象的构造函数中调用任何脚本 API。
The restrictions above affect the majority of the Scripting API. Only some parts of the Unity scripting API are exempt and you can call them from anywhere:
Debug.Log
Mathf
functionsVector3
and Quaternion
To reduce the risk of errors during serialization, only call API methods that are self-contained and don’t need to get or set data in Unity itself, unless there is no alternative.
You can organize your data to ensure you get optimal use of Unity’s serialization.
UnityEngine.Object
. These classes are separate; they only reference each other and they don’t embed the contents.