本ページにはプロモーションが含まれます
Unity

【Unity】DelegateでFSM作るよ

この記事は約6分で読めます。

はじめに

Unity C# の delegate は、関数やメソッドへの参照を格納することができる型で、イベントやコールバックの実装に便利な機能です。C# ではデリゲートを使うと、特定の条件下で実行する関数を動的に選択したり、複数の関数をまとめて呼び出したりできます。これは、コールバック処理やイベント処理によく用いられます。

デリゲートとは?

public delegate void MyDelegateType(); // 引数なし、戻り値なしのデリゲート型を宣言
public class MyClass
{
    public MyDelegateType myDelegate; // デリゲート型の変数を宣言
    public void MyMethod()
    {
        if (myDelegate != null)
            myDelegate(); // デリゲートに代入されたメソッドを呼び出す
    }
}

なるほどわからん。

デリゲートの使用例

using UnityEngine;
public class DelegateExample : MonoBehaviour
{
    // 戻り値なし、引数なしのデリゲート型を定義
    public delegate void SimpleDelegate();
    // デリゲート変数の宣言
    public SimpleDelegate myDelegate;
    void Start()
    {
        // デリゲート変数にメソッドを代入
        myDelegate = MyFunction;
        // デリゲートを実行
        myDelegate?.Invoke(); // 安全に呼び出すために null チェックも含める
    }
    void MyFunction()
    {
        Debug.Log("MyFunction is called!");
    }
}

要は変数に関数入れて実行できるってこと。

この例では、SimpleDelegate というデリゲート型を宣言し、それを通して MyFunction を呼び出しています。

デリゲートを使う理由

  • コールバック処理: 処理が完了した後に別の処理を実行したい場合に便利です。
  • イベント処理: Unityの UnityEvent や Action 型もデリゲートの一種で、イベントを設定したり、複数の関数をまとめて実行するのに役立ちます。
  • 動的な関数呼び出し: 実行時にメソッドを動的に変更したり、組み合わせたりすることができます。

イベントの実装にも使われる

Unity では delegate と event を組み合わせてイベント処理を行うことも一般的です。たとえば、プレイヤーの体力が尽きたときなどに特定のメソッドを呼び出す場合、デリゲートを使って柔軟にイベントを設定することができます。

Action と Func の簡単な使い方

C# の Action と Func もデリゲートの一種で、簡単に使える汎用デリゲートです。Action は戻り値がないメソッド、Func は戻り値があるメソッドに適しています。

// Action の例(引数なし、戻り値なし)
Action action = () => Debug.Log("Action called");
action.Invoke();
// Func の例(引数なし、戻り値あり)
Func<int> func = () => 42;
int result = func.Invoke();
Debug.Log(result);

なるほどわからん。

結果

  • デリゲートはメソッドの参照 を格納する型で、柔軟なメソッド呼び出しができる。
  • イベントやコールバック を作成するのに便利で、条件に応じた処理を設定できる。
  • Unity のスクリプトでも 柔軟な動的処理 やイベントシステムの実装に活用される。

デザインパターンと利便性についてなどあるが、なるほどわからん。

FSM作るよ

using UnityEngine;
public class SimpleFSM : MonoBehaviour
{
    // 各状態の処理を表すデリゲート定義
    public delegate void State();
    
    // 現在の状態を保持するデリゲート変数
    private State currentState;
    void Start()
    {
        // 初期状態を設定
        TransitionTo(IdleState);
    }
    void Update()
    {
        // 現在の状態を実行
        currentState?.Invoke();
    }
    // 状態遷移を行うメソッド
    void TransitionTo(State newState)
    {
        currentState = newState;
    }
    // 各状態の処理
    void IdleState()
    {
        Debug.Log("Idle");
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // ジャンプ状態に遷移
            TransitionTo(JumpState);
        }
    }
    void JumpState()
    {
        Debug.Log("Jumping");
        if (Input.GetKeyDown(KeyCode.LeftShift))
        {
            // 走る状態に遷移
            TransitionTo(RunState);
        }
    }
    void RunState()
    {
        Debug.Log("Running");
        if (Input.GetKeyDown(KeyCode.S))
        {
            // アイドル状態に遷移
            TransitionTo(IdleState);
        }
    }
}

実装の詳細

  1. 状態デリゲート State:
    • State は、各状態のメソッドシグネチャとして void 戻り値、引数なしのデリゲートです。
    • IdleState, JumpState, RunState は、この State デリゲートと一致するシグネチャを持つメソッドとして定義されています。
  2. currentState デリゲート変数:
    • 現在の状態を表し、TransitionTo メソッドで更新されます。
    • Update メソッドで毎フレーム currentState?.Invoke() により現在の状態メソッドを呼び出します。
  3. 状態遷移メソッド TransitionTo:
    • 引数で渡された State デリゲートを currentState に代入し、状態遷移を行います。
  4. 各状態メソッド:
    • IdleState, JumpState, RunState の各メソッドがそれぞれの状態における処理を実行し、特定のキー入力に基づいて次の状態へと遷移します。

拡張方法

この構造は拡張しやすく、以下のように使うことができます:

  • 新しい状態の追加:
    • 例えば「しゃがみ状態」を追加する場合、新しいメソッド CrouchState を定義し、必要な条件で TransitionTo(CrouchState); とするだけで対応できます。
  • 汎用的なパラメータの管理:
    • 状態が特定の情報(たとえば体力やスタミナ)を参照する必要がある場合、クラスのフィールドにパラメータを追加して、各状態メソッドで利用できます。

まとめ

  • シンプルで柔軟: 状態の追加・削除が容易で、動作もわかりやすい。
  • パフォーマンスに優れる: デリゲートを用いてメソッド参照を保持するため、処理のオーバーヘッドが少ない。
  • Unity のフレームごとに変化する状態に最適: Update で呼び出すことで、フレームごとの状態確認と切り替えが自然に行えます。

デリゲートを使った FSM は、簡潔でわかりやすく、Unity のスクリプト内で動作を効率的に管理できます。

おわりに

今作ってるゲームの、タイマー終わったらキューに実行スタックしていくのに使えそう。