この記事でのアイテム管理の定義は「ゲーム開発中のアイテムのパラメータの管理」になります。「ゲーム実行中のアイテムの所持数やインベントリなどの管理」は別記事にまとめます。
というわけで、アイテムのパラメータ管理としてUnityでよく利用されるのはScriptableObjectです。
ScriptableObject は、クラスのインスタンスとは独立した大量のデータを保存するためのデータコンテナです。ScriptableObject の主な使用例の 1 つに、値のコピーを避けてプロジェクトのメモリ使用量を減らすことがあります。これは、プロジェクトに プレハブ があり、設定された MonoBehaviour スクリプトに不変のデータを保存している場合に有効です。
参照元:Unity公式マニュアル
言葉だけ聞いてもよくわからないと思うので、解説していきます。
前提
- Unityバージョン:2022.3.9.f1
UnityのScriptableObjectについて解説
先ほど掲載した公式の説明以上に適切な言葉はあまりないので、メリデメを提示します。
ScriptableObjectのメリット
ScriptableObjectをアイテム管理に使うメリットはメモリ使用量を減らすということ以外にもいくつかあると思ってます。それは次のようなものでしょう。
- インスペクター上でデータを追加・修正ができる。
- シーンを跨いでデータにアクセスできる。
ソースに直にパラメータを書かないので、プログラマー以外の人がパラメータをいじれるのはメリットと考えられますね。またアイテムを追加しようとした場合も、プログラマー以外の人が追加できるので、新人とかに任せることもできそうです。
ランタイムに作られるインスタンスにデータを格納するわけではないので、シーンを跨いでデータにアクセスできるのもありがたいですね。シーン間でのデータの引き継ぎが減るのは嬉しいですね。
ScriptableObjectのデメリット
デメリット的には次の感じですかね。
- 外部でデータ管理をしている場合インポートが大変(エディタ拡張が必要)
インスペクター上で設定する必要があるので、スプレッドシート上でアイテムデータの管理をしていると、ダブルメンテになってしまいます。そのためエディタ拡張でスプレッドシートからインポートする機能がないと、修正漏れが発生してしまいそうです。
ScriptableObjectのサンプル
百聞は一見にしかずです。まずは使ってみましょう。一番最初にやるのは公式ページに記載のものをそのまま使ってみることです。
using System.Collections; using System.Collections.Generic; using UnityEngine; [CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)] public class SpawnManagerScriptableObject : ScriptableObject { public string prefabName; public int numberOfPrefabsToCreate; public Vector3[] spawnPoints; }
はい、というわけでまずはScriptableObjectの派生クラスを作りました。
クラスの上にカッコ書きで属性が書いてあり、メニューからこのクラスのアセットを作ることができるようになってます。
fileNameがデフォルトのファイル名、menuNameが右クリックメニューの名前、orderはメニューの位置ですね。↓こんな感じ。
早速Dataを作って、インスペクターに値を入れました。作られるオブジェクトの名前がTestDataで、Spawn Pointsにオブジェクトが作られて行きます。
最後に公式マニュアルにあるSpawnerクラスを作って、適当なGameObjectにアタッチします。
ここでは「Spawner」というからのオブジェクトを作ってアタッチします。適当に3DのCubeをPrefabにしてインスペクターで設定します。そしてSpawn Manager Valuesにさっき作った「Data」を入れます。
これを実行すると、次のようになります。
TestData1〜TestData5のCubeが指定したポジションに作られていますね!
・・・で、だから何やねん!これがどうアイテム管理に役立つんだよ!という感じがします。
ここからアイテム管理についてです。
【Unity】アイテム管理の解説【ScriptableObjectによるデータコンテナ】
ここからScriptableObjectを使ったアイテム管理について解説します。解説では武器と盾のアイテム管理をする例を示します。
作成するScriptableObjectの派生クラスとしてBaseItemDataクラスを作り、武器と盾のクラスはBaseItemDataクラスの派生クラスとして作ります。
具体的には下記の3クラスとなります。
using UnityEngine; public class BaseItemData : ScriptableObject { public string itemName; public int price; }
using UnityEngine; [CreateAssetMenu(fileName = "Weapons", menuName = "ItemData/Weapons")] public class WeaponData : BaseItemData { public int atk; }
using UnityEngine; [CreateAssetMenu(fileName = "Shield", menuName = "ItemData/Shield")] public class ShieldData : BaseItemData { public int def; }
こうすることで、アイテムとして共通のプロパティはBaseItemDataクラスで定義しつつ、武器や盾の特有のパラメータをそれぞれのクラスで管理できるようになります。さらにクラスを分けることでそれぞれのアイテムのデータコンテナを作るメニューを分けられました。↓こんな感じに。
武器と盾のインスペクター上の設定項目はそれぞれ次のようになっています。
このようにItemNameやPriceは設定しつつ、武器にはAtk(攻撃力)、盾にはDef(防御力)を設定できるようになっています。
列挙型を使ってアイテムのタイプを分ける方法もあるっちゃありますが、私はクラスをちゃんと分けるべきと考えています。そうすることで次のようなメリットがあります。
- 余計なパラメータの設定が不要になる。(武器に防御力の設定が不要になる)
- オペミスによる不具合の可能性を減らせる。(Atkの値をDefに設定するようなことがない)
- アイテムのタイプを増やしたときに、そのタイプでしか使わないパラメータがあった時に、影響範囲を限定できる。(杖を追加して魔法攻撃力を追加した時に、武器や盾に影響がない)
このようにしてアイテム管理をすると良いと思います。
【Unity】ScriptableObjectの読み込み
さて、上記ではScriptableObjectのアセットをインスペクター上で指定して実行していました。しかしこの数が増えると、インスペクターに毎回ドラッグ&ドロップしなければなりません。
そこでランタイム上でScriptableObjectのアセットを読み込む方法について解説します。
ランタイム上からアセットを読み込むためには、Resourcesフォルダの下に配置する必要があります。(Resourcesフォルダについては公式ページをご覧ください。)
Resourcesフォルダに配置したらあとは、次のようなスクリプトで読み込むことができます。簡単ですね!
SpawnManagerScriptableObject stage = Resources.Load<SpawnManagerScriptableObject>(ファイル名);
ファイル名はResourcesフォルダからの相対パスで大丈夫です。フォルダも指定することができ、「フォルダ名/ファイル名」という指定も可能です。ちなみにファイル名には拡張子は不要です。
まとめ
UnityのScriptableObjectを使ったアイテム管理についてまとめました。
といっても、正直、これぐらいのものならJSONやCSVでもいい気もします。公式の説明にもある通り、インスタンスを作った時に共通のデータを参照するときにメリットがあるものなので、例えば次のような場合に使用することをお勧めします。
- 3マッチパズルなどで同じオブジェクトを複数作るような場合
- フィールドに同じモンスターが多数出現する場合
- 背景や世界観作りのためにNPCを使いまわす場合
次は、ゲーム内でのアイテム管理(インベントリー)について記事を書こうかと思います。