UnityC# デリゲートとイベントとUnityActionの使い方

デリゲートとイベントとはの画像 Unity C#入門講座
デリゲートとイベントはメソッドを値として使うためのもの


Unityの本格ゲーム制作講座はこちら
【30日間の全額返金保証付き】

今回の記事ではデリゲートとイベントについて解説していきます。

前回は辞書の使い方について解説しましたね。

UnityC# 辞書(ディクショナリー)の使い方 キーと値でデータを指定する
今回の記事では辞書型(ディクショナリー、Dictionary)について解説します。 辞書型も前回扱ったListクラスと同じくコレクションの一種になります。 前回の記事: 簡単にデータにアクセスできる辞書型(ディクショナリー)って何...

デリゲートとイベントを使うことでメソッドを値として使用することができます。メソッドを値にして利点があるのかと思われますが、アプリでできることが広がるので重要なC#の機能になります。

今回の内容は当サイトのUnity C#入門講座の中でも高難易度な箇所になります。もし一回でわからなくても挫折しないようにしましょう。
わかるところまで理解して、次へ進み、また適宜戻ってくるのもよいでしょう。

今回の講座を理解すればUnityのイベントの実装の仕組みがわかるようになります。


【無料転職サポート&就職後に授業料を全額返金】

デリゲートとイベントとは

デリゲートおよびイベントは参照型になります。それぞれメソッドを値として扱うための型になります。

デリゲートについて

デリゲートについての画像

デリゲートはメソッドを表す型になる。定義した後は変数のように使用でき、複数のメソッドを設定できる。

デリゲート(Delegate)とはメソッドを表すものになります。そのため定義の書き方はメソッドと似ています。

デリゲートの定義の書き方:delegate <戻り値> <デリゲート名> (<引数>, …)

定義したデリゲートは型のように扱いますが、sealedキーワードが自動的に指定されているためそれ以上派生できません

デリゲートには以下の演算子が用意されており、これらの演算子でメソッドの管理ができます。

  • +演算子:2つのデリゲートを一つにまとめる。
  • -演算子:右辺値側のメソッドがあったらそれを削除する。

また、デリゲートはメソッドのように引数を渡す事で設定されたメソッドを全て実行することができます。また、Invokeメソッドを使用しても同じ結果が得られます。

  • メソッド形式:<デリゲート型の変数>(<引数>, …);
  • Invokeメソッド:<デリゲート型の変数>.Invoke(<引数>, …);

ただし、デリゲートに何もメソッドが設定されていない場合にメソッドを実行しようとすると例外が発生します。なので、基本的には?演算子とInvokeメソッドを組み合わせてメソッドを呼び出すのがオススメです。

  • オススメなメソッドの呼び出し方:<デリゲート型の変数>?.Invoke(<引数>, …);

アロー演算子(=>)の使い方がわからなくなってる人はメソッドの記事を復習してみてね。

GetInvocationListメソッドについて

デリゲート型にはGetInvocationListメソッドというメンバメソッドが定義されています。このメソッドを使用することでデリゲート型に設定されているメソッドのリストを取得することができます。

実際にメソッドの情報にアクセスするにはリフレクションの知識が必要になってきます。これはデリゲートの高度な使い方になります。

無名関数とラムダ関数ついて

無名関数とラムダ関数についての画像

無名関数とラムダ関数を使うとメソッドの中にメソッドを定義できる。

デリゲート型にはクラスのメンバメソッド以外にも無名関数ラムダ関数も設定することができます。

無名関数とラムダ関数は似た様なものでメソッド内でメソッドの定義を書くことができるC#の機能になります。

  • 無名関数の書き方:delegate(引数, …) { <処理>… };
  • ラムダ関数の書き方:(<引数>, …) => { <処理>… };。(引数が一つの時は丸かっこは省略できます。)

これらのメソッドの設定方法は同じですが、削除する際にどこのメソッドなのか判別することができないのでそのままでは削除できません

削除したい場合は一度デリゲート型の変数に設定した後にその変数を使用することで削除できます。また、GetInvocationInfoメソッドとリフレクションを使用し削除したいメソッドを特定することでもできますが、こちらの方法は少々難しいやり方になります。

