[unity2019.LTS]ScriptableObject の使い方

知らなくてもゲームは作れる ScriptableObject ですが、上手く使えばメリットもある。
今回は、「実際に作ってみる」「利点・欠点」といった内容を紹介します。

実際に作って(使って)みる

物凄くザックリ言えば、大体こういう表(テーブル)を作り、編集・管理するものです。

この図よりはもう少し便利に使えますが、使えるようになるまでが面倒、そんな印象です。
以下の4ステップで使えるようになるので、試してみましょう!

青地の「列」部分を作る

Test.cs を作成し、以下をコピペしてください。
HumanList が、表(テーブル)データです。

using System.Collections.Generic;
using UnityEngine;

public class Test : ScriptableObject
{
    /// <summary>
    /// 人データ
    /// </summary>
    [System.Serializable]
    public class HumanEntity
    {
        public string       Name;
        public int          Age;
        public int          Sex;
        public float        Height;
        public float        Weight;
        public int         Place;
    }

    public List<HumanEntity> HumanList = new List<HumanEntity>();
}

Scriptable Object を生成する

生成はちょっと面倒です。
以下の CreateTest.csEditor/ に入れ、Tools - Create ScriptableObject を行うと、Assets/Resources/Test.asset という ScriptableObject が生成されます。
生成された ScriptableObject はインスペクタで値を編集することができます。

using System.IO;
using UnityEditor;

public class CreateTest : EditorWindow
{
    [MenuItem("Tools/Create ScriptableObject")]
    static void createScriptableObject()
    {
        string path = "Assets/Resources/Test.asset";
        
        // なければ作る
        if (Directory.Exists("Assets/Resources/") == false)
        {
            Directory.CreateDirectory("Assets/Resources/");
        }

        // なければ作る
        var data = (Test)AssetDatabase.LoadAssetAtPath(path, typeof(Test));
        if (data == null)
        {
            Test table = CreateInstance<Test>();
            AssetDatabase.CreateAsset(table, path);
        }

        AssetDatabase.Refresh();
    }
}

Scriptable Object を使いやすくする

性別(Sex)や出身地(Place)が数字だと、なんの事かわかりませんし、入力ミスも起こります。
それでは問題なので、Test.cs を変更します。
身長(Height)や体重(Weight)もついでにスライダーで選択できるようにしましょう。Unity Editor 拡張が使えます。

using System.Collections.Generic;
using UnityEngine;

public class Test : ScriptableObject
{
    /// <summary>
    /// 出身地
    /// </summary>
    public enum BirthPlace
    {
        Hokkaido,
        Niigata,
        Saitama,
        Nagoya,
        Tottori,
        Hiroshima,
        Yamaguchi,
    };

    /// <summary>
    /// 性別
    /// </summary>
    public enum Sex
    {
        Unknown,
        Male,
        Female,
    };

    /// <summary>
    /// 人データ
    /// </summary>
    [System.Serializable]
    public class HumanEntity
    {
        public string     Name;
        public int        Age;
        public Sex        Sex;
        [Range(0,200.0f)]
        public float      Height;
        [Range(0,150.0f)]
        public float      Weight;
        public BirthPlace Place;
    }

    public List<HumanEntity> HumanList = new List<HumanEntity>();
}

コードがコンパイルされると、自動的に Test.asset も変化します。これは便利!
enum が選択式のコンボボックスに、Range を指定した部分がスライダーになりました。
Test.cs を変更したからといって、前に入力したものが消えることもありません。

実行中にデータを取得する

再生中にデータを受け取れるかチェックします。
TestCheck.cs を作成し、以下をコピペ。

using UnityEngine;

public class TestCheck
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void testCheck()
    {
        Test test = Resources.Load<Test>("Test");
        foreach (var entity in test.HumanList)
        {
            Debug.Log($"{entity.Name}: {entity.Sex}");
        }
    }
}

オブジェクトにアタッチするのが面倒なので RuntimeInitializeLoadType.BeforeSceneLoad 内メソッドとしましたが、同じことは Monobehaviour クラスの中でも当然可能です。

利点・欠点

このようなデータを扱うのであれば、json 形式(場合によっては csv も…?)がメジャーだと思うので、それと比較します。

利点

  • インスペクタで値を編集できる
  • enum やクラス内クラスなども扱えるので汎用性が高いし、コンボボックスやスライダーなどの入力も使える
  • アクセスが速い
  • バイナリ形式なので、データを解析・改ざんされにくい、ネタバレしづらい

欠点

  • 作成が面倒。Test クラスは仕方ないとしても、CreateTest クラスを自作するのは…。
  • unity にしかない。多フレームワーク展開している場合、他のフレームワークでは使えない

「初心者向けじゃない」導入のために、いまいち広まらないのは残念ですね。
導入さえこなしてしまえば、使いやすさはかなり高いので、是非使ってみてください。