見出し画像

短編RPG制作(Unity2D)【第51回】勉強:コールバック関数


コールバック関数

クラス同士の関係を見直す際に使ったものです。
勉強した内容を自分なりにまとめてみます。
私は「UnityEvent」を使いましたが、「UnityAction」「delegate」を用いた記述もあるようなので使い分けてください。
ここではざっくりした説明に終始するつもりです。他の記述でも基本は同じ……はずです。

構文

呼び出されるクラス「Manager」(関数を記述するクラス)

class Manager()
{
    private Enemy _enemy // 参照取得

    private void Start()
    {
        _enemy.onChildDestroy.AddListener(Result); // コールバックの設定
    }

    // Invoke時に実行されるメソッド
    private void Result() {
    }
}

呼び出すクラス「Child」

using UnityEngine.Events;

class Child()
{
    // インスタンス(関数の入れ物)生成
    private UnityEvent onChildDestroy = new UnityEvent();

    private void OnDestroy()
    {
        onChildDestroy.Invoke(); // オブジェクト消滅時の処理を呼び出し
    }
}

説明

とても簡略化すると、下位のクラスから上位のクラスのメソッドを呼び出しています。下位のクラスが「メソッドを実行してください」と通知している感じですね。
細かく見ていきます。

  1. 「Parent」に呼び出したい関数を記述する

  2. 「Child」にインスタンス(関数の格納場所)を生成

  3. 「Parent」から「Child」に関数を渡す

  4. 「Child」で渡された関数を実行

流れとしてはこのようになります。
例の内容は私が書いているスクリプトに寄せてみました。

イメージ

(個人的な意見が多くあります。間違っているかもしれませんのでご参考までに。)
まず、「こんな面倒くさいことしなくても「Enemy」側にResultメソッドを書けば良いのでは」と思うかもしれません。
コールバックを実装することで得られる利点を、例で説明していきます。

今回は、Resultメソッドで「Playerクラスに経験値やドロップアイテムを渡す」「画面の表示を更新する」処理を実装したいとします。
「Enemy」にResultメソッドがある場合を考えます。

敵の死亡時処理をEnemyに実装

この場合、EnemyクラスがPlayerクラスとInterfaceクラス(UIにアタッチするクラスをイメージ)にアクセスして変数を書き換える必要があります。
ここで生じる疑問が、「これはEnemyクラスが請け負うべき処理なのか?」ということです。ドロップアイテムの処理はまだしも、画面更新まで敵キャラクターが行うのは責任が重すぎる気がします。

そこで、処理を適切に分割するために、クラス全体を管理するクラス「Manager」を作成してみることにします。各クラスにまたがる処理などはこの「Manager」に任せることにします。

Managerクラスを追加

もちろんResultメソッドはManagerクラス内に記述します。
……しかし、敵キャラクターが死亡したタイミングを知っているのはEnemyクラスなので、Enemy側から呼び出さなければ適切なタイミングでResultメソッドを実行できません。
これは上位のクラスが下位のクラスに依存している状態であり、好ましくありません。
そこで活躍するのがコールバックの仕組みです。

関数を渡す

実行する内容としてはほとんど同一ですが、コールバック関数を用いると、EnemyクラスはManagerクラスの参照を取得する必要がありません。
実行のタイミングだけを通知して、処理自体はManagerクラス側に任せることができます。
この依存性の逆転こそがコールバック関数の最大のメリットだと思っています。

まとめ

私のコールバックに対する認識を書きました。
間違っているところもあると思いますので、その際はご指摘いただけると助かります。

この記事が気に入ったらサポートをしてみませんか?