戻り値およびref、outキーワードがついた引数の扱いについて

デリゲートから受け取れるメソッドからの出力は一つのメソッドのみの画像

デリゲートに複数のメソッドを設定している時に受け取れる戻り値とパラメータ付き引数は最後のメソッドのものだけになるので注意。

デリゲートでも設定したメソッドの戻り値を取得できたり、引数についたrefやoutキーワードなどでメソッドから値を設定できます。

その場合の注意点として、戻り値および引数に設定される値はデリゲートの中で最後に呼び出されたメソッド一つだけになりますので注意してください。

System.Delegate型について

少しマニアックな解説になりますが全てのデリゲート型はSystem.Delegate型から派生した型になります。

System.Delegate型は一つのメソッドしか表さないので、複数個のメソッドを扱いたい時はSystem.MulticastDelegateというSystem.Delegate型から派生したクラスが実際には使われています。

C#がこれらの型を裏側で上手い事使ってデリゲートを実現しているので、豆知識程度に覚えておくといいでしょう。

イベントについて

イベントについての画像

イベントはクラス専用のデリゲートみたいなもの。定義したクラスからのみ設定されたメソッドを実行できる。

イベント(Event)はクラスのメンバの一種で指定したデリゲート型を簡単に扱うためのものです。

イベントを定義したクラスにはデリゲートと同じく複数のメソッドを設定することができ、そのクラス内部でのみ設定したメソッドを呼び出すことができます(内部的にはデリゲートのInvoke()メソッドを使用しています。)

以下の演算子およびメソッドがあります。

  • メソッドの追加:+=, add()
  • メソッドの削除:-=, remove()

これらの演算(add, remove)は定義しなくてもイベントを使用できますが、デフォルトの挙動をプロパティのget,setのようにオーバーライドできます。その際はsetメソッドと同じくvalue変数に設定される値が格納されています。

(プロパティ、set、getの使い方やvalueについての解説はこちら>>)

EventHandler<T>型とEventArgs型について

.Netと呼ばれるMicrosoftが提供するフレームワークにおいてイベントはEventHander<T>型とEventArgs型を利用しています。

これらの型はFormアプリケーションなどWindows環境での標準的なイベントの使い方として利用されています。

これらの2つの型を使用することで新しくデリゲート型を定義する必要なくイベントを使用することができるので、利用できる場合は活用してみましょう!

  • EventHandler<T>型:テンプレートなデリゲート型でTにはEventArgsを継承した型を指定する。
  • EventArgs型:EventHandler<T>型を使用するときのイベント発生時のパラメータを表す型。イベントのパラメータはこの型を継承した型になる。

Unityが提供しているデリゲート・イベント型について

Unity専用のデリゲート・イベントについての画像

Unityでは専用のデリゲートとイベントが用意されているので、基本それらを使用するのがオススメ。

Unityでは上で説明したC#のデリゲート・イベントも使用できますが、Unity専用のデリゲート・イベントも存在しています。

UnityActionについて

UnityにはUnityEngine.Events.UnityActionというUnity専用のデリゲート型があります。

UnityActionはUnityの仕様にあったものなので、デリゲート型をUnityで使用する時はこちらを使用するといいでしょう。

またメソッドの引数に合わせたバリエーションも存在していますので、用途に合わせて使い分けてください。これらのバリエーションではテンプレートを利用してメソッドの引数を指定します。戻り値はvoid型のものしかありませんので注意してください。

  • UnityAction<T>
  • UnityAction<T1, T2>
  • UnityAction<T1, T2, T3>
  • などなど

後、UnityActionでは対応した戻り値・引数を持つメソッドをUnityActionに自動的に変換(※)してくれる様になっています。

