シンプルなUnityサンプル:デリゲートを使ったイベント管理

Unityでデリゲートを使ったサンプル

この例では、プレイヤーが特定のキーを押すとメッセージを表示する処理をデリゲートで管理します。

1. スクリプト全体

using UnityEngine;

public class DelegateExample : MonoBehaviour
{
    // デリゲートの定義
    public delegate void PlayerAction();

    // デリゲートのインスタンス
    public PlayerAction onPlayerAction;

    void Start()
    {
        // デリゲートにメソッドを登録
        onPlayerAction += ShowMessage;
        onPlayerAction += LogAction;

        Debug.Log("デリゲートにメソッドを登録しました。");
    }

    void Update()
    {
        // スペースキーを押したときにデリゲートを呼び出す
        if (Input.GetKeyDown(KeyCode.Space))
        {
            onPlayerAction?.Invoke(); // nullチェックしてから実行
        }
    }

    // メッセージを表示するメソッド
    void ShowMessage()
    {
        Debug.Log("プレイヤーがアクションを実行しました!");
    }

    // ログを記録するメソッド
    void LogAction()
    {
        Debug.Log("Action logged at: " + Time.time);
    }
}

2. 動作概要

デリゲートの定義と登録:

  • PlayerAction というデリゲートを定義します。
  • onPlayerAction というデリゲートのインスタンスを作成します。
  • Start メソッドで、onPlayerAction に複数のメソッド(ShowMessageLogAction)を登録します。

イベントのトリガー:

  • Update メソッド内で、Space キーが押されたときに onPlayerAction を呼び出します。
  • 登録されたすべてのメソッド(ShowMessageLogAction)が順に実行されます。

デリゲートの活用:

  • デリゲートを使うことで、処理を柔軟に追加・削除できます。

3. 実行方法

  1. Unityプロジェクト内で新しいC#スクリプトを作成し、名前を DelegateExample にします。
  2. 上記のコードをコピーしてスクリプトに貼り付けます。
  3. 空のGameObjectをシーンに配置し、DelegateExample スクリプトをアタッチします。
  4. 再生ボタンをクリックし、ゲームを実行します。
  5. キーボードの スペースキー を押すと、コンソールに次のメッセージが表示されます:

「プレイヤーがアクションを実行しました!」
「Action logged at: (現在の時間)」


4. 応用例

プレイヤーのアクションに応じて効果を変更

  • 他のスクリプトからもデリゲートにメソッドを登録・削除できます。
  • 例えば、プレイヤーのレベルに応じて異なる処理を登録することが可能です。

敵やアイテムのイベント管理

  • デリゲートを使えば、敵の死亡イベントやアイテム取得時の処理を効率的に管理できます。

5. 応用例コード:敵の死亡イベント

以下のコードは、敵が倒されたときにデリゲートでイベント処理を管理する例です。

using UnityEngine;

public class Enemy : MonoBehaviour
{
    public delegate void EnemyDefeated();
    public EnemyDefeated onEnemyDefeated;

    void Start()
    {
        onEnemyDefeated += GiveReward;
        onEnemyDefeated += LogDefeat;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.K)) // Kキーで敵を倒したと仮定
        {
            Debug.Log("敵が倒されました!");
            onEnemyDefeated?.Invoke(); // 登録された処理をすべて実行
        }
    }

    void GiveReward()
    {
        Debug.Log("報酬を獲得しました!");
    }

    void LogDefeat()
    {
        Debug.Log("敵が倒された時間: " + Time.time);
    }
}

このように、Unityでデリゲートを使うと、柔軟にイベントを管理でき、スクリプト間の依存を減らすことができます。ぜひプロジェクトに取り入れてみてください!

はい、FuncAction を使うことで、C#のデリゲートの記述を簡略化できます。FuncAction は汎用的なデリゲート型として用意されており、カスタムデリゲート型を定義する必要がなくなります。

以下に、先ほどのUnityの例を Action に置き換えたコードを示します。


UnityでActionを使ったサンプル

1. サンプルコード

using UnityEngine;

public class ActionExample : MonoBehaviour
{
    // Actionの定義(引数なし、戻り値なし)
    public Action onPlayerAction;

    void Start()
    {
        // Actionにメソッドを登録
        onPlayerAction += ShowMessage;
        onPlayerAction += LogAction;

        Debug.Log("Actionにメソッドを登録しました。");
    }

    void Update()
    {
        // スペースキーを押したときにActionを呼び出す
        if (Input.GetKeyDown(KeyCode.Space))
        {
            onPlayerAction?.Invoke(); // nullチェックしてから実行
        }
    }

    // メッセージを表示するメソッド
    void ShowMessage()
    {
        Debug.Log("プレイヤーがアクションを実行しました!");
    }

    // ログを記録するメソッド
    void LogAction()
    {
        Debug.Log("Action logged at: " + Time.time);
    }
}

2. 修正ポイント

  • カスタムデリゲート型(delegate int Operation(int x, int y);)の代わりに、Action を使用しています。
  • Action は、引数が不要で戻り値も不要なデリゲート型です。
  • デリゲートの onPlayerActionAction に置き換えました。

3. 実行方法

  1. Unityで新しいC#スクリプトを作成し、ActionExample という名前を付けます。
  2. 上記のコードをスクリプトに貼り付けます。
  3. 空のGameObjectを作成し、スクリプトをアタッチします。
  4. 再生ボタンを押し、スペースキーを押すと動作します。

応用例: Funcを使う

次に、Func を使って、引数や戻り値を持つメソッドをデリゲートで管理する例を示します。

サンプルコード

using UnityEngine;
using System;

public class FuncExample : MonoBehaviour
{
    // Funcの定義(引数: int, int / 戻り値: int)
    public Func<int, int, int> onCalculate;

