今回はUnityでデータを扱う時に便利な「ScriptableObject」に関する話題で、「こういう場面で活用するとものすごく便利ですよ」という話です。
以前の記事でScriptableObjectのザックリした使い方をご紹介しましたが、実際のところ
と感じた方は多いのではないでしょうか?…かくいう私も最初は使い道が全く分かりませんでした😅
しかし、ある程度使い方が分かってきた今となってはScriptableObjectがないとゲームを作れないくらい必要不可欠なものとなりました。そこでここでは、私が普段からゲーム作りに採用しているScriptableObjectの具体的な使い方を3つご紹介しますね。
ScriptableObjectの3つの活用事例
では早速ですが、今回ご紹介するScriptableObjectの具体的な使い方は次の3つです。
- 共通データの管理
- シーンをまたぐデータの保持
- イベントの管理
それぞれ詳しく見ていきましょう。
活用事例1:共通データの管理
まず一つ目は定番の使い方ですが「共通データの管理」に活用できます。
例えば敵キャラの最大HPなどは同じ種類の敵であれば全く同じですよね。このような場合、プレハブに値を保存しても良いのですが、そうするとプレハブのインスタンスの分だけメモリを食ってしまいます(※最大HPは値としては同じだが、インスタンスごとに別々にメモリが確保されてしまう)。
しかしScriptableObjectに保存された値を読み込めばそのような問題を回避することができます。これは基本中の基本ですね。
活用事例2:シーンをまたぐデータの保持
次に二つ目は、あまり知られていませんがScriptableObjectは「シーンをまたぐデータの保持」にとても役に立ちます。
Unity関係のサイトを見ていると(というかウチのUnity講座のサンプルゲームもそうですが)、シーンをまたぐデータを保持したいときは「DontDestroyOnLoadしたゲームオブジェクトに値を持たせる」方法がよく見受けられます。しかしその方法だと
- データ保持用オブジェクトがシーンに1つだけ存在することを保証する必要がある
- データ保持用オブジェクトを各シーンに配置しておかなければならない(=通しプレイなら最初のシーンだけ配置されていればよいが、途中のシーンからデバッグするときに必要)
- データ保持用オブジェクトを参照するときに面倒くさい(=別のシーンから持ち越される場合があるので、予めインスペクターに登録できず結局Find系関数を使う必要がある)
といったデメリットがあり、どうしても不便な感じが否めません。
一方でこのような場合にはScriptableObjectを使うと便利です。なぜなら
- ScriptableObjectはどのシーンからでも参照することができる(=ゲームオブジェクトのようにシーンに依存しない)
- ScriptableObjectは予めインスペクターに登録することができる(=シリアライズが可能
)
からです。
では具体的にどうすればScriptableObjectでデータを保持できるのか?というと、例えばプレイヤーのスコアを保持しておきたければ次のように作業すればOKです。
- ScriptableObjectを継承したクラスを作る(※サンプルコードは後述)
- ScriptableObjectのアセットを作る
- スコアを参照したいゲームオブジェクトに1.の型の変数を作り、インスペクターから2.のアセットを登録する
これで必要なときにスコアを参照することができるようになります。
手順1のサンプルコード:
using System; using UnityEngine; [CreateAssetMenu(fileName = "PlayerScore", menuName = "PlayerScore")] public class PlayerScore : ScriptableObject { [NonSerialized] public int score = 0; }
注:そのままだとUnityエディタを終了したときにスコアの値が保存されてしまうため[NonSerialized]をつけています(※あくまでもエディタ上でテストプレイしたときの変更が保存されるだけで、ScriptableObjectは実際のゲーム上ではセーブデータのように扱うことはできない点には注意が必要です)。
活用事例3:イベントの管理
最後に三つ目は、これもマイナーな使い方ですがScriptableObjectは「イベントの管理」に非常に役立ちます。
例えば、プレイヤーが死亡してゲームオーバーになったときに次の処理を呼び出したいとします。
- 「GameOver」という画面を出す
- すべての敵キャラを停止させる
このような場合、1.は普通に実装できると思いますが2.は何やら面倒くさそうなのが分かる方も多いと思います。なぜならプレイヤーが死亡したかどうかをどうやってチェックするのか?という問題があるからです。…もちろん「敵キャラ側で毎フレームプレイヤーの状態をチェックする」なんてのはスマートなやり方ではありませんよね。
そこでScriptableObjectの登場です。詳しい話はUnity公式の「ScriptableObject を使用して変更とデバッグを効率化するコードを構築する(Unity)」が秀逸なのでそちらを参照していただきたいのですが、要約すると
- イベント(=ここではゲームオーバー)が起きた時に実行するべき処理を保持するためのScriptableObjectを作る
- イベントが起きた時の実際の処理を1.に登録するスクリプトを作り、必要なゲームオブジェクト(ここでは敵キャラ)にアタッチする
- そうすると、ゲームオーバーになったときに必要な処理が実行される
というような感じです。これによって、プレイヤーと敵キャラは互いに参照関係を持たないにもかかわらず、敵キャラはプレイヤーが死亡したことを検知して必要な処理を行うことができるようになります。
正直この概念は理解するまでが難しいですが、理解できるとめちゃくちゃ便利なのでぜひモノにしていただければと思います。
おわりに:ScriptableObjectを使って便利なゲームシステムを作ろう
以上、ScriptableObjectの具体的な使い方を3つご紹介しました。今回はやや難しめの内容になってしまいましたが、Unityでゲームを作るうえでScriptableObjectの活用方法を知っておいて損はないので参考にしていただければ幸いです。