メインコンテンツまでスキップ
  • Action
  • EventHandler
  • Delegate
  • UnityEvent
  • Func

について

1. 通常の利用方法

  • Action:
    戻り値がなく、引数がない、もしくは1つ以上の引数を取ることができるデリゲートです。

  • EventHandler:
    標準的なC#のイベントシステムで、通常、イベント発生元 (sender) とイベントデータ (EventArgs) を渡します。

  • Delegate:
    カスタムのデリゲートを定義でき、任意の引数と戻り値を設定可能です。

  • UnityEvent:
    Unityの特有のイベントシステムで、インスペクターから設定可能なイベントです。デフォルトでは戻り値がなく、引数はオプションです。

  • Func:
    戻り値を持つデリゲートで、1つ以上の引数を取ることができます。



2. 引数を取る場合 (複数の引数を取る場合も含む)

各デリゲートやイベントで、引数を取る場合の例です。

Action

using System;
public Action<string, int> onAction;

void Start()
{
onAction += (message, number) => Debug.Log($"{message} - {number}");
onAction?.Invoke("Message", 42);
}
  • Action<T1, T2>のように複数の引数を取ることが可能です。


EventHandler

public event EventHandler<CustomEventArgs> onEvent;
public class CustomEventArgs : EventArgs { public string Message; public int Number; }

void Start()
{
onEvent += (sender, e) => Debug.Log($"{e.Message} - {e.Number}");
onEvent?.Invoke(this, new CustomEventArgs { Message = "Event message", Number = 42 });
}
  • EventArgsをカスタムして複数のデータを渡せます。

Delegate

public delegate void CustomDelegate(string message, int number);
public CustomDelegate onCustomEvent;

void Start()
{
onCustomEvent += (message, number) => Debug.Log($"{message} - {number}");
onCustomEvent?.Invoke("Delegate message", 42);
}
  • 任意のシグネチャでカスタムデリゲートを作成できます。

UnityEvent

using UnityEngine.Events;
public UnityEvent<string, int> onUnityEvent;

void Start()
{
onUnityEvent.AddListener((message, number) => Debug.Log($"{message} - {number}"));
onUnityEvent?.Invoke("UnityEvent message", 42);
}
  • UnityEvent<T1, T2>のように複数の引数を設定できます。インスペクターから引数を設定することも可能です。

Func

public Func<string, int, string> onFunc;

void Start()
{
onFunc += (message, number) => $"{message} - {number}";
string result = onFunc?.Invoke("Func message", 42);
Debug.Log(result);
}
  • Func<T1, T2, TResult>のように複数の引数を取ることができ、最後の型は戻り値です。

3. 戻り値を取る場合

ActionEventHandlerUnityEvent戻り値を持ちませんが、FuncやカスタムDelegateを使うことで戻り値を持たせることが可能です。

Func (戻り値あり)

public Func<string, int, string> onFunc;

void Start()
{
onFunc += (message, number) => $"{message} - {number}";
string result = onFunc?.Invoke("Func message", 42);
Debug.Log(result); // 出力: Func message - 42
}
  • Func戻り値を持つデリゲートで、最後の型パラメータが戻り値の型になります。

Delegate (戻り値あり)

public delegate int CustomDelegate(string message, int number);
public CustomDelegate onCustomEvent;

void Start()
{
onCustomEvent += (message, number) => message.Length + number;
int result = onCustomEvent?.Invoke("Hello", 5);
Debug.Log(result); // 出力: 10
}
  • カスタムデリゲートに戻り値を持たせることが可能です。

4. 違いや使い所についてのまとめ

特徴ActionEventHandlerDelegateUnityEventFunc
戻り値なしなし任意なしあり
引数最大16個EventArgsを使って渡す任意UnityEvent<T>形式で設定最大16個
インスペクター対応なしなしなしありなし
使いどころシンプルなイベント処理C#の標準的なイベントカスタム処理や複雑なパターンUnityエディターでイベント設定したい場合戻り値が必要な場合