    void Start()
    {
        // Funcにメソッドを登録
        onCalculate = Add;
        Debug.Log($"3 + 5 = {onCalculate(3, 5)}"); // 出力: 3 + 5 = 8

        onCalculate = Multiply;
        Debug.Log($"3 * 5 = {onCalculate(3, 5)}"); // 出力: 3 * 5 = 15
    }

    // 足し算
    int Add(int a, int b)
    {
        return a + b;
    }

    // 掛け算
    int Multiply(int a, int b)
    {
        return a * b;
    }
}

解説

  • Func<int, int, int> は、2つの引数(int 型)を受け取り、戻り値が int 型であるメソッドを保持する汎用デリゲート型です。
  • 必要に応じて、Func を使えば戻り値が必要なロジックも簡潔に管理できます。

どちらを使うべきか?

  • Action を使うべき場合:
  • 引数が不要で戻り値も必要ない場合。
  • 主にイベント処理(ボタンのクリックやプレイヤーの行動)に適している。
  • Func を使うべき場合:
  • 戻り値が必要な場合。
  • 計算やデータ処理に適している。

これらを使うことで、C#のデリゲートを簡単かつモダンに管理できます。ぜひUnityプロジェクトで試してみてください!

はい、デリゲートを使った処理はイベント(event キーワード)に置き換えることができます。イベントはデリゲートの仕組みをさらに安全に、かつ明確にしたものです。イベントを使うことで、特定の条件(例: ボタンが押された、敵が倒された)に応じて処理を通知することが簡単になります。

以下に、先ほどのサンプルコードをイベントに置き換えた例を示します。


Unityでイベントを使ったサンプル

1. サンプルコード

using UnityEngine;

public class EventExample : MonoBehaviour
{
    // イベントの定義(Actionを基にしたイベント)
    public event System.Action onPlayerAction;

    void Start()
    {
        // イベントにメソッドを登録
        onPlayerAction += ShowMessage;
        onPlayerAction += LogAction;

        Debug.Log("イベントにメソッドを登録しました。");
    }

    void Update()
    {
        // スペースキーを押したときにイベントを発火
        if (Input.GetKeyDown(KeyCode.Space))
        {
            onPlayerAction?.Invoke(); // イベントを発火(登録されたすべてのメソッドを実行)
        }
    }

    // メッセージを表示するメソッド
    void ShowMessage()
    {
        Debug.Log("プレイヤーがアクションを実行しました!");
    }

    // ログを記録するメソッド
    void LogAction()
    {
        Debug.Log("Action logged at: " + Time.time);
    }
}

2. 修正ポイント

event キーワードを使用:

  • デリゲートをイベントとして扱うために、event キーワードを使用しました。
  • event を使うことで、外部から直接イベントを発火させることを防ぎます。

デリゲート型として System.Action を利用:

  • デリゲート型 System.Action をベースにしたイベントとして onPlayerAction を定義しました。

イベントの発火(Invoke:

  • イベントはデリゲートと同様に ?.Invoke() を使って発火できます。

3. イベントを使う利点

1. 安全性の向上

イベントは、デリゲートと異なり外部から直接変更(削除や上書き)されることを防ぎます。

// 外部からイベントの登録・削除は可能
eventExample.onPlayerAction += ExternalMethod;

// しかし、直接上書きはできない(デリゲートなら可能)
eventExample.onPlayerAction = ExternalMethod; // コンパイルエラー

2. 明確な意図

イベントは「特定の条件が発生したときに通知する」という意図を明示できます。イベントという名前自体が、用途を説明しています。

3. 複数のリスナー

1つのイベントに複数のリスナー(メソッド)を登録できるため、柔軟な拡張が可能です。


4. 応用例:敵の死亡イベント

以下は、イベントを使って敵の死亡を通知する例です。

コード例

using UnityEngine;

public class Enemy : MonoBehaviour
{
    // 敵が倒されたときに発火するイベント
    public event System.Action onEnemyDefeated;

    void Update()
    {
        // Kキーで敵が倒されたと仮定
        if (Input.GetKeyDown(KeyCode.K))
        {
            Debug.Log("敵が倒されました!");
            onEnemyDefeated?.Invoke(); // イベントを発火
        }
    }
}

public class Player : MonoBehaviour
{
    public Enemy enemy;

    void Start()
    {
        // 敵の死亡イベントにメソッドを登録
        enemy.onEnemyDefeated += GainReward;
        enemy.onEnemyDefeated += ShowVictoryMessage;
    }

    void GainReward()
    {
        Debug.Log("プレイヤーは報酬を獲得しました!");
    }

    void ShowVictoryMessage()
    {
        Debug.Log("プレイヤーは敵を倒しました!");
    }
}

動作説明

Enemy クラス:

  • 敵が倒されたときに onEnemyDefeated イベントを発火します。

Player クラス:

  • プレイヤーが EnemyonEnemyDefeated イベントにメソッドを登録します。
  • イベントが発火すると、登録されたメソッドがすべて実行されます。

5. イベントを使った拡張性の高い設計

イベントを使うことで、以下のような拡張が容易になります。

1. 複数のリスナーを追加

  • 複数のスクリプトで同じイベントをリッスン可能。

2. 条件に応じた通知

  • イベントを発火する条件を柔軟にカスタマイズ。

3. スクリプト間の依存を減らす

  • イベントを使うことで、スクリプト間で直接依存する必要がなくなります。

まとめ

  • イベントはデリゲートの仕組みをさらに安全かつ柔軟にしたもの。
  • Unityでイベントを使うことで、プレイヤーアクションや敵の死亡などのゲームイベントを簡潔に管理できます。
  • イベントはリスナーを登録・削除できるため、拡張性の高い設計が

デリゲート

Posted by hidepon