UnityでRPGやアドベンチャーゲーム等のゲームを作っていると、
- あるイベントが完了済みかどうかをチェックしたい
- 他のイベントの進行状況によってストーリーを変化させたい
といった処理を実装したくなることがあります。このような場合は「フラグ」と呼ばれる変数を定義し、それを元に条件分岐を行うのが一般的だというのは皆さんもご存じかと思います。
ただゲームによってはたくさんのフラグが必要になることがあり、そのような場合は「フラグをどうやって管理するか?」という問題が生じてしまいます。もちろん一つ一つスクリプトに変数を追加していくようでは日が暮れてしまうので、ゲーム開発ソフトによってはフラグを簡単に管理できる機能が搭載されているものもあるのですが残念ながらUnityにはそのような機能はありません。なので一体どうしたものかと途方に暮れてしまう方も多いのではないでしょうか。
そこでここでは、そのような問題に直面している方のために比較的簡単に実装できるScriptableObjectを使ったフラグ管理機能の作り方をご紹介しますね。
今回作るフラグ管理機能について
でははじめに今回作るフラグ管理機能についてザックリと説明していきます。
仕様
まず仕様についてですが、次のような内容となっています。
- bool型のフラグを好きなだけ用意できる。
- ScriptableObjectによってフラグのチェック・変更を管理する。
- フラグの状態を保存するためのセーブ機能は別途用意するものとする。
今回はScriptableObjectというものを使ってフラグを管理する仕組みにしようと思います。ただしセーブ機能が別途必要になるため、アセットを使うなり自前実装するなりで各自対応してください。
なおセーブ機能の実装方法については以下の記事で詳しく説明しているので、自前実装したい方はそちらも併せてご覧いただければと思います。
ScriptableObjectとは?
さて、上記の説明をご覧になった皆さんの中には「ScriptableObjectって何だよ」と思った方もいらっしゃるかと思います。ScriptableObjectについては以下の記事で詳しく説明しているのでそちらも併せてご覧いただきたいのですが…
一言でいうと、これは「ゲーム中で変化しないデータを格納しておくファイル」のことです。一般的には
- 敵キャラのステータス
- アイテムの効果
といったデータを格納しておくために使われるのですが、ScriptableObjectは非常に応用範囲が広いのでフラグ管理機能の実装にも役立ちます。というのはScriptableObjectには
という特性があるので、フラグのようにあらゆるシーンから参照できるような値を格納しておくにはもってこいなのです。
ただしScriptableObjectはゲーム中での変更が保存されない性質があるため(※)、そのままセーブデータのように扱うことはできません。そこで前述のようにセーブ機能は別途用意しておく必要があります。
※注:正確に言うとビルド後はゲーム中での変更が保存されないのですが、エディタ上では再生モードを終了しても変更が保持される…というややこしい挙動をします(つまり、ゲーム開発中は再生モードで確認しながら値をいじって調整することが可能)。このことはちゃんと覚えておいたほうがいいです。
よくあるフラグ管理の仕組みとの違い
ところで今回の方法はよくあるフラグ管理の仕組みとは一味違うやり方なので、一体どんなメリットがあるのかと思った方もいらっしゃるかもしれません。そこでその点についても簡単に説明しておきます。
まず、フラグ管理で一般的によく用いられる方法としては次のようなものがあると思います。
- ビット演算を使う
- ディクショナリを使う
それぞれについての詳細はググって頂きたいのですが、Unityでのゲーム開発に限って言うとそれぞれ次のようなデメリットがあります。
- ビット演算→大量のフラグを管理するのには向いていない
- ディクショナリ→Unityの標準機能ではシリアライズできないので、インスペクターに表示されない
これではフラグ管理機能としては不十分ですよね。しかし今回ご紹介する方法であれば
- フラグをいくつでも簡単に追加できる
- ScriptableObjectはUnityの標準機能なのでインスペクター上で簡単に扱える
- どのシーンからでも参照できる
- ScriptableObjectのファイル名によってどれがどのフラグなのかを識別できるので間違えにくい
といったメリットがあるのですごく便利なのです。
フラグ管理機能のC#スクリプト
というわけで前置きが長くなってしまいましたがここからが本題です。フラグ管理機能のC#スクリプトを掲載します。
下記のC#スクリプトは自由に使っていただいて構いませんが、作者を偽るのは禁止です。例えば何か別の場所に掲載する場合は必ずクレジットを明記してください。
フラグデータを格納するためのScriptableObject
using UnityEngine; [CreateAssetMenu(fileName = "New FlagData", menuName = "ScriptableObject/Flags/FlagData")] public sealed class FlagData : ScriptableObject { [SerializeField] bool isOn = false; public bool IsOn { get { return isOn; } } public void InitFlag() { isOn = false; } public void SetFlagStatus(bool value = true) { isOn = value; } }
フラグの一覧を格納するためのScriptableObject
using System.Collections.Generic; using UnityEngine; [CreateAssetMenu(fileName = "New FlagList", menuName = "ScriptableObject/Flags/FlagList")] public sealed class FlagList : ScriptableObject { [SerializeField] List<FlagData> flags = new(); public List<FlagData> Flags { get { return flags; } } public void InitFlags() { foreach(FlagData f in flags) { f.InitFlag(); } } public void SetFlag(FlagData flag, bool value = true) { foreach(FlagData f in flags) { if(f == flag) { f.SetFlagStatus(value); return; } } } public bool GetFlagStatus(FlagData flag) { foreach (FlagData f in flags) { if (f == flag) { return f.IsOn; } } return false; } }
フラグ管理機能の使い方
では最後に上記で実装したフラグ管理機能の使い他をご説明します。
フラグ管理機能を使うために必要な準備
まずはフラグ一覧を登録する「FlagList」ファイルを作っておく必要があります。
プロジェクトビューの任意のフォルダで右クリック→「作成」→「ScriptableObject」→「フラグ」→「FlagList」から新しいFlagListを作成しておきましょう。
フラグの追加方法
新しいフラグを追加する方法は簡単で、「FlagData」ファイルを新規作成して先ほどのFlagListに登録するだけです。
フラグの変更・確認方法
次にフラグを変更したり、フラグの状態をチェックしたりしたい場合は
- FlagDataを直接参照する
- FlagListの関数を使う
のいずれかの方法で可能です。基本的にはFlagDataを直接参照する方法を使ったほうが簡単なのでそちらを多用することになると思います。
フラグをセーブしたい場合
最後にフラグをセーブする場合ですが、少し工夫が必要です。次の手順を参考に実装してください。
- まずセーブデータにbool型のリストを作っておく。
- 手順1のリストの要素数をFlagListの要素数と同じにする。
- FlagListに登録されているFlagDataのフラグの値を、リストの対応するインデックスの要素に転写する。
- セーブ処理を実行する。
ScriptableObjectはセーブデータに記録できないので、代わりにFlagDataの並び順のインデックスを元にしてセーブデータのリストにフラグの値を登録します。
FlagListの要素の並び順を変更するとセーブデータとの対応関係が崩れておかしなことになってしまいます。FlagListの並び順は絶対に変更しないようにしてください。
おわりに
以上、ScriptableObjectを使ったフラグ管理機能の作り方についてご説明しました。
フラグ管理ができるようになるとゲーム開発の幅が広がってさらに面白いゲームを作れるようになるので、ぜひ上記の内容を参考にしていただければと思います。
この記事がUnityでのゲーム開発のお役に立てば幸いです。