使い所まとめ:

  1. Action:

    • シンプルなイベント処理や、戻り値が不要な場合に便利。
    • 引数を使った汎用的なイベント処理に向いています。
  2. EventHandler:

    • 標準的なC#イベント。複数のリスナーやイベント発生元、追加データ (EventArgs) を伝えたい場合に使用します。
    • 複雑なイベントのやり取りが必要な場面で使いやすいです。
  3. Delegate:

    • カスタムのデリゲートを定義したい場合。引数や戻り値を自由に定義でき、柔軟なイベント処理を構築できます。
    • 特定の処理に合わせたイベントや、シグネチャを細かくカスタマイズしたいときに適しています。
  4. UnityEvent:

    • Unityのエディターからも設定可能なイベントシステム。インスペクターからイベント設定ができるため、スクリプトに依存せずイベント処理を追加・管理したい場合に最適です。
    • UIやゲームオブジェクトのインタラクションに向いています。
  5. Func:

    • 戻り値が必要なイベントやデリゲートを使いたい場合に適しています。
    • 例えば、処理の結果を返したり、データを計算して返す必要がある場合に使います。

サンプルコード

ActionEventHandlerDelegateUnityEventFuncを使ったサンプルコード






1. Action の利用例

using UnityEngine;
using System;

public class ActionExample : MonoBehaviour
{
// 引数付きのAction (stringとintを受け取る)
public Action<string, int> onAction;

void Start()
{
// Actionにメソッドを登録
onAction += PrintMessage;

// Actionを実行
onAction?.Invoke("Hello from Action", 42);
}

void PrintMessage(string message, int number)
{
Debug.Log($"{message} - {number}");
}
}
  • この例では、Action<string, int>を使い、stringintを引数に取るイベントを作成しています。





2. EventHandler の利用例

using UnityEngine;
using System;

public class EventHandlerExample : MonoBehaviour
{
// カスタムEventArgsクラス
public class CustomEventArgs : EventArgs
{
public string Message { get; set; }
public int Number { get; set; }
}

// EventHandler (CustomEventArgsを使う)
public event EventHandler<CustomEventArgs> onEvent;

void Start()
{
// EventHandlerにメソッドを登録
onEvent += OnEventTriggered;

// イベントを発生させる
onEvent?.Invoke(this, new CustomEventArgs { Message = "Hello from EventHandler", Number = 42 });
}

void OnEventTriggered(object sender, CustomEventArgs e)
{
Debug.Log($"{e.Message} - {e.Number}");
}
}
  • この例では、EventHandlerを使い、カスタムEventArgsを渡してデータをやり取りしています。





3. Delegate の利用例

using UnityEngine;

public class DelegateExample : MonoBehaviour
{
// カスタムデリゲート (stringとintを受け取り、intを返す)
public delegate int CustomDelegate(string message, int number);

// カスタムデリゲートの変数
public CustomDelegate onCustomEvent;

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

// デリゲートを実行し、結果を取得
int result = onCustomEvent?.Invoke("Hello from Delegate", 42) ?? 0;
Debug.Log($"Result: {result}");
}

// stringの長さとnumberを足して返す
int CalculateStringLengthAndAddNumber(string message, int number)
{
return message.Length + number;
}
}
  • この例では、カスタムデリゲートを使い、stringintを受け取り、処理結果としてintを返しています。





4. UnityEvent の利用例

using UnityEngine;
using UnityEngine.Events;

public class UnityEventExample : MonoBehaviour
{
// 引数付きのUnityEvent (stringとintを受け取る)
public UnityEvent<string, int> onUnityEvent;

void Start()
{
// UnityEventにリスナーを登録
onUnityEvent.AddListener(PrintMessage);

// UnityEventを実行
onUnityEvent?.Invoke("Hello from UnityEvent", 42);
}

void PrintMessage(string message, int number)
{
Debug.Log($"{message} - {number}");
}
}
  • この例では、UnityEvent<string, int>を使い、引数を渡してイベントを処理しています。インスペクターからもリスナーを設定できます。





5. Func の利用例

using UnityEngine;
using System;

