dasukoのブログ

現役エンジニアのブログです。

【Unity】SerializeReferenceを使ってみた

はじめに

今回はSerializeReferenceを使ってみたいと思います。

ドキュメントはこちら

Unity - Scripting API: SerializeReference

SerializeReferenceとは

フィールドをシリアル化する方法として、SerializeFieldAttributeを追加するという方法があります。

しかし、この方法でシリアル化できるのは、値型とクラスが抽象クラスやインターフェースではない参照型オブジェクトのみでした。

When Unity serializes an object, it serializes all fields as value types, unless the field type derives from [UnityEngine.Object]. By default, polymorphic fields are not supported and reference based topologies, like graphs, cannot be expressed natively.

通常、UnityEngine.Objectを継承していない限りは値としてシリアル化されるようです。

SerializeReferenceAttributeを指定することにより、本来値としてシリアル化されるフィールドを参照としてシリアル化できるというわけです。

SerializeReferenceAttributeはインターフェースや抽象クラスもシリアル化することができます。

ドキュメントにある注意事項についても引用しておきます。

  • The field type must not be of a type that specializes UnityEngine.Object.
  • The field type can be abstract.
  • The field type can be an interface.
  • Generic Lists and array fields decorated with [SerializeReference] apply the attribute to the elements of the list/array not the list/array instance itself.
  • Referenced values cannot be shared between UnityEngine.Object instances. For example, two MonoBehaviours cannot share an object that is serialized by reference. Use ScriptableObjects instead to share data.
  • Field value can be null.
  • Field value cannot be a specific specialization of a generic type(inflated type).
  • The type of the dynamic instance/object assigned to the field must be of a [Serializable] type.
  • The types 'System.Object', 'List<System.Object>' or 'System.Object[]' are also supported for the field type.

注意点として重要なのは以下だと思います。 - UnityEngine.Objectを継承したクラスであってはならない - 参照型の値はインスタンス間で共有できない -フィールドタイプは抽象クラスやインターフェースも指定可能 - System.Objectを指定することも可能 - Serializable Attribute必須

インターフェースのシリアライズ

まずはインターフェースをシリアライズしてみます。

以下のようなクラスを追加して、シリアライズされるのか確認してみました。

using UnityEngine;

public class SerializeReferenceTest : MonoBehaviour
{
    [SerializeReference]
    private ICharacter character;
}

public interface ICharacter
{
}

public class Slime : ICharacter
{
    public int HP;
    public int Attack;
    public int Defense;
}

インスペクター上でシリアライズはされてるけど、継承したクラスを実装してもクラスを選択できないみたいです。

f:id:dasuko:20201020014438p:plain

この辺はエディタ拡張で具体的なクラスを指定してあげる必要があるみたいです。

以下でエディタ拡張を実装した例が紹介されています。

【Unity】インターフェイスをSerialize出来るようにするSerializeReferenceのための表示attributeを作ってみた - Qiita

抽象クラスのシリアライズ

次は同じ実装を抽象クラスを使って実装してみました。

フィールドは抽象クラスで定義しました。

using UnityEngine;

public class SerializeReferenceTest : MonoBehaviour
{
    [SerializeReference]
    private CharacterBase character;
}

[System.Serializable]
public abstract class CharacterBase
{
    public int HP;
    public int Attack;
    public int Defense;
}

[System.Serializable]
public class Slime : CharacterBase
{
  
}

こちらもやはり同様にフィールドはシリアライズされませんでした。

f:id:dasuko:20201020014438p:plain

まとめ

参照で持てるようになったとは言え、追加実装が必要となると、まだまだ使いづらい印象です。

追加実装なしにインスペクターからクラスを選択できるような機能をUnityが提供してくれたらいいですね。

参考

Unity - Scripting API: SerializeReference

【Unity】インターフェイスをSerialize出来るようにするSerializeReferenceのための表示attributeを作ってみた - Qiita