Unityで複数のGameObjectに一斉に通知する方法 Notification, EventBus

はじめに

UnityでGameObjectAがGameObjectBに何かしらのイベントを渡す場合、何パターンか、方法があります。

  • ExecuteEvent (SendMessage)
  • ラムダ式 (delegate通知)
  • System.Action (delegateとほぼ同意)

ただ、上記の場合、GameObjectAがGameObjectBの存在を知っていないと行けないですし、2つのGameObject(クラス)がつながっている必要があります。
この場合やっかいなのが、GameObjectAがGameObjectBの存在を知らなくても、通知をイベントを送りたい時です。

実例で言うと、先日unity 1 weekゲームジャムで作ったちいさなぎんがでは、星と星が結合し1つの星になる際に、ログを表示するのですが、全ての星とログウィンドウとを結びつけるとあまりにも冗長です。
そこでiOSの場合だとNotification、AndroidだとEventBusという考え方を使って、この問題を解決したいと思います。

Notification, EventBusとは?

Observerっぽい考え方です。
1つの代表する通知を管理するクラスが居て、そのクラスに対して通知送信者は通知を送り、通知受信者は通知を受信するよう登録します。

iOSで言うと、Notificationライク考え方の代物で、AndroidだとEventBusですね。

イメージとしては以下な感じです。(上記のちいさなぎんがの時の設計です

f:id:project-unknown:20171210144846p:plain

通知管理部分が、通知を受信するObject, 登録するObjectとの窓口を担います。
各通知を送りたいObjectが通知管理クラスに対し、通知を送ります。
ログウィンドウが、通知購読を行っており、Objectから通知が届いたら、通知管理クラスが通知購読を行っているログウィンドウに対し通知を行う流れです。

実際に作ってみる

通知管理

考え方はシングルトンとObserverの一種なので至ってシンプルです。

ここでは、単純に通知を送る(NotifySomeEvent)と、引数付きの通知を送る(NotifySomeEventWithArgs)を用意しました。
また、通知購読をしているクラスへの受付としてはDelegateとしています。
これは単純に使い勝手の問題なので、ラムダ式で繋いでしまっても問題ありません。

gist.github.com

通知購読

通知管理クラスは出来たので通知購読を行います。
まずは、引数無し通知を受け取る例です。

void SubscribeSample() {
    Notification.Instance.SubscriveSomeEvent((Notification.OnSomeEvent)OnSomeEvent);
}
void OnSomeEvent() {
   /*処理*/
}

NotificationクラスにあるSubscriveSomeEventクラスにDelegateを登録しているだけです。
これで、Notificationクラスに対して通知が行われた際に、このDelegateが呼び出されます。

次に実際に通知を登録する際は以下の通り

Notification.Instance.NotifySomeEvent();

通知と言ってもメソッドを呼び出すだけです。

もう通知が不要になったら、以下のように購読解除を行います (購読解除を行わないとオブジェクトが解放されないため、確実に解除してください)

void UnSubscribeSample() {
  
  Notification.Instance.UnSubscribeSomeEvent((Notification.OnSomeEvent)OnSomeEvent);
}

ちなみに、引数付きの通知は以下の様にやり取りを行います。

通知購読

void SubscribeSample() {
    Notification.Instance.SubscriveSomeEventWithArgs((Notification.OnSomeEventWithArgs)OnSomeEvent);
}
void OnSomeEventWithArgs(string text) {
   /*処理*/
}

通知送信

Notification.Instance.NotifySomeEventWithArgs("hogehoge");

注意点

Objectのハブとして通知管理クラスを立てているので、Object毎に依存関係がなくなり非常に使いやすいのですが、 Debugが非常にやりづらいです
1,2個のオブジェクトであれば、何か問題があっても直ぐに突き止められますが、オブジェクトの数が数百を超える場合は何がなんだかわからなくなります。(それに1,2個のメッセージのやり取りなら、冒頭で記載した通り、ExecuteEventの手法を取ったほうが絶対良いです)
なので、使うなら、自動的にObjectに識別子を付与した通知を行うように工夫すると良いかもしれません。