はじめに
今回はコンポーネントに依存しないメソッドの実行方法についての記事になります!
UnityにはSendMessageという一つのメッセージで複数の関数を起動するメソッドが備わっています。
具体的にはゲームオブジェクトにアタッチされている指定した名前と同じ全てのメソッドを呼び出すという動作をします。
おそらくよく使われるStart・Updateなどもこれで動作している?と勝手に思っていますが、正しいのはまだ分かっていないです。
これについて少し掘り下げてみましょう。
SendMessage
まずはSendMessageの詳細を見てみましょう。
public void SendMessage (string methodName, object value= null, SendMessageOptions options= SendMessageOptions.RequireReceiver);
ここでの引数の詳細は以下の通りです。
| 引数名 | 意味 |
|---|---|
| methodName | 呼び出すメソッドの名前 |
| value | 呼び出すメソッドに渡す値 |
| options | ターゲットのオブジェクトにメソッドが存在しない場合、エラーを発生させるかどうか |
optionsはSendMessageOptionsという列挙型を指定します。
| 名前 | 意味 |
|---|---|
| RequireReceiver | 必ず受信先がなくてはならない |
| DontRequireReceiver | 受信先がなくてもよい |
SendMessageの使用例
実際に使った例を書いてみました。
Sender.cs
using UnityEngine; public class Sender : MonoBehaviour { private void Start() { gameObject.SendMessage("OnRecieve",10); //Reciever1: 10 Reciever2: 10 } }
Reciever1.cs
using UnityEngine; public class Reciever1 : MonoBehaviour { public void OnRecieve(int value) { Debug.Log("Reciever1: " + value); } }
Reciever2.cs
using UnityEngine; public class Reciever2 : MonoBehaviour { public void OnRecieve(int value) { Debug.Log("Reciever2: " + value); } }


ただ、このメッセージは非アクティブのゲームオブジェクトには送信されないことに気をつけてください。
MessageSystem
先程のSendMessageには、自分で名前を入力することやリファクタリングしにくい、引数が一つのみなどの欠点をいっぱい抱えていました。
これを解決するために作られたのがMessageSystemで、今から使うひとはこちらだけ覚えれば大丈夫です。
MessageSystemで重要な要素はIEventSystemHandlerインターフェイス・ExecuteEvents.Executeメソッドの二つです。
bool ExecuteEvents.Execute<T>(GameObject target, BaseEventData eventData, ExecuteEvents.EventFunction<T> functor);
ただBaseEventDataとExecuteEvents.EventFunction<T>もなかなかに分かりづらいので詳細を見てみましょう。
BaseEventDataクラスは新しい EventSystem 内の全イベントタイプに共通する基本イベントデータを内包したクラスを指しているらしく、コンストラクタは以下の通りです。
public BaseEventData (EventSystems.EventSystem eventSystem);
EventSystems.BaseEventData - Unity スクリプトリファレンス
ここで分かればよかったのですが、新しくEventSystemっていったいどんなや・・・という疑問が生まれてきてしまいました。
EventSystemはuGUIを作成すると自動で作成されるキーボード、マウス、タッチやカスタムの入力に基づいて、アプリケーション内のオブジェクトにイベントを送信する方法みたいです。
イベントシステム - Unity マニュアル
だんだん覚えることが多くて辛くなってくるかもしれませんが、実際にBaseEventDataを使うことはほとんどなく、基本はeventDataはnullにしておいて大丈夫だと思います。
ただ使いたい場合は、最低限EventSystem.currentで現在のEventSystemを返すstatic変数があることだけ覚えればOKだと思います。
eventData: new BaseEventData(EventSystem.current)
またExecuteEvents.EventFunction<T>は対象のインスタンス・EventDataを引数に持つラムダ式を指定します。(別にラムダ式じゃなくても良いですが)
functor: (reciever, eventData) => reciever.OnRecieve()
細かい説明はほどほどにして、実際に使った例を見てたほうが分かりやすいと思うのでみてみましょう。
MessageSystemの使用例
まずは引数なしの基本的なものの例を書いてみました。
IRecieveMessage.cs
using UnityEngine.EventSystems; public interface IRecieveMessage : IEventSystemHandler { void OnRecieve(); }
Sender.cs
using UnityEngine; using UnityEngine.EventSystems; public class Sender : MonoBehaviour { private void Start() { ExecuteEvents.Execute<IRecieveMessage>( target: gameObject, eventData: null, functor: (reciever, eventData) => reciever.OnRecieve() ); } }
Reciever1.cs
using UnityEngine; public class Reciever1 : MonoBehaviour , IRecieveMessage { public void OnRecieve() { Debug.Log("Recieve1"); } }
Reciever2.cs
using UnityEngine; public class Reciever2 : MonoBehaviour , IRecieveMessage { public void OnRecieve() { Debug.Log("Recieve2"); } }


引数ありの使用例
MessageSystemの場合は無制限に引数が指定でき、以下のように実装します。
IRecieveMessage.cs
using UnityEngine.EventSystems; public interface IRecieveMessage : IEventSystemHandler { void OnRecieve(int value, string text); }
Sender.cs
using UnityEngine; using UnityEngine.EventSystems; public class Sender : MonoBehaviour { private void Start() { ExecuteEvents.Execute<IRecieveMessage>( target: gameObject, eventData: null, functor: (reciever, eventData) => reciever.OnRecieve(10, "value: ") ); } }
Reciever1.cs
using UnityEngine; public class Reciever1 : MonoBehaviour , IRecieveMessage { public void OnRecieve(int value, string text) { Debug.Log("Recieve1: " + text + value); } }
Reciever2.cs
using UnityEngine; public class Reciever2 : MonoBehaviour, IRecieveMessage { public void OnRecieve(int value, string text) { Debug.Log("Recieve1: " + text + value); } }

さいごに
これらを用いるとより疎結合ができる利点はありますが、特にSendMessageは自分でメソッドの名前の書き間違いには要注意です。
また使ってみた感じExecuteEvents.EventFunction<T>の第2引数のeventDataはほぼ使わない?というような気がしました。
もしこんな使い方があるというのを知っている方がいましたら、是非コメント等で教えていただけると嬉しいです!