public class FuncExample : MonoBehaviour
{
// 戻り値付きのFuncデリゲート (stringとintを受け取り、stringを返す)
public Func<string, int, string> onFunc;

void Start()
{
// Funcにメソッドを登録
onFunc += FormatMessage;

// Funcを実行し、戻り値を取得
string result = onFunc?.Invoke("Hello from Func", 42);
Debug.Log(result);
}

// stringとintを受け取り、整形されたメッセージを返す
string FormatMessage(string message, int number)
{
return $"{message} - {number}";
}
}
  • この例では、Func<string, int, string>を使い、引数を受け取って整形し、戻り値を返しています。





6. それぞれの使い所のまとめ

種類用途Unityインスペクターで設定可能戻り値引数
Actionシンプルなイベント処理に最適。戻り値は不要。なしなし最大16個
EventHandler標準的なイベントシステム。イベント発生元やデータの管理が必要な場合。なしなしEventArgsで管理
Delegate自由な引数・戻り値を設定可能なカスタムイベント。なしあり任意
UnityEventUnityエディターでイベントを設定したい場合。UIなどのイベント管理に最適。ありなし最大4つまで
Func戻り値が必要な場合に使用。結果を返す処理に向いている。なしあり最大16個










ファイルを分けて書く

実際の開発では、スクリプトが別ファイルに分かれることが多いです。特に、以下のようなケースではファイルを分けることが推奨されます。

1. クラスやデリゲートの再利用

  • カスタムデリゲートEventArgsを定義した場合、複数のクラスで再利用したいことが多いです。そのため、これらの定義を別のファイルに分けておくと、管理や拡張が容易になります。

2. 機能ごとに分ける

  • 例えば、ある機能に特化したイベントを管理するクラスと、他のクラスでイベントを呼び出す側(Invoker)、または受け取る側(Listener)が異なる場合は、それぞれ別のスクリプトに分けます。これにより、クラスごとの責任を明確にし、コードの保守性を高めることができます。

3. Unityの実践的なプロジェクト構成

  • Unityプロジェクトでは、スクリプトを機能ごとに整理することが重要です。通常、次のような構成にすることが多いです。
    • イベントデリゲートやカスタムクラスの定義は別ファイルにまとめる。
    • メインのゲームロジックやイベントの呼び出しは個別のクラスにする。
    • イベントリスナーやイベント発生元のクラスは、別々のスクリプトファイルに分ける。





実際にファイルを分けた例

1. CustomEventArgs.cs

using System;

public class CustomEventArgs : EventArgs
{
public string Message { get; set; }
public int Number { get; set; }
}
  • カスタムEventArgsの定義は別ファイルに分け、複数のスクリプトから利用できるようにします。

2. EventInvoker.cs(イベントを発生させるクラス)

using UnityEngine;

public class EventInvoker : MonoBehaviour
{
// イベントの宣言
public event EventHandler<CustomEventArgs> onEvent;

void Start()
{
// イベントを発生させる
TriggerEvent();
}

void TriggerEvent()
{
if (onEvent != null)
{
onEvent.Invoke(this, new CustomEventArgs { Message = "Hello from Invoker", Number = 99 });
}
}
}
  • このスクリプトはイベントを発生させる役割を持つクラスです。

3. EventListener.cs(イベントを受け取るクラス)

using UnityEngine;

public class EventListener : MonoBehaviour
{
public EventInvoker invoker;

void OnEnable()
{
if (invoker != null)
{
// イベントのリスナーを登録
invoker.onEvent += OnEventTriggered;
}
}

void OnDisable()
{
if (invoker != null)
{
// イベントのリスナーを解除
invoker.onEvent -= OnEventTriggered;
}
}

void OnEventTriggered(object sender, CustomEventArgs e)
{
Debug.Log($"Received event: {e.Message} - {e.Number}");
}
}
  • イベントを受け取るリスナーは別ファイルにして、イベント発生元と疎結合にします。
  1. コードの再利用がしやすくなり、複数のスクリプトから共通の機能を利用可能。
  2. 責任の分離により、各クラスの役割が明確になり、保守性が向上。
  3. イベント管理が整理され、プロジェクト全体の構成がスッキリとします。