Unityで使えるイベント
Unityで使えるイベントには、UnityイベントとC#のイベントの2つがあります。
Unityイベントでよく使われるのは Button の OnClick で、インスペクターからメソッドを登録できます。
ここでは、ボタン以外で自分でイベントを作って扱う方法を、UnityイベントとC#のイベントの両方で説明します。最後に2つの使い分けもまとめます。
※この記事は Unity 6.6(6000.6系)に合わせて更新しています。キー入力には Input System を使っています。
- 1. Unityイベントチュートリアル
- 1.1. 新しくUnityEventTestプロジェクトを作成します。
- 1.2. シーンをUnityEventSceneとして、保存します。
- 1.3. Cubeオブジェクトを作成します。
- 1.4. PlayerController_UnityEventスクリプトを作成します。
- 1.5. CubeAction_UnityEventスクリプトを作成します。
- 1.6. Playerゲームオブジェクトに2つのスクリプトをアタッチします。
- 1.7. イベントを登録します
- 1.8. 実行してみましょう
- 1.9. objAにアタッチされているスクリプト
- 1.10. objBのスクリプト(インスペクターで登録する場合)
- 1.11. objBのスクリプト(コードで登録する場合)
- 1.12. イベントに引数を取りたい場合
- 1.13. イベントを登録します
- 2. C#イベントチュートリアル
- 3. UnityイベントとC#イベントの使い分け
- 4. おすすめ動画
- 5. ドキュメント
Unityイベントチュートリアル
チュートリアルを通して、検証してみましょう
新しくUnityEventTestプロジェクトを作成します。
Unity Hub で「Universal 3D」テンプレートを選んでプロジェクトを作成します。
Unity 6 のテンプレートから作成したプロジェクトには、キー入力に使う Input System パッケージが最初から入っています。
シーンをUnityEventSceneとして、保存します。
あとで、C#イベントも作成するため、シーンを分けます。
Cubeオブジェクトを作成します。
名前をPlayerにします。
PlayerController_UnityEventスクリプトを作成します。
このコードは、スペースキーを押下されたらメソッドを呼び出すものですが、どのメソッドが呼び出されるのかを記述していません。また、どこから呼び出されたのかもわかりません。スペースキーを押下すると何かのメソッドが呼び出されることがこのコードの目的になります。
このようなコードは、結びつきが弱い(依存性が低い)コードと言います。
コード
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
public class PlayerController_UnityEvent : MonoBehaviour
{
public UnityEvent OnCubeAction;
private void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
OnCubeAction?.Invoke();
}
}
}
コードの説明
イベントが発生したときに呼び出すメソッドを登録しておくメンバーを宣言します。
UnityEvent は、呼び出したいメソッドを複数まとめて登録しておける Unity のクラスで、ここでは「戻り値なし、引数なし」のメソッドを登録できます。
C# のデリゲートと似た役割ですが、UnityEvent 自体はデリゲートではなくクラスです。public にしておくと、インスペクターからメソッドを登録できます。
public UnityEvent OnCubeAction;
?は、「もしnullでなければ」を略するもので、if(OnCubeAction != null)と同じです。
Invoke()で、メソッド(OnCubeAction)を呼び出します。
スペースキーを押下すると実行されます。
OnCubeAction?.Invoke();
スペースキーが押された瞬間を判定します。Input System の書き方で、押されたフレームだけ true になります。
if (Keyboard.current.spaceKey.wasPressedThisFrame)
以前のUnityでは Input.GetKeyDown(KeyCode.Space)(旧Input Manager)と書いていましたが、Unity 6 の新規プロジェクトでは Input System が既定のため、旧方式のままだと実行時にエラーになります。Input System の基本はUnity 6でキー入力による移動を実装する ― Input Systemの基本で解説しています。
CubeAction_UnityEventスクリプトを作成します。
コード
using UnityEngine;
public class CubeAction_UnityEvent : MonoBehaviour
{
public void ColorChange()
{
GetComponent<Renderer>().material.color = Color.green;
}
public void Rotate()
{
transform.Rotate(Vector3.forward * 10);
}
}
コードの説明
Cubeのカラーを緑にします
GetComponent<Renderer>().material.color = Color.green;
z軸を中心に10°回転します
transform.Rotate(Vector3.forward * 10);
Playerゲームオブジェクトに2つのスクリプトをアタッチします。
スクリプトをアタッチすると、インスペクター にコンポーネントの1つとして登録されます。
コンポーネントは、他にTransform,MeshRenderer,BoxColliderなどがあります。
イベントを登録します
インスペクターで PlayerController_UnityEvent の On Cube Action () 欄に、2つのメソッドを登録します。
「+」ボタンで欄を追加し、Object 欄に Player オブジェクト自身をドラッグ&ドロップしてから、右側の関数ドロップダウンで CubeAction_UnityEvent → ColorChange を選びます。同じ手順でもう1つ欄を追加し、Rotate を選びます。
UnityEvent には、このように複数のメソッドを登録できます。

実行してみましょう
スペースキーを押下すると、Cubeが緑になって、10°回転するか確認しましょう

インスペクターから登録しない方法
CubeAction_UnityEventスクリプトに次のコードを追記します。
private void Start()
{
var action = GetComponent<PlayerController_UnityEvent>().OnCubeAction;
action.AddListener(ColorChange);
action.AddListener(Rotate);
}
インスペクターで登録したメソッドは「-」ボタンで削除してから確認しましょう。インスペクターとコードの両方で登録すると、1回のキー入力で2回ずつ呼ばれてしまいます。
なお、コードで登録したメソッドは RemoveListener で登録を解除できます。
イベントを宣言しているスクリプトと、イベントにメソッドを登録するスクリプトが、別々のオブジェクトにアタッチされている場合です。
objAにアタッチされているスクリプト
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
public class EventSample : MonoBehaviour
{
public UnityEvent actionEvent;
void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
actionEvent?.Invoke();
}
}
}
objBのスクリプト(インスペクターで登録する場合)
objA を選択し、インスペクターの EventSample コンポーネントにある Action Event () 欄に、objB の AttackHandler と DefenseHandler を登録します(Object 欄には objB をドラッグ&ドロップします)。
using UnityEngine;
public class Act : MonoBehaviour
{
public void AttackHandler()
{
Debug.Log("Attack");
}
public void DefenseHandler()
{
Debug.Log("Defense");
}
}
objBのスクリプト(コードで登録する場合)
objB の Act コンポーネントにある Event Sample 欄に、インスペクターで objA をドラッグ&ドロップして参照を設定しておきます([SerializeField] を付けた private 変数もインスペクターに表示されます)。
コードで登録する場合は、AddListener メソッドを使います。インスペクターで登録した場合と同じ動きになります。
別のオブジェクトのイベントに登録したときは、自分が破棄されるときに RemoveListener で登録を解除しておくと安全です。
using UnityEngine;
public class Act : MonoBehaviour
{
[SerializeField]
EventSample eventSample;
private void Start()
{
eventSample.actionEvent.AddListener(AttackHandler);
eventSample.actionEvent.AddListener(DefenseHandler);
}
private void OnDestroy()
{
if (eventSample != null)
{
eventSample.actionEvent.RemoveListener(AttackHandler);
eventSample.actionEvent.RemoveListener(DefenseHandler);
}
}
public void AttackHandler()
{
Debug.Log("Attack");
}
public void DefenseHandler()
{
Debug.Log("Defense");
}
}
イベントに引数を取りたい場合
イベントハンドラ側に情報を渡したい場合があります。その場合、引数として渡すことができます。
コード
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
public class PlayerController_UnityEvent : MonoBehaviour
{
public UnityEvent<string> OnCubeAction;
private void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
OnCubeAction?.Invoke("メッセージ");
}
}
public void Show(string msg)
{
Debug.Log(msg);
}
}
コードの説明
引数付きのイベントを宣言します。
型パラメータに <string> を指定すると、「戻り値なし、string型の引数を1つ取る」メソッドを登録できるようになります。
public UnityEvent<string> OnCubeAction;
?は、「もしnullでなければ」を略するもので、if(OnCubeAction != null)と同じです。
Invoke()で、メソッド(OnCubeAction(引数))を呼び出します。
スペースキーを押下すると実行されます。
OnCubeAction?.Invoke("メッセージ");
今回は、サンプルとして、同じクラスにイベントハンドラを記述してみました。
もちろん、同じオブジェクトにアタッチされている別のスクリプトや別のオブジェクトにアタッチされているスクリプトを登録してもいいです。
public void Show(string msg)
{
Debug.Log(msg);
}
イベントを登録します
インスペクターで PlayerController_UnityEvent の On Cube Action (String) 欄にメソッドを登録します。
関数のドロップダウンを開くと、Show が「Dynamic String」と「Static Parameters」の2か所に表示されます。コードから渡した値(ここでは Invoke(“メッセージ") の “メッセージ")を受け取りたい場合は、Dynamic String 側の Show を選びます。

渡す引数の値が固定値でいい場合
上記の設定選択で、Dynamic String ではなく、Static Parameters側を選びます。
メソッド欄の下側に空白が出てきますので、ここに記入します。
イベントハンドラ側には、このデータが渡されます。

C#イベントチュートリアル
C#(.NET)に用意されている EventHandler デリゲート型を使って、C# の event を作成します。
シーンをDotNetEventとして、保存します。
プロジェクトビューで、新しくシーンを作成します。名前は、DotNetEventとします。
Cubeオブジェクトを作成します。
名前をPlayerにします。
PlayerController_DotNetEventスクリプトを作成します。
コード
using System;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController_DotNetEvent : MonoBehaviour
{
public event EventHandler OnCubeAction;
private void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
OnCubeAction?.Invoke(this, EventArgs.Empty);
}
}
}
コードの説明
event キーワードを付けて、EventHandler 型のイベントを宣言します。
EventHandler は .NET に用意されているデリゲート型で、「戻り値なし、引数は object sender(イベントを発生させたインスタンス)と EventArgs e(イベントの情報)」のメソッドを登録できます。OnCubeAction には、あとでメソッドを登録します。
public event EventHandler OnCubeAction;
?は、「もしnullでなければ」を略するもので、if(OnCubeAction != null)と同じです。
Invoke()で、メソッド(OnCubeAction)を呼び出します。
イベントを発生させたインスタンスは、このインスタンス自身とし、情報は特に提供しないため、Emptyとします。
OnCubeAction?.Invoke(this, EventArgs.Empty);
CubeAction_DotNetEventスクリプトを作成します。
コード
using System;
using UnityEngine;
public class CubeAction_DotNetEvent : MonoBehaviour
{
private void Start()
{
PlayerController_DotNetEvent action = GetComponent<PlayerController_DotNetEvent>();
action.OnCubeAction += ColorChange;
action.OnCubeAction += Rotate;
}
public void ColorChange(object sender, EventArgs e)
{
GetComponent<Renderer>().material.color = Color.green;
}
public void Rotate(object sender, EventArgs e)
{
transform.Rotate(Vector3.forward * 10);
}
}
コードの説明
PlayerController_DotNetEventコンポーネントを取得します。この名称は、クラス名(スクリプト名)と同じです。
PlayerController_DotNetEvent action = GetComponent<PlayerController_DotNetEvent>();

Visual Studio では、図のように型名の代わりに var(暗黙的な型指定)を使う書き方を提案してくれます。右辺の GetComponent<PlayerController_DotNetEvent>() で型がわかるので、var にすると短く見やすくなります。
イベントを登録します
イベントが発生したら呼び出されるメソッドを登録します。
PlayerController_DotNetEvent クラスの OnCubeAction に、+= で呼び出したいメソッドを追加していきます。
event を付けたメンバーは、宣言したクラスの外からは = での代入や Invoke による呼び出しができず、+=(追加)と -=(解除)だけが許されています。これにより、他のスクリプトが登録済みのメソッドを上書きしたり、勝手にイベントを発生させたりすることを防げます。
今回は同じオブジェクト上のスクリプト同士なので省略していますが、別のオブジェクトのイベントに登録した場合は、OnDestroy で -= して登録を解除しておくと安全です。
action.OnCubeAction += ColorChange;
action.OnCubeAction += Rotate;
Playerゲームオブジェクトに2つのスクリプトをアタッチします。
スクリプトをアタッチすると、インスペクター にコンポーネントの1つとして登録されます。
コンポーネントは、他にTransform,MeshRenderer,BoxColliderなどがあります。
実行してみましょう
スペースキーを押下すると、Cubeが緑になって、10°回転するか確認しましょう

UnityイベントとC#イベントの使い分け
どちらも「イベントを発生させる側」と「処理する側」を切り離すための仕組みですが、向いている場面が違います。
| 観点 | Unityイベント(UnityEvent) | C#イベント(event) |
|---|---|---|
| メソッドの登録方法 | インスペクターから登録できる(コードでは AddListener) | コードで += する |
| 向いている場面 | ボタンや演出など、インスペクターで配線を調整したいとき | スクリプト同士の連携など、コードの中で完結させたいとき |
| 外部からの操作 | public にすると、他のスクリプトからも Invoke や RemoveAllListeners ができる | 宣言したクラスの外からは += と -= だけ |
| 呼び出しのコスト | C#イベントよりやや大きい | 軽い |
迷ったら、インスペクターで設定したいものは UnityEvent、コードの中だけで使うものは C# の event、と考えるとよいでしょう。
なお、Unity の C# イベントでは、この記事の EventHandler の代わりに public event Action OnCubeAction;(using System; が必要)のように Action を使う書き方もよく見かけます。Action なら引数なしのメソッドをそのまま登録でき、OnCubeAction?.Invoke(); で呼び出せます。
おすすめ動画
ドキュメント
Unity 公式のスクリプトリファレンス:UnityEvent









ディスカッション
コメント一覧
まだ、コメントがありません