[Unity] ScriptableObjectを使ってみた話

Unity

Unityでゲームを作っていると、キャラクターやアイテム、スキルなど「たくさんの種類を持つデータ」を管理したくなる瞬間があります。
私みたいな個人でゲーム制作する程度の規模感で、且つテキトー人間(笑)であれば、直接コードにデータを埋め込んだりすることが結構多いですw

最初は enum配列 を使って何とかしていたものの、要素数が増えるたびに「あれ?このデータ、どこで定義してたっけ?」「てか直接いじるの面倒い」と、ここまでくるのがいつもの流れ、様式美です。

で、そんな時実際にどのようなデータ管理するのが良いの?と選択肢について色々考えてみました。
今まで実際にゲーム会社で制作した際、データ管理をする場合は、CSVやTSV形式のファイルをマスタデータとして使用することが多かったです。
(おそらくサーバーからデータを送ったり、昔から使われているやり方なのでノウハウ的にも共通認識が取れているからよく使われているのかな?知らんけど。)

色々考えているうちに、そういえば使ったことないじゃないか!と思いついたのが
ScriptableObject です。

ScriptableObjectって何?

ということで、思い出したように使ってみた ScriptableObject。
名前はよく聞くけど「なんかアセット増えるし難しそう…」と避けてたあなた(=過去の私)に向けて、ざっくり説明してみます。

ScriptableObject は、Unityが用意してくれている「データだけを持つ特殊なオブジェクト」です。
MonoBehaviourみたいに値を保持できるけど、シーンに存在せず、Projectビューでアセットとして保存できるというのが最大の特徴です。

例えば「魔法の名前・威力・属性」みたいな情報を一つのファイルにまとめて、ゲーム内で参照して使えるんです。
Prefabみたいにポンと作って、インスペクターから値を変えられて、スクリプトからも呼べる。なんだこの便利グッズ…!

要するに、「コードを書かずに、データだけ独立して管理したい!」というときに超役立ちます。

早速試しに作ってみましょう

ってことで論より証拠、とりあえずサクッと作ってみましょう。

MagicData.cs(ScriptableObjectの定義)

using System;
using System.Collections.Generic;
using UnityEngine;

[Serializable]
public class MagicElement
{
    public string magicName;
    public string description;
    public int power;
    public int manaCost;
    public ElementType element;
}

public enum ElementType
{
    Fire,
    Water,
    Wind,
    Earth,
    Light,
    Dark
}

[CreateAssetMenu(fileName = "MagicData", menuName = "Game/MagicData")]
public class MagicData : ScriptableObject
{
    public List<MagicElement> magicList = new List<MagicElement>();
}

↑のサンプルコードをUnityに追加すると、
メニューの “Assets/Create/Game/MagicData” をクリックできるようになります。
クリックしたら MagicData ScriptableObjectが生成されます。

これで、MagicElement(1魔法の要素)を複数持つことができるMagicData(データリスト)を作成することができます。

こんなイメージ(適当に入れました)

利用側のコード例(MagicDataを使うスクリプト)

using UnityEngine;

public class MagicManager : MonoBehaviour
{
    public MagicData magicData;

    void Start()
    {
        foreach (var magic in magicData.magicList)
        {
            Debug.Log($"魔法名:{magic.magicName} / 属性:{magic.element} / 威力:{magic.power}");
        }
    }
}

今度はその設定したデータを引っ張ってくるクラスを作ってみました。
参照したデータを読み込んで、ただコンソールに表示するだけ

実際に動かしてみると…

こんな感じで表示されてますね。
必要に応じて呼び出して使うってことは容易にできそう。

メリット

どういう場面で使われるのかな?と思ったけどやっぱりデータ管理がメインになるのかな?

  • キャラクターやアイテムなどの「マスタデータ」
  • 各種定数やバランス調整値の一元管理
  • レベル構成やシナリオなどの構造化データ

色々調べた感じだと、データを個別で管理できるとか、直接Unityで修正できるとか、色々メリットはあると思うけど、一番はメモリ管理的に負担が少ない(はず…?)しかもあまり気にせず簡単に!というところになるのかなと。
参照して直接データを保持できるから、インスタンスとして生成する必要が無い分メモリ消費は抑えられそう。

選択肢としてはどう?

で、データ管理として使うのはアリでしょうか?

まだ使い始めたばっかりなので判断は難しいですが、個人的にはCSV管理で良くね?って気持ちになりました。
データの管理を独立させたい。メモリの負担を下げたい。とかはデータ管理するクラスをその辺意識して作れば特に従来通りのマスタ管理でも問題ないなーというのが正直な感想です。

  • Inspectorで変更できる = 非エンジニアでも使いやすい

これに関しては正直デメリット(とまでは言わないかな?)で、正直Inspectorでちまちま設定するのは性に合わない…
コピペとか、関数とか使って設定できるスプレッドシートとかの方が流石にやりやすいかな…と現時点では思ってます。

ただ、これは多分自分がまだ有用なやり方を身に付けてないからだよ!って話かもしれないので、
そのあたりは要勉強ってところですかね。

結局どうしたのか

と、ここまで考えてCSVで良いかーと思ったところ、「あ、でもScriptableObject使ったら単純にデータクラス作る手間省けるんじゃね?」と思って、

  • データ作成はスプレッドシートで CSV管理
  • Unity内で使用するときはScriptableObject

ということで決着をつけました。
結局 CSV を ScriptableObject へ変換するParserクラスを作るはめになりましたが、これを基本として管理したら、いちいちデータクラス作る手間は省けたので、とりあえず便利になったんじゃないんだろうか、と感じているところです。

なので実際の流れとしては以下のようになりました。

1. CSVでデータを作成
2. CSV → ScriptableObject Parserを使って CSVからScriptableObjectに変換
3. Unity内ではScriptableObjectを参照してデータを使う

最後に

個人的にはCSVと併用する管理になりましたが、Inspectorの操作にそこまで抵抗感がない方であれば、全部そっちに寄せちゃっても良いかもしれませんね。
そのあたりは完全に好みな気がする…

さっきも書いた通り、まだ全然使いこなせてない感じがするので、ぜひこういう使い方もあるよ!などアドバイスいただけると嬉しいです!

コメント

タイトルとURLをコピーしました