Version: 5.6
スクリプトの制限
ビルトインのシリアル化

スクリプトのシリアル化

Serialization is the automatic process of transforming data structures or object states into a format that Unity can store and reconstruct later. Some of Unity’s built-in features use serialization; features such as saving and loading, the Inspector window, instantiation, and Prefabs. See documntation on Built-in serialization use for background details on all of these.

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

シリアル化エラーカスタム製のシリアル化ビルトインシリアル化 に関するドキュメントも参照してください。

ホットリロードを理解する

ホットリロード

ホットリロードは、エディターが開いている間にスクリプトを作成または編集し、スクリプトの動作を即座に適用する処理です。 変更を有効にするために、ゲームやエディターを再起動する必要はありません。

スクリプトを変更して保存すると、Unity は現在ロードされているすべてのスクリプトデータをホットリロードします。まず、ロードしたすべてのスクリプトにシリアライズ可能な変数すべてを格納し、スクリプトをロードした後にそれらを復元します。 シリアライズ可能でないすべてのデータは、スクリプトのリロード後に失われます。

保存とロード

Unity uses serialization to load and save Scenes, Assets, and AssetBundles to and from your computer’s hard drive. This includes data saved in your own scripting API objects such as MonoBehaviour components and ScriptableObjects.

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

インスペクターウィンドウ

インスペクターウィンドウ でゲームオブジェクトのコンポーネントのフィールドの値を表示/変更するときに、Unity はこのデータをシリアライズしてからインスペクターウィンドウに表示します。フィールドの値を表示しているときは、インスペクターウィンドウは Unity のスクリプティング API と通信しません。

スクリプトのプロパティーを利用する場合、インスペクターウィンドウの値の表示や変更を行っても、プロパティーのゲッターやセッターはまったく呼び出されません。なぜなら、Unity は直接インスペクターウィンドウのフィールドをシリアライズするからです。つまり、インスペクターウィンドウのフィールドの値はスクリプトのプロパティーを表示しますが、インスペクターウィンドウの値を変更しても、スクリプト内でプロパティーのゲッターとセッターは呼び出されません。

シリアル化のルール

Unity のシリアライザーはリアルタイムのゲーム環境で動作します。 これはパフォーマンスに大きな影響を与えます。例えば、Unity のシリアル化は、他のプログラミング環境のシリアル化とは異なる挙動をします。 以下に、Unity でのシリアル化の使い方に関するヒントをいくつか紹介します。

スクリプトでフィールドがシリアライズされているのを確認する方法

以下を確認します。

  • フィールドが public または SerializeField 属性をもっていること。

  • フィールドが static でないこと。

  • フィールドが const でないこと。

  • フィールドが readonly でないこと。

  • Has a fieldtype that can be serialized
    (See Simple field types that can be serialized, below.)

シリアライズ可能な簡易なフィールドタイプ

  • Serializable 属性をもつカスタムの非抽象クラスと非ジェネリッククラス
    (後述の カスタムクラスがシリアライズ可能なことを確かめる方法 参照)

  • Serializable 属性をもつカスタム構造体

  • UnityEngine.Object から派生するオブジェクトへの参照

  • プリミティブなデータ型 (intfloatdoubleboolstring など)

  • Enum 型

  • 特定の Unity ビルトイン型: Vector2Vector3Vector4RectQuaternionMatrix4x4ColorColor32LayerMaskAnimationCurveGradientRectOffsetGUIStyle

シリアライズ可能なコンテナーフィールドタイプ

  • シリアライズ可能な簡易なフィールドタイプの配列

  • シリアライズ可能な簡易なフィールドタイプの List<T>

注意: Unity は、マルチレベルタイプ (多次元配列、ジャグ配列、ネストされたコンテナータイプ) のシリアライズをサポートしていません。
これらをシリアライズするには 2 つのオプションがあります。クラスまたは構造体でネストされた型をラップするか、シリアル化コールバック ISerializationCallbackReceiver を使用してカスタム製のシリアル化を実行します。 詳細については、 カスタムシリアル化 に関するドキュメントを参照してください。

カスタムクラスがシリアライズ可能なことを確かめる方法

以下を確認します。

  • Serializable 属性をもっていること。

  • フィールドが抽象でないこと。

  • フィールドが静的でないこと。

  • ジェネリッククラスから継承したとしてもフィールドがジェネリックでないこと。

カスタムのクラスか構造体のフィールドがシリアライズされていることを確認するには、前述の スクリプトでフィールドがシリアライズされているのを確認する方法 を参照してください。

シリアライザーが予期しない動作を行う場合

カスタムのクラスが構造体のように動作する

UnityEngine.Object から派生していないカスタムクラスでは、Unity は構造体をシリアライズするのと同じように、値によってインラインでシリアライズします。カスタムクラスのインスタンスへの参照をいくつかの異なるフィールドに格納すると、それらはシリアライズされるときに別々のオブジェクトになります。その後、Unity がそれらのフィールドをデシリアライズすると、フィールドには同じデータを持つ別個のオブジェクトが含まれます。

参照を使用して複雑なオブジェクトグラフをシリアライズする必要がある場合は、Unity がオブジェクトを自動的にシリアライズしないようにしてください。代わりに、 ISerializationCallbackReceiver を使用して手動でシリアライズします。これにより、Unity がオブジェクト参照から複数のオブジェクトを作成するのを防ぎます。詳細については、ISerializationCallbackReceiver に関するドキュメントを参照してください。

これは、カスタムクラスの場合にのみ当てはまります。Unity は、カスタムクラスを「インライン」でシリアライズします。なぜなら、それらのデータは、使用される MonoBehaviour または ScriptableObject の完全なシリアル化データの一部となるためです。フィールドが public Camera myCamera などの UnityEngine.Object の派生のクラスを参照するとき、Unity はカメラ UnityEngine.Object への実際の参照をシリアライズします。 MonoBehaviour または ScriptableObject (両方ともUnityEngine.Object から派生) から派生したスクリプトのインスタンスでも同じことが起こります。

カスタムクラスの null はサポートされません

以下のスクリプトを使用する MonoBehaviour をデシリアライズするとき、いくつのアロケーションが発生するか考えてみてください。


class Test : MonoBehaviour

{

    public Trouble t;

}

[Serializable]

class Trouble

{

   public Trouble t1;

   public Trouble t2;

   public Trouble t3;

}

1 回アロケーションが発生するのは普通です。Test オブジェクトで発生します。2 回アロケーションが発生するのも異常ではありません。Test オブジェクトと Trouble オブジェクトで発生します。

しかし、Unity は実際には 1000 以上のアロケーションを行います。 シリアライザーは null をサポートしていません。 オブジェクトをシリアライズし、フィールドが null の場合、Unity はその型の新しいオブジェクトをインスタンス化し、それをシリアライズします。 明らかに、これは無限のサイクルにつながる可能性があるため、7 レベルの深度制限があります。 この時点に達すると、Unity は、カスタムクラス、構造体、リスト、配列の型を持つフィールドのシリアライズを停止します。

Unity のサブシステムの多くはシリアル化のシステム上で構築されるため、Test MonoBehaviour の予想外に大きなシリアル化のストリームは、これらすべてのサブシステムのパフォーマンスを必要以上に低下させます。

ポリモーフィズムはサポートされません

public Animal[] animalsDogCatGiraffe のインスタンスを加えると、シリアル化後に 3 つの Animal のインスタンスができます。

この制限に対処する 1 つの方法は、インラインでシリアル化されるカスタムクラスにのみ適用されることに注意することです。他の UnityEngine.Objects への参照は実際の参照としてシリアル化され、それらのためにポリモーフィズムが実際に機能します。 ScriptableObject 派生クラス、または別の MonoBehaviour 派生クラスを作成し、それを参照します。 これの欠点は、Monobehaviour またはスクリプタブルオブジェクトをどこかに格納する必要があり、インラインで効率的にシリアライズできないことです。

これらの制限の理由は、シリアル化システムの中核基盤の 1 つとして、オブジェクトのデータストリームのレイアウトが事前にわかっていることです。 つまり、フィールド内に格納されるものではなく、クラスのフィールドの型に依存します。

ヒント

シリアル化の最適化

Unity のシリアル化を最適な方法で使用するために、データを整理します。

  • Unity に最小限のデータセットをシリアライズさせるためにデータを整理します。 これの主な目的は、コンピューターのハードドライブ上の領域を節約するためではなく、以前のバージョンのプロジェクトとの下位互換性を確実に維持するためです。 シリアライズされたデータの大きなセットを扱う場合、下位互換性は開発の後になるほど難しくなります。

  • Unity が複製されたデータやキャッシュされたデータをシリアライズしないようにデータを整理します。 これは、下位互換性にとって重大な問題が起きる原因になります。データが同期されにくくなるため、エラーが発生する可能性が高くなります。

  • 他のクラスを参照するネストされた再帰的な構造を避けるようにします。シリアライズされた構造のレイアウトは常に同じである必要があります。 つまり、データとは独立し、スクリプト内に公開されているもののみに依存します。他のオブジェクトを参照する唯一の方法は、UnityEngine.Object から派生したクラスを使用することです。これらのクラスは完全に分かれていて、互いを参照するだけで、コンテンツを埋め込むことはありません。

エディターコードをホットリロード可能にする

スクリプトをリロードすると、Unity はロードしたすべてのスクリプトのすべての変数をシリアライズして保存します。 スクリプトをリロードした後、Unity はそれらをシリアル化前の元の値に復元します。

スクリプトをリロードすると、Unity は変数に SerializeField 属性がなくても、シリアル化の要件を満たすすべての変数 (プライベート変数を含む) を復元します。特に、プライベート変数がリストアされることを避けることが必要な場合もあります。例えば、スクリプトからリロードした後に参照を null にする場合などです。 この場合、 NonSerializable 属性を使用します。

Unity は決して静的変数を復元しないので、スクリプトをリロードした後に保持する必要のある状態には静的変数を使用しないでください。


スクリプトの制限
ビルトインのシリアル化