Unity を検索

Unity 2021 LTS における SerializeReference の改善点

2022年7月11日 カテゴリ: Engine & platform | 11 分 で読めます
A metallic abstract cube on a black background
A metallic abstract cube on a black background
取り上げているトピック
シェア

Is this article helpful for you?

Thank you for your feedback!

最新の LTS リリースでは、ポリモーフィックなシリアル化により、ユーザーの共同作業環境と API へのアクセスが改善され、さらに欠落している型に対してよりきめ細かい処理ができるようになっています。

SerializeReference 属性を使用すると、フィールドに割り当てられたオブジェクトを値でシリアル化するのではなく、参照としてシリアル化することができます。参照されるオブジェクトは「マネージ参照」と呼ばれます。SerializeReference 属性を持つフィールドは、ポリモーフィズムと NULL 値をサポートする役割を果たします。最近では、Unity 2021 LTS でマネージ参照に Stable ID が導入され、欠落している型のきめ細かい処理と API アクセスの向上が実現されました。このブログでは、これらの変更の詳細と、それがどのように皆さんに直接利益をもたらすかについてお伝えします。

Stable ID

マネージ参照は、そのホストのシリアル化データ内に保存されます。ホストは Unity オブジェクト(MonoBehaviour や ScriptableObject から派生したクラスなど)です。そのために、各マネージ参照オブジェクトにユニークな ID を割り当てます。

SerializeReference 属性を持つフィールドは、参照されるオブジェクトの ID を保存することでシリアル化されます。マネージ参照自体は ManagedReferenceRegistry と呼ばれるリストに保存され、ホストのシリアル化されたデータの中に含まれる。

Unity 2019 および 2020 LTS では、コンテンツの深さ優先探索により、保存時に ID が割り当てられました。この方法の主な欠点は、配列の要素の並び替えのような小さな動作が、影響を受けるファイルに大きな変化をもたらす可能性があることです。そのため、ファイル内の大きな変更はマージ時のコンフリクトを引き起こし、共同作業環境では解決が困難となります。

そこで、Stable ID を導入しました。Stable ID は、一度オブジェクトに固有の ID が割り当てられると、その ID が保存と読み込みのサイクルを連続して回しても保持されることを保証してくれます。つまり、ホスト上でマネージ参照のフィールド割り当てを変更し、再度保存しても、ID は変更されないのです。

Stable ID の価値を説明するために、次のような例を考えてみましょう。

Serialize source code

この例では、Sandwich クラスと Fruit クラスのインスタンスを交互に配置した、マネージ参照オブジェクトの配列を作成します。LunchBox1.asset ファイルを検査することで、配列の中身を見ることができます。

Sample Inspector

最初のエントリーをリストの最後に移動すると、基礎となるアセットファイルが変更されます。以下の差分ツールのスクリーンショットは、配列内のオブジェクトが配列の順序に依存しない ID を持つようになった Unity 2021.3 において、差分がいかにシンプルになったかを示しています。

Unity 2020.3:

2020.3 array ordering in Unity

Unity 2021.3:

2021.3 array ordering in Unity

ユニークな ID でスムーズな共同作業を実現

Stable ID 機能は Unity ファイル内の変更を減らすだけでなく、共同作業でよくある課題に対応するように設計されています。以前のバージョンでは、同じホストでマネージ参照オブジェクトを追加した 2 人のユーザーが同じ ID を持ってしまい、マージが困難になることがありました(特に、1 つのマネージ参照オブジェクトが複数のフィールドで参照される可能性があるため)。Unity 2021 では、ID は時刻とシステム情報のハッシュ値に基づいて生成されるため、このような衝突は事実上回避されるようになりました。より高度なシナリオでは、SerializationUtility.SetManagedReferenceIdForObject を呼び出して、デフォルトの ID 割り当てシステムをオーバーライドすることも可能です。

欠落している型の処理

SerializeReference はポリモーフィズムをサポートしており、フィールドの型から派生したクラスのインスタンスにフィールドを割り当てることができます。実際には、すべての C# クラスの基底クラスのルートである「System.Object」をフィールドの型としてサポートしています。しかしこうすると、コンパイルに成功したプロジェクトにおいて、以前は存在していて、シーンやアセットファイルに保存されていたクラスの定義が欠落する可能性が出てきます。ソースファイルの削除、クラス名の変更、別のアセンブリへの移動などにより、クラスが欠落する場合もあります。

SerializedReference のホストオブジェクトを読み込む際、各マネージ参照オブジェクトの完全修飾型名が調べられ、インスタンス化するためには有効なクラス型に解決される必要があります。以前のバージョンの Unity では、クラスが欠落していると、有効なマネージ参照オブジェクトを読み込むことなく、「ホスト」オブジェクト全体がエラー状態になることがありました。そのため、15 個のマネージ参照オブジェクトの配列を持つ「ホスト」があったとして、そのうち 1 個のオブジェクトでも解決できなかった場合、インスペクターにはすべてのオブジェクトが表示されなくなります。ホストオブジェクトを検査したときにエラー状態であることが視覚的に示されていなくても、コンソールにエラーが記録され、行われたすべての編集が通知なしで破棄されます。

Unity 2021 では、読み込み可能なすべてのマネージ参照オブジェクトをインスタンス化し、欠落しているオブジェクトを NULL に置き換えるようになりました。これにより、ユーザーはホストオブジェクトの状態をより詳しく確認することができ、また、欠落している型の解決も容易になります。一方、ホストオブジェクトの読み込み中に欠落していた型が復元された場合、トリガーされたドメインの再ロードによってマネージ参照オブジェクトが復元され、それを参照するすべてのフィールドが適切に更新されます。

これは、型がないオブジェクトがインスペクタに表示される例です。

Unity 2020.3 では、Fruit クラスがないにもかかわらず、インスペクターに配列要素が表示されず、エラーの表示もありません。

Unity 2021.3では、Sandwich オブジェクトは表示されたままですが、Fruit オブジェクトは NULL エントリとして表示されるという警告がインスペクターに表示されます。

コンソールのエラーメッセージも新しくなり、どのホストオブジェクトに型がないかを示すだけとなり、同じ情報の繰り返しが少なくなりました。

Unity 2020.3 でのエラーメッセージは以下のようになります。

これを、Unity 2021.3 のこちらの警告メッセージと比較してみてください。

プレハブ

こうした ID の改良を活かして、プレハブ内のマネージ参照オブジェクトに加えられた変更も、それらのマネージ参照オブジェクトに紐づくようになりました。従来、PropertyModification は、そのフィールドにつながる最初のプロパティのパスに基づき、フィールドを指していました。これは、パスが変更された場合(例えば、配列の並べ替えなど)、PropertyModification が意図したマネージ参照を見失い、プロパティを適切に解決できなくなることを意味します。Unity 2021 では、PropertyModification は Stable ID を組み込んだパスを使用してマネージ参照オブジェクトを参照するようになっています。つまり managedReferences[12345].myString のように参照します。これにより、マネージ参照オブジェクトは、ホスト上のどこに移動しても、そのオーバーライドされた値を維持することが保証されます。

API サポートの改善

Unity API に新しく SerializationUtility クラスが追加され、SerializeReference に関連する機能を公開するようになりまし た。例えば、SerializationUtility.ClearAllManagedReferencesWithMissingTypes() は、欠落している型への参照を削除するために使用できます。欠落している型に対して復元する見込みがない場合、ホストから警告状態を削除するなどの使い方ができます。

CustomEditor のコンテキストでマネージ参照を扱うための API を改良し、SerializedProperty.managedReferenceValue への読み取りアクセスのオプションを追加しました。

また、新しいメソッドのリファレンスではサンプルコードを提供し、参照のトピックではシリアル化に関するより詳細な情報を盛り込みました。

SerializeReference を使用している既存のプロジェクトは、シリアル化コードが古いマネージ参照形式と互換性があるため、新しいバージョンの Unity でもスムーズに読み込めるように配慮されています。多くの場合、SerializeReference の使用には、Stable ID の概念や新しい API のメソッドに関する深い知識は必要ありません。仕組みについては「ボンネットの裏」のままであっても、これらの改善は一般的な使用、特に共同作業環境において有益なものです。

この記事をきっかけに、さらに機能を深堀りしていただければと思います。シリアル化チームは、すべての Unity ユーザーのために機能を強化し続けているので、この専用のフォーラムスレッドで継続的にフィードバックとディスカッションをお願いします。

2022年7月11日 カテゴリ: Engine & platform | 11 分 で読めます

Is this article helpful for you?

Thank you for your feedback!

取り上げているトピック