LIGHT11LIGHT11読者になるLIGHT112020-07-09【Unity】SerializeReferenceをちゃんと理解するUnityUnityで参照をシリアライズするSerializeReferenceアトリビュートについてまとめました。参考Unity2019.4.0はじめにSerializeReferenceアトリビュートはUnity2019.3で追加された、参照をシリアライズするための機能です。うまく使えば非常に便利な機能なのですが、使いこなすには仕様をしっかり理解する必要があります。値をシリアライズする従来のSerializeFieldとの性質の違いも把握する必要があります。本記事ではこのSerializeReferenceの基本的な知識をまとめた上で実用的な活用方法について考えます。注意点:結構バグってた以下のフォーラムを見ると、SerializeReferenceについては結構バグの報告が多い印象です。https://forum.unity.com/threads/serializereference-attribute.678868/修正は随時行われているようなので、微妙なUnityのバージョン違いで挙動が異なることがあると思います。Unity2019.3の前半あたりは結構まだバグが多いかもしれません。本記事中でももし古い内容などありましたらご指摘いただけますと幸いです。以前書いた記事なお以前SerializeReferenceについて以下の記事を書きましたが、あくまで触りだけだったので本記事できちんとまとめます。light11.hatenadiary.comSerializeFieldを復習するSerializeReferenceについて学ぶ前に、まずは以前からあるSerializeFieldの挙動について簡単にまとめます。プリミティブ型・構造体をシリアライズできるSerializeFieldを付けるとprivateなプリミティブ型、構造体を明示的にシリアライズ対象にできます。ためしにいくつかのプリミティブ型や構造体にSerializeFieldを付けたコンポーネントを作ってみます。}}このコンポーネントを適当なGameObjectにアタッチすると、SceneやPrefabに値がシリアライズされます。それらの値は以下のようにInspectorから編集可能になります。InspectorUnityEngine.Objectの派生型の参照をシリアライズできるまた、UnityEngine.Objectから派生した型のインスタンスに関してはその参照をシリアライズすることができます。_field3;}このコンポーネントを適当なGameObjectにアタッチすると以下のように各オブジェクトの参照を代入できることが確認できます。Inspector非UnityEngine.Objectなクラスのインスタンスは値としてシリアライズできるUnityEngine.Objectではないクラスのインスタンスも、Serializableアトビリュートが付いていればシリアライズすることができます。この場合、参照ではなくフィールドの値をシリアライズします。同じインスタンスを違うフィールドに代入しても、デシリアライズされた際にそれらは別々のインスタンスになってしまいます。この挙動を確認するコードを以下に示します。SerializeTestを実行すると以下のようなログが出力されます。出力デシリアライズされると二つのオブジェクトが全く異なるインスタンスになっていることが確認できました。ちなみにこうしてシリアライズしたフィールドはnullにならず必ずインスタンスが代入された状態になるという仕様があります。抽象クラスやインタフェースには非対応さてSerializeFieldは非UnityEngine.Objectの抽象クラス型やインタフェース型をシリアライズ対象にすることはできません。UnityEngine.Object派生型の抽象クラスについてはシリアライズ対象にできます。MonoBehaviour{}Genericな型はシリアライズできる(Unity2020.1から)なおUnity2020.1からはGenericな型をSerializeFieldでシリアライズできるようになりました。_testField;}SerializeReferenceの仕様を理解するそれでは次にSerializeReferenceの仕様を見ていきます。SerializeReferenceは非UnityEngine.Objectのための機能さてまず前提として、SerializeReferenceはUnityEngine.Objectを継承した型ではない型の参照をシリアライズするためのものです。前述の通り、SerializeFieldはUnityEngine.Objectの派生型の参照をシリアライズできるのでそっちを使えばOKです。仮にSerializeReferenceアトリビュートを付けたInterface型にUnityEngine.Objectを代入しようとすると以下のようなエラーが出力されます。Unity.Object参照をシリアライズするさてSerializeReferenceはSerializeFieldとは異なり、参照情報を含めてシリアライズします。これを確認するために、SerializeFieldのときに使ったコードをSerializeReferenceに書き換えてみます。SerializeTestを実行すると以下のようなログが出力されます。同じインスタンスを二つのフィールドにシリアライズしてデシリアライズしても、それらは同じインスタンスを示すことがわかりました。抽象型やインタフェース型のフィールドをシリアライズできるSerializeReferenceアトリビュートは抽象クラスやインタフェース型のフィールドに付けることもできます。これらのフィールドにインスタンスを代入すると実際の型の情報がシリアライズされます。}}Inspectorにも実際の型に対応したプロパティが表示されます。Inspector参照を二つのMonoBehaviour間で共有することはできないSerializeReferenceでシリアライズした参照情報を二つのMonoBehaviour間で使いまわすことはできません。ScriptableObjectにシリアライズした時も同様で、二つのScriptableObject間で使いまわすことはできません。nullを表現できる上述の通り、SerializeFieldには、このアトリビュートを付けたフィールドはnullにならないという仕様がありました。これに対してSerializeReferenceを付けたフィールドはnullを表現できます。Genericを使ったクラスのインスタンスには使えないGenericを使ったクラスのインスタンスをシリアライズしようとしてもできません。そのようなケースではさらにそれを継承する非Genericなクラスを定義する必要があります。}}SerializeReferenceの使いどころさてこのように意外と複雑なSerializeReferenceですが、実際どのような活用方法があるのでしょうか。グラフ構造やツリー構造をシリアライズする参照をシリアライズするという特徴からまず考えられるのはグラフ構造やツリー構造です。グラフ構造このようにお互いに参照関係を持つ構造はまさにSerializeReferenceの使いどころといえます。ScriptableObjectとSerializeFieldでも実現可能ですが、ノードの数が多いと辛いです。エディタの入力インタフェースを手間なく切り替えられる次の使いどころとして、入力インタフェースが簡単に切り替えられる点があります。いま、スキルの効果値を入力することを考えます。スキルの種類には攻撃とバフがあり、攻撃の場合には攻撃力を入力できます。バフの場合はバフの種類(攻撃力、防御力など)と効果値、そして効果時間を入力できます。これをSerializeReferenceを使って実装してみます。}インスタンスの型を切り替えるとそれに応じて入力フィールドも変更されていることが確認できます。振る舞いをエディタから変更できるインスタンスが差し替えられるということは以下のように振る舞いも差し替えられます。Debug.Log($"{word}{word}{word}");}これを実行するとシリアライズされているインスタンスの種類に応じたSpeakメソッドが呼ばれます。ちなみにこちらもScriptableObjectでも実現はできますが、振る舞いの種類ごとにScriptableObjectを作らなければならなくなります。1はてなブログをはじめよう!halya_11さんは、はてなブログを使っています。ブログを報告する引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。