いんでぃーづ

個人でゲーム開発してる上で吸収したモノたち紹介。UnityからGIMPまでなんでも。デザインとかゲーム論まで語っちゃうよ的なアレ。

MENU

Unity : グローバルなイベント通知の仕組み(EventBus)を自前実装する [C#]

{スポンサーリンク}
{スポンサーリンク}

例えば、敵を倒したタイミングで、プレイヤー、UI、他の敵が何らかのアクションを起こしたい場合、どのような経路でイベントを通知するか悩みます。

敵を各オブジェクトが監視したりするのは、参照が絡み合って管理がめんどうくさいです。

f:id:sugar_affordance:20170526120444j:plain

そこで便利なのがEventBusです。

元はAndroidなどに存在するライブラリです。
発生したイベントをグローバルなバス(伝送経路的な意味)を伝って、それを受け取りたいオブジェクト全部に伝えるというもの。

f:id:sugar_affordance:20170526120554j:plain

今回はこれを実装してみます。

EventBusクラスを作成

シングルトンクラス にします。
MonoBehaviourの継承は不要です。

・EventBus.cs
*ソース中Enemyクラスは敵につけているスクリプトとします

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EventBus {

    // シングルトンにする
    static EventBus instance;
    public static EventBus Instance {
        get {
            if (instance == null) {
                instance = new EventBus ();
            }
            return instance;
        }
    }
    private EventBus() {
    }

    // 通知受け取りデリゲート定義
    public delegate void OnDefeatEnemy(Enemy enemy);
    event OnDefeatEnemy _OnDefeatEnemy;


    // 通知受け取り登録
    public void Subscribe(OnDefeatEnemy onDefeatEnemy) {
        _OnDefeatEnemy += onDefeatEnemy;
    }

    // 通知受け取り解除
    public void Unsubscribe(OnDefeatEnemy onDefeatEnemy) {
        _OnDefeatEnemy -= onDefeatEnemy;
    }

    // 通知実行
    public void NotifyDefeatEnemy(Enemy enemy) {
        if (_OnDefeatEnemy != null)
            _OnDefeatEnemy (enemy);
    }
}

通知を受け取るオブジェクトで、下のように実装します。

public class Receiver : MonoBehaviour {

    void Start () {
        // 通知受け取り登録
        EventBus.Instance.Subscribe ((EventBus.OnDefeatEnemy)OnDefeatEnemy);
    }

    void OnDestroy() {
        // 通知受け取り解除
        EventBus.Instance.Unsubscribe ((EventBus.OnDefeatEnemy)OnDefeatEnemy);
    }

    void OnDefeatEnemy(Enemy enemy) {
        Debug.Log ("敵を倒した!");
    }
}

通知を送信する場合、倒された敵のスクリプトでNotifyを呼び出します。

public class Enemy : MonoBehaviour {

    void OnDefeated() {
        // 敵を倒した通知
        EventBus.Instance.NotifyDefeatEnemy (this);
    }
}

利点

まずMonoBehaviourを継承していないので、シーン内にオブジェクトとして置いておく必要がありません
また、通知を受け取りたいオブジェクト内で登録と解除を完結できるので、オブジェクト間の依存性を極力減らすことができます。 新しい種類の通知を受け取りたい場合、デリゲートを増やしていけばいくらでも対応可能です。 (ファイル肥大化が嫌ならこちらを参考に)

注意

Unsubscribeしないでオブジェクト削除された場合、削除されたはずのスクリプトの関数が呼ばれてしまうので、ちゃんと後処理はやりましょう。
Subscribe 時に関数をキャストしていますが、やらなかったらどうなるかは…おたのしみに。


図の素材をお借りしました。

【Rド】→RPG2003ドット絵素材

Amazon.co.jpアソシエイト