はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】ScriptableObjectを使ってインスペクターを使ったポリモーフィズムを実現する

はじめに

今回はScriptableObjectを使った一例を紹介したいと思います。

どんな例かというとインスペクターを使ったポリモーフィズムの実現です。

f:id:hanaaaaaachiru:20200607014853p:plain

よくある例をみてから、それをどう改善していくかをみていきましょう。

シンプルなもの

まずはこの公式ドキュメントを参考にしながら、シンプルなポリモーフィズムを書いてみます。
docs.microsoft.com

using UnityEngine;

public abstract class Shape 
{
    public abstract void Draw();
}

public class Circle : Shape
{
    public override void Draw()
        => Debug.Log("円を書いたよ");
}

public class Rectangle : Shape
{
    public override void Draw()
        => Debug.Log("四角を書いたよ");
}
public class Triangle : Shape
{
    public override void Draw()
        => Debug.Log("三角を書いたよ");
}


これを利用する側はこんな感じに実装しました。

using System.Collections.Generic;
using UnityEngine;

public class Drawer : MonoBehaviour
{
    private List<Shape> _shapes = new List<Shape>
    {
        new Circle(),
        new Rectangle(),
        new Triangle()
    };

    private void Start()
    {
        foreach(var shape in _shapes)
        {
            shape.Draw();
        }
    }
}

f:id:hanaaaaaachiru:20200607014842p:plain

問題点

この実装方法だとそれぞれのインスタンスを管理するコレクションの初期化をコード側で書く必要があります。

private List<Shape> _shapes = new List<Shape>
{
    new Circle(),
    new Rectangle(),
    new Triangle()
};


Unityではインスペクターで設定するという強力なシステムがありますので、これを利用したいと考えました。

一番先に思いついた方法はShapeクラスにMonoBehaviorを継承させる手法ですが、それだけのためにコンポーネントを増やすのは愚作のような気もします。(MonoBehavoir自体大きいクラスですしね)

ここで思いついたのが、ShapeScriptableObjectを継承させてしまうということです。

ScriptableObjectを使う

Shapeと各サブクラスを以下のように変更します。

using UnityEngine;

public abstract class Shape  : ScriptableObject
{
    public abstract void Draw();
}

[CreateAssetMenu]
public class Circle : Shape
{
    public override void Draw()
        => Debug.Log("円を書いたよ");
}

[CreateAssetMenu]
public class Rectangle : Shape
{
    public override void Draw()
        => Debug.Log("四角を書いたよ");
}

[CreateAssetMenu]
public class Triangle : Shape
{
    public override void Draw()
        => Debug.Log("三角を書いたよ");
}

ScriptableObjectを継承したのと[CreateAssetMenu]を付けただけですね。

このようにすることで、各図形をアセットとして作成することができます。

f:id:hanaaaaaachiru:20200607020243p:plain

利用する側

今度はこれらの図形を利用してみましょう。

using System.Collections.Generic;
using UnityEngine;

public class Drawer : MonoBehaviour
{
    [SerializeField] private List<Shape> _shapes;

    private void Start()
    {
        foreach(var shape in _shapes)
        {
            shape.Draw();
        }
    }
}

こうすることで、どの図形をどの順番で実行するかを全てインスペクターから決定することができます

f:id:hanaaaaaachiru:20200607020531p:plain

付け足し

この記事ではみやすさのためにShapeCircleなどのクラスを一緒に書いていますが、実際はきちんとスクリプトを分けて書いてください。

というものScriptableObjectにはクラス名とファイル名は同じにする必要があるからです。

忘れずにやっておきましょう。

さいごに

実はScriptableObjectを使ったことでまだまだ利点があります。

それはエディタ拡張がしやすいということで、インスペクターをみやすくしたり、アセット自体をエディタ拡張で作成したりなんかもできます。

上手に使えればかなり強力なのではないでしょうか。

またマサカリ等があればじゃんじゃんコメント等で教えていただけると嬉しいです。

ではまた。