Unityでゲームを作っていると、結構な頻度で
という場面に出くわすときがあります。これは例えばゲームオブジェクトA・Bがあり、A→Bへの参照がないにも関わらずBが持つ処理をAから実行したい…というような場合ですね。
普通に考えるとこのような場面では、処理を指示する側のゲームオブジェクトに、指示される側のゲームオブジェクトの処理への参照を持たせないといけないと思うことでしょう。しかし
- ScriptableObject
- UnityEvent
の2つを駆使すると、なんと参照がないにもかかわらず他方のゲームオブジェクトを操作することができるのです。一体どうやるのか気になりませんか?
そこでここでは、上記の2つの機能を駆使して全く関係のないゲームオブジェクト同士を連携させる方法をご紹介しますね。
参照のないゲームオブジェクトの処理を実行するためのC#スクリプト
では早速ですが、参照していないゲームオブジェクトの処理を実行するためのC#スクリプトを掲載します。この連携システムは次の2つのC#スクリプトで構成されます。
- GameEvent:イベントを発火させるためのScriptableObject
- GameEventListener:イベント発火時に実行する操作を登録するためのクラス
GameEvent(ScriptableObject)
using System; using System.Collections.Generic; using UnityEngine; namespace KurokumaSoft { [CreateAssetMenu(fileName = "New GameEvent", menuName = "GameEvent")] public class GameEvent : ScriptableObject { [NonSerialized] private List<GameEventListener> listeners = new List<GameEventListener>(); public void Raise() { for (int i = listeners.Count - 1; i >= 0; i--) { listeners[i].OnEventRaised(); } } public void RegisterListener(GameEventListener listener) { listeners.Add(listener); } public void UnregisterListener(GameEventListener listener) { listeners.Remove(listener); } public void ClearAllListeners() { listeners.Clear(); } } }
まず、これはイベントを発火させるためのScriptableObjectです。任意のタイミングでこのScriptableObjectのRaise関数を呼び出すと、後述するGameEventListenerに登録したイベントが一斉に呼び出されるという仕組みになっています。
この仕組みにScriptableObjectを使っている理由は、ScriptableObjectはシーンに依存しないためどこからでもこのScriptableObjectへの参照を登録できるからです。もしこれがシーンに依存した形になると結局特定のゲームオブジェクトへの参照が必要になるため、「参照のないゲームオブジェクト同士を連携させる」という目的を達成できなくなってしまいます。
なお「ScriptableObjectって何?」という方は下記の記事も併せてご覧頂くと理解が深まると思います。
GameEventListener
using UnityEngine; using UnityEngine.Events; namespace KurokumaSoft { public class GameEventListener : MonoBehaviour { [SerializeField] GameEvent gameEvent = null; public UnityEvent response = new UnityEvent(); void OnEnable() { gameEvent.RegisterListener(this); } void OnDisable() { gameEvent.UnregisterListener(this); } public void OnEventRaised() { response.Invoke(); } } }
次にこちらは、UnityEventを使ってGameEvent発火時に実行する操作を登録するだけのシンプルなクラスです。先ほどのGameEventクラスのRaise関数が実行されたタイミングで何かをさせたいゲームオブジェクトにアタッチし、処理を登録する形になります。
上記のC#スクリプトの使い方
さて上記のスクリプトはシンプルではありますが、そうは言っても具体的な使い方が今一つピンとこない方もいらっしゃるかと思います。そこで最後に上記のC#スクリプトの使い方の手順をご紹介させて頂きます。
主な手順は次の3ステップです。
- 適当なフォルダに新しいGameEventを作成する
- 手順1のイベントが発火したときに何かさせたいゲームオブジェクトに「GameEventListener」コンポーネントをアタッチし、処理を登録する
- 適当なゲームオブジェクトのスクリプトにおいて、任意のタイミングで手順1のScriptableObjectのRaise関数を呼び出すようにする
手順1:新しいGameEventを作成する
まずは新しいGameEventを作成する必要があります。プロジェクトビューの何もない場所で右クリックすると、「作成」→「GameEvent」という項目が出るようになると思うのでそれを選択すれば新しいGameEventファイルが作られます。
ここでは例として、「ボスを撃破したときの処理を実行するイベント」に使うことにしてみましょう。デフォルトの名前では何のイベントなのかが分かりにくいので「BossBattleEnd」と変更しておきます。
手順2:GameEventListenerをアタッチする
次に、ボスを撃破したときの処理を行うゲームオブジェクトにGameEventListenerをアタッチし、必要な処理を登録します。
例えばボスを撃破したときにボス専用のHPバーを消したいのであれば、そのHPバーを持つキャンバスにGameEventListenerをアタッチして次のように設定を行います。
手順3:GameEventのRaise関数を呼び出すようにする
最後にボスを撃破したタイミングで、GameEventのRaise関数が呼び出されるようになればOKです(※この処理はゲームによって様々だと思うのでご自身で考えてください)。
Raise関数が呼び出されると、関連するGameEventListenerに登録されたすべての処理が実行されます。
おわりに
以上、参照のない別のゲームオブジェクトの処理を実行させる方法をご紹介しました。
この仕組みは結構ややこしくて最初は「なんだコレ?」と思うかもしれません。しかし使い方を理解できればとんでもなく便利なので、ぜひ上記の内容を参考にしていただき理解していただければと思います。
この記事がUnityでのゲーム開発のお役に立てば幸いです。