【Unity】データ管理にはScriptableObjectを使おう

はじめに

「どうやってアイテムの情報を管理すればいいのか分からない」

「複数の敵パラメータを管理する方法が分からない」

これを読んでいるあなたは、こんな悩みを持っているのではないでしょうか?

ゲーム制作ではアイテムや敵、ステージなど様々なデータを扱います。

例えば敵だと「名前、HP、攻撃力、防御力、経験値」などのデータが敵の数だけ必要です。

ゲームには膨大なデータが必要で適当に管理しているとゲーム制作後半でかなり苦しみます…

でも大丈夫です!安心してください!

Unityでは「ScriptableObject」という機能を使うとデータ管理も簡単にできちゃうんです。

この記事を読むと「Unityで複雑なデータを扱いやすく管理する方法」を知ることができます。

あなたのゲーム制作が捗ること間違いなしなのでぜひ最後まで読んでみてください。

ScriptableObjectとは?

まずはScriptableObjectとは何なのかを軽く説明しておきます。

公式サイトでは以下のように説明されています。

ScriptableObject はスクリプトインスタンスから独立した大量の共有データを格納できるクラスです。

https://docs.unity3d.com/ja/540/Manual/class-ScriptableObject.html

何やら難しい説明ですが、簡単に言うと「ゲームで使う変化しないデータアセットを作る機能」です。

変化しないデータとは「敵のパラメータの初期値」や「アイテムの効果」、「ステージの制限時間」などです。

ScriptableObjectとを使うと、こういった初期値などに使うデータを管理するためのアセットを作ることができます。

ScriptableObjectでデータ管理

今回はScriptableObjectで敵データを管理してみたいと思います。

ScriptableObjectを使う手順は次のようになります。

  • ScriptableObjectを継承したクラスを作成
  • エディタでScriptableObjectのAsset作成
  • ScriptableObjectを読み込む

ScriptableObjectのクラス作成

ScriptableObjectを作成するためのスクリプトを作成します。

EnemySettingという名前のスクリプトを適当な場所に作りましょう。

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

[Serializable]
public class EnemyData
{
    public string Id;
    public int Hp;
    public int Attack;
    public int Defense;
    public int Exp;
}

[CreateAssetMenu(menuName = "ScriptableObject/Enemy Setting", fileName = "EnemySetting")]
public class EnemySetting : ScriptableObject
{
    public List<EnemyData> DataList;
}

ScriptableObject用のクラスは以下の設定が必要です。

  • ScriptableObjectを継承する
  • CreateAssetMenuアトリビュートをクラスに持たせる

敵1体分の情報は別クラスで作成して、そのクラスのリストをデータとして持たせています。

これで敵の種類ごとにデータを管理できるようになります。

[Serializable]
public class EnemyData

1体分の情報クラスには[Serializeable]属性をつけて作成します。

これでこのクラスの変数がインスペクター上に表示可能になります。

ScriptableObjectのAsset作成

クラスを作成すると、インスペクター上でScriptableObjectのアセットが作れるようになっています。

Projectウィンドウで右クリックすると、menuNameで指定した名前のメニューができているはずです。

作成したアセットをクリックすると、インスペクターでデータを設定できます。

こんな感じでデータを入力します。

ScriptableObjectの読み込み

ScriptableObjectはアセットをロードしないと使えません。

今回はEnemyManagerというクラスを作成して、EnemySettingをメンバ変数に作ります。

ロードしたデータを変数に入れておき、敵データを知りたいときはその変数を参照します。

using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using System.Linq;

public class EnemyManager : MonoBehaviour
{
    EnemySetting m_enemySetting;

    private async UniTask Start()
    {
        m_enemySetting = await Addressables.
               LoadAssetAsync<EnemySetting>("Settings/EnemySetting.asset");

        //スライムのデータを取得
        var slimeData = m_enemySetting.DataList.
                        FirstOrDefault(enemy => enemy.Id == "Slime");
        Debug.Log($"ID:{slimeData.Id}");
        Debug.Log($"Hp:{slimeData.Hp}");
        Debug.Log($"攻撃力:{slimeData.Attack}");
        Debug.Log($"防御力:{slimeData.Defense}");
        Debug.Log($"経験値:{slimeData.Exp}");
    }
}

実行すると以下のようになります。

ロードに使ったAddressablesはPackageManagerから簡単にインストールできるので、まだの方は導入しておきましょう。

また、こちらのコードにはUniTaskも使っています。

UniTaskをまだ導入していないかたはこちらの記事を参考にしてみてください。

ScriptableObjectのメリット

ScriptableObjecを使ったデータ管理の方法を学びましたが、ScriptableObjecを使うメリットも書いておきます。

まとめると以下の点がScriptableObjecを使うメリットです。

  • メモリを節約できる
  • パラメータの調整がしやすい
  • 実行時パフォーマンスに優れている

メモリを節約できる

一番大きなメリットがこちらになります。

もしも敵のクラスに直接パラメータを持た場合、同じ敵が複数出たときにメモリが無駄に消費されます。

例えば以下のようなスライムクラスがあったとします。

class Slime
{
    public int CurrentHp = 10;
    public int MaxHp = 10;
    public int Attack = 5;
}

スライムが10体出現した場合、MaxHp・Attackはどのスライムでも同じ値を使うので9体分のメモリは本来使わずに済んだはずです。

ScriptableObjectを使って変化しない静的なデータを定義しておき、そこから参照するようにすれば無駄が出ません。

パラメータの調節がしやすい

2つ目のメリットはパラメータの調節がしやすい点です。

ScriptableObjecはUnityの機能なのでパラメータがインスペクタ上でいじれます。

実行中もインスペクターから値が変更でき、実行終了後も値が保持されたままなのでバランス調整がとてもしやすいです。

実行時パフォーマンスに優れている

ロード時間やメモリ使用量が他のデータ形式と比べて優れています。

さすがUnity純正の機能なのでその辺は強いですね!

ScriptableObjectの注意点

ScriptableObjecはセーブデータなど変化するデータの保存はできません。

あくまで「ゲームで使う変化しないデータアセットを作る機能」であるという点には注意が必要です。

まとめ

今回はScriptableObjecを使ったデータ管理の方法を解説しました。

最初はめんどくさく感じるかもですが、慣れてくるととても便利な機能です。

Unityでゲームを作る場合ほぼ間違いなく使う機能なので、この機会にぜひマスターしましょう!

👇あなたにお勧めの書籍

コメント