Version: 2022.3
言語: 日本語
起動時エディタースクリプト実行
スクリプトのコンパイル

スクリプトのシリアル化

シリアル化 (シリアライゼーション) は、データ構造やゲームオブジェクトの状態を Unity が保存して後で再構築できる形式に変換する自動処理です。

Unity プロジェクトのデータを整理する方法は、Unity がそのデータをシリアライズする方法に影響し、プロジェクトのパフォーマンスに重大なインパクトを与える可能性があります。ここでは、Unity のシリアル化のための概要と、プロジェクトの最適化方法について説明します。

このセクションでは、以下のトピックについて説明します。

シリアライゼーションの規則

Unity のシリアライザーは、特にランタイムに効率的に動作するように設計されています。このため、Unity でのシリアル化は他のプログラミング環境でのシリアル化とは挙動が異なります。Unity のシリアライザーは、C# のクラスのプロパティではなく、フィールド に直接作用します。そのため、フィールドがシリアル化されるために準拠しなければならない規則があります。次のセクションでは、Unity でフィールドシリアライゼーションを使用するための概要を説明します。

フィールドシリアライゼーションを使用するには、フィールドが以下の状態であることを確認します。

  • public であるか、または SerializeField 属性をもっていること。
  • 静的でないこと。
  • const ではないこと。
  • 読み取り専用でないこと。
  • シリアラル化可能なフィールド型であること。
    • プリミティブなデータ型 (int、float、double、bool、string など) であること。
    • Enum 型 (32 バイト以下) であること。
    • 固定サイズバッファであること。
    • Unity の組み込み型 (例えば、Vector2、Vector3、Rect、Matrix4x4、Color、AnimationCurve) であること。
    • Serializable 属性をもつカスタム構造体であること。
    • UnityEngine.Object から派生するオブジェクトへの参照であること。
    • Serializable 属性を持つカスタムクラスであること。(カスタムクラスのシリアライズ を参照)。
    • 上記のフィールド型の配列であること。
    • 上記のフィールド型の List<T> であること。

ノート: Unity は多階層型 (多次元配列、ジャグ配列、辞書、ネストしたコンテナ型) のシリアライゼーションをサポートしていません。これらをシリアライズする場合、2 つのオプションがあります。

カスタムクラスのシリアライゼーション

Unity がカスタムクラスをシリアライズするためには、クラスが以下の状態であることが必要です。

  • Serializable 属性をもっていること。
  • 静的ではないこと。

UnityEngine.Object から派生したクラスのインスタンスをフィールドに割り当てそのフィールドを保存する場合、Unity はフィールドをシリアライズしてそのインスタンスへの参照にします。Unity はそのインスタンス自体を個々にシリアライズします。そのため、インスタンスに複数のフィールドが割り当てられても重複しません。しかし、UnityEngine.Object から派生しないカスタムクラスの場合、Unity はインスタンスの状態を、それらを参照する MonoBehaviour や ScriptableObject のシリアル化されたデータに直接加えます。inlineSerializeReference の 2 つの方法があります。

  • インラインシリアライゼーション: デフォルトでは、クラスを参照するフィールドに SerializeReference を指定しない場合、Unity はカスタムクラスを値によってインラインでシリアライズします。つまり、カスタムクラスのインスタンスへの参照を複数の異なるフィールドに保存する場合、これらはシリアライズされると別々のオブジェクトになります。それらはシリアライズされると別々のオブジェクトになります。そして、Unity がフィールドをデシリアライズすると、それらは同一のデータを持つ異なる別個のオブジェクトを含みます。
  • SerializeReference シリアライゼーション: SerializeReference を指定する場合、Unity はオブジェクトをマネージ参照として確立します。ホストオブジェクトは、オブジェクトをそのシリアル化されたデータに直接保存しますが、専用のレジストリセクションに格納します。

SerializeReference は若干のオーバーヘッドを加えますが、以下のケースをサポートします。

  • フィールドは NULL でもよい。インラインシリアライゼーションでは、NULL を表現することはできません。代わりに、NULL を未割り当てのフィールドを持つインラインオブジェクトに置き換えます。
  • 同じオブジェクトへの複数の参照。SerializeReference を使用せずに、カスタムクラスのインスタンスへの参照を複数の異なるフィールドに格納すると、シリアライズ時にそれらは別々のオブジェクトになります。
  • グラフや循環データ (例えば、それ自体に戻る参照を持つオブジェクトなど)。インラインクラスのシリアライゼーションは、NULL や参照の共有をサポートしません。そのため、データの循環は、インスペクターの動作の異常、コンソールエラー、無限ループなど、予期せぬ結果につながる可能性があります。
  • ポリモーフィズム。親クラスから派生したクラスを作成し、親のクラスを型として使用するフィールドに割り当てる場合、SerializeReference なしに、Unity は親クラスに属するフィールドのみをシリアライズします。Unity がクラスインスタンスをデシリアライズするとき、派生クラスではなく、親クラスをインスタンス化します。
  • データ構造が、オブジェクトの配列位置をハードコードしたり配列全体を検索したりせずに、特定のオブジェクトを指し示す安定した識別子を必要とする場合。SerializationUtility.SetManagedReferenceIdForObject を参照してください。

ノート: インラインシリアライゼーションはより効率的なので、SerializeReference がサポートする機能の 1 つを特に必要としない限り、インラインシリアライゼーションを使用すべきです。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] 属性を使用します。

カスタムシリアライゼーション

Unity のシリアライザーがサポートしないもの (例えば、C# の Dictionary) をシリアル化したい場合があるかもしれません。最良の方法は、ISerializationCallbackReceiver インターフェースをクラスで実装することです。これにより、シリアライゼーションとデシリアライゼーション中の重要なポイントで呼び出されるコールバックを実装することができます。

  1. オブジェクトがシリアル化されようとするとき、Unity は OnBeforeSerialize() コールバックを呼び出します。このコールバックの内部で、データを Unity が理解できるものに変換することができます。例えば、C# の Dictionary をシリアライズする場合、データを Dictionary からキーの配列と値の配列にコピーします。
  2. OnBeforeSerialize() コールバックが完了した後、Unity は配列をシリアライズします。
  3. その後、オブジェクトがデシリアライズされると、Unity は OnAfterDeserialize() コールバックを呼び出します。このコールバックの内部で、データをメモリのオブジェクトに都合の良い形に変換して戻すことができます。例えば、キーと値の配列を使用して、C# Dictionary を再入力します。

Unity のシリアライゼーションの利用法

保存とロード

Unity はシリアル化を使って シーンアセットアセットバンドル をデバイスのメモリに (または、メモリから) ロードして保存します。これには、独自のスクリプティング API オブジェクトに格納される MonoBehaviour コンポーネントや ScriptableObject などのデータも含まれます。

Unity エディターの機能の多くは、基軸となるシリアル化システム上に構築されています。シリアル化で特に気を付けるべき 2 つの点は