ノートの端の書き残し

UnityやらC#やら。設計が得意かもしれない。

SerializeReferenceとSubclassSelectorがすごい便利

Unity2019.3から使用可能なSerializeReferenceと、

docs.unity3d.com

下記で公開くださっているSubclassSelectorがすっごい便利でしたので布教したくなりました。

github.com

SerializeReferenceについても素晴らしく丁寧な説明をしてくださっています。

light11.hatenadiary.com

なお、今回の記事はUnity2020.2f1で動作を確認したものとなります。

使ってみた

/// <summary>
/// SerializeReference用サンプルScriptableObject
/// </summary>
[CreateAssetMenu]
public class Sample : ScriptableObject
{
    [SerializeReference, SubclassSelector] private ISample[] _array;
}

/// <summary>サンプル用インターフェース</summary>
public interface ISample { }

/// <summary>サンプル用クラスA</summary>
public class SampleClassA : ISample
{
    [SerializeField] private string _name;
    [SerializeField] private int _value;
}

/// <summary>サンプル用クラスB</summary>
public class SampleClassB : ISample
{
    [SerializeField] private string _name;
    [SerializeField] private string _description;
}

これが、こうな……る……? あれ? f:id:u_osusi:20210204193120p:plain

しまった。基本的にどんなプロジェクトでもエディタ拡張アセットのOdinが使えるようにしているのでOdinアリの見た目になってました。 どうやらOdinさんはもともとSerializeReferenceだと代入可能な型を選択できる機能が備わっていたようですね。 さすが大人気アセット。隙がねぇな。

assetstore.unity.com

さて、Odin無い通常バージョンでの見た目を改めて確認すると f:id:u_osusi:20210204193719p:plain

期待した通りになってますね!

インターフェース型変数をシリアライズしたいんだよなぁというケースはめちゃくちゃあったので、これはすごくありがたいですね!

ちょっと改良してみる

仕事だと、実際にエディタで色々と調整を施すのはプランナーなのが多い、と思います。(小規模ならそうでもないことも多そうですけどね) なので、クラスの名前がそのまま表示されちゃうのはちょっと困るかも。そんなときは表示用のメタデータ

public class NameAttribute : Attribute
{
    public string Name { get; }
    public NameAttribute(string name) => Name = name;
}
/// <summary>サンプル用クラスA</summary>
[Name("サンプルA")]
public class SampleClassA : ISample
{
    [SerializeField] private string _name;
    [SerializeField] private int _value;
}

/// <summary>サンプル用クラスB</summary>
[Name("サンプルB")]
public class SampleClassB : ISample
{
    [SerializeField] private string _name;
    [SerializeField] private string _description;
}

こうしてあげると f:id:u_osusi:20210204193726p:plain

よさげ。

注意点

SerializeReferenceはその名の通り、参照をシリアライズします。 そのため、SubclassSelectorは「指定された型のインスタンスを生成してその参照をシリアライズ」という手法を取っています。 SubclassSelectorDrawer.csを見ていただくとわかるんですが、インスタンスの生成にはActivator.CreateInstanceを使用しています。 Activator.CreateInstanceはnewと同じですしこれは正しいのですが、生成したい型に引数無しのコンストラクタが無いとエラーが出てしまいます。

(インスペクタ表示用のクラスにデフォルト以外のコンストラクタを用意するのは悪手だとは思いますけどね)

これについての説明と対処法はこちらの記事で解説しています。

nigiri.hatenablog.com

余談

f:id:u_osusi:20210204195422p:plain

Unity2020.2f1で、順番入れ替え可能なReordableListがデフォルトで使えるようになっているようです。 前まではinternalで存在していて、知る人ぞ知るものだったんですが嬉しいですね。 またUnity2020.2からはC#バージョンも8に上がり(.NET Standardは2.0のままですが…)、Switch式など超便利な記述が多数可能になっていて、これも見逃せません。