(※これについてはC#の暗黙の型変換と呼ばれる機能を利用しています。詳しい解説は長くなるのでここでは省略します。)

UnityEventクラスについて

UnityEngine.Events.UnityEventクラスはUnityAction用のイベントみたいなものになります。

UnityActionと同じくテンプレート版のものも用意されています。

スクリプト上からメソッドを追加・削除する時は次のメソッドを使用してください。

  • AddListenerメソッド:指定したメソッドを追加する。
  • RemoveListenerメソッド:指定したメソッドを削除する。
  • RemoveAllListenersメソッド:登録されているメソッドを全て削除する。

UnityEventクラスを使用する利点として、Inspector上からメソッドの設定ができます。そのためUnityではイベント型よりこちらを使用するのをオススメします。

Inspector上から設定する時はシーンに存在するGameObjectを指定しそれにアタッチされているコンポーネントのpublicなメンバが指定できます。(メソッドだけではなく、フィールドも設定できます。)

またUnityEventクラスの便利な点として、Inspector上では異なるメソッドの戻り値・引数を持つものでも引数が一つの数値型、および文字列のものでも設定することが可能です。その際はInspectorからそのメソッドに渡す値を入力してください。

UnityEventをInspectorから設定するの画像

UnityEventはInspector上からシーン上にあるGameObjectのメソッドなどを設定できる。

【実践】Unityでイベントとデリゲートを使ってみよう!

それでは実際にデリゲートとイベントをUnityで使ってみましょう!

Unityには先に紹介したUnityActionUnityEventが提供されているので今回のサンプルではそちらを使用します。

サンプル実行例の画像

SampleコンポーネントをアタッチしたGameObjectをクリックすると、Actionフィールドに設定したメソッドが実行される。クリックしたいGameObjectにはColliderをアタッチする必要があるので注意。

サンプルコードではSampleコンポーネントを使用してイベントを設定したり、実行したりします。

Sampleコンポーネントの使い方は以下のものになります。

  • このコンポーネントをアタッチされたGameObjectがクリックされた時に実行したいメソッドをActionフィールドに設定。
  • 他のイベントに設定できるメソッドとしてMoveActionメソッドを用意している。

GameObjectをクリックした時の処理はUnityの機能の一つであるOnMouseUpAsButtonメソッドを利用しています。このメソッドを使用する際はColliderと呼ばれる物理エンジンの当たり判定を表すコンポーネントをアタッチしておく必要がありますので注意してください(※)。

Actionフィールドに設定できるものはUnityが提供しているコンポーネントにも色々ありますので、それらもSampleコンポーネントのActionフィールドに設定してみるのもいいでしょう!

(※物理エンジンについての解説は省略しますが、基本的に<XXX>Colliderと名付けられたコンポーネントがアタッチされていればOnMouseUpAsButtonメソッドでクリックできる様になります。)

まとめ

今回の記事ではデリゲートとイベントについて解説してきました。簡単にまとめると以下の様になります。

  • デリゲートはメソッドを表す参照型。
  • デリゲート型は引数と戻り値、およびデリゲート名で定義される。
  • デリゲート型は同じ引数、戻り値を持つメソッドを変数として複数個格納することができる。
  • デリゲート型はメソッドのように使用するか、Invoke()メソッドで格納されたメソッドを実行することができる。
  • デリゲート型に何もメソッドが設定されていない時にメソッドを実行すると例外が発生する。
  • なので、基本的には?演算子とInvokeメソッドを組み合わせて使うのがオススメ。
  • デリゲート型のGetInvocationInfoメンバメソッドを使用することで設定されているメソッドの情報が取得できる。(ただし、リフレクションの知識がいる。)
  • 複数のメソッドが設定されているときにメソッドを呼び出すと最後に実行されるメソッドの戻り値が返される。
  • これと同じくref、outキーワード指定された引数がある場合も最後のメソッドのものが設定される。
  • イベントはクラスのメンバの一種で、指定したデリゲート型を使いやすくしたもの。
  • イベントはプロパティと似たもので、add、removeというメソッドを定義することができる。
  • イベントは定義されたクラスからしか設定されたメソッドを実行することができない。
  • Unityを使用するときはUnityActionおよびUnityEventを使用すると便利。

それでは次の記事に行ってみましょう!

UnityC#のLinq・属性・拡張メソッド・クラスの部分定義の使い方
今回の記事では拡張メソッド、Linq、属性、クラスの部分定義など開発にとても役立つC#の機能を説明します。 前回のデリゲートとイベントはちょっと難易度が高かったですが、今回の記事はもう少し取り組みやすくなります。ぜひ読んでみてください。 ...

初心者向けUnityC#入門講座に戻る>>

コメント

タイトルとURLをコピーしました