(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

SendMessageを置き換えるために設計されたというメッセージシステムとやら【Unity】


このエントリーをはてなブックマークに追加

この記事でのバージョン
Unity 5.3.1f1 Personal

はじめに

Unity 4.6から登場したメッセージシステム、これは以下のようなもので、

SendMessageの後継みたいな感じらしいです。

新しい UI システムは、SendMessageを置き換えるために設計されたメッセージシステムを使用します。システムは、ただの C# です。SendMessageのいくつかの問題に対処することを目指しています。


Unity 4.6登場から結構経ちますが、実はまだ一度も使った事がなかったり……、

そもそもSendMessage自体使ってなかったですし(:3っ)∋〜


とは言え、便利そうな気配が漂いまくってるので、勉強がてら記事にしてみました!


SendMessageから置き換えてみる

とりあえずどんな感じで使うのか、SendMessageと比べてみました。


処理の内容は送信側のSender

受信側のRecieverにあるOnRecieveというメソッドを実行しています。


なお、分かりやすいようにReciever、Seneder共に同じオブジェクトにある場合を想定しています。


f:id:kan_kikuchi:20160301134341p:plain


ではコードです。


送信側(SendMessage)

using UnityEngine;

public class Sender : MonoBehaviour {

  private void Start () {

    //SendMessageを使ってメソッド実行
    gameObject.SendMessage ("OnRecieve");

  }

}


送信側(メッセージシステム)

using UnityEngine;
using UnityEngine.EventSystems; //必須

public class Sender : MonoBehaviour {

  private void Start () {

    //ExecuteEvents.Executeを使ってメソッド実行
    ExecuteEvents.Execute<RecieveInterface>(
      target: gameObject, 
      eventData: null, 
      functor: (reciever, eventData) => reciever.OnRecieve()
    ); 

  }

}


受信側(SendMessage)

using UnityEngine;

public class Reciever : MonoBehaviour {

  //privateメソッドでもok
  private void OnRecieve (){
    Debug.Log("受け取った!");
  }

}


受信側(メッセージシステム)

using UnityEngine;

//RecieveInterfaceを実装
public class Reciever : MonoBehaviour, RecieveInterface{

  //privateはダメ
  public void OnRecieve (){
    Debug.Log("受け取った!");
  }

}
using UnityEngine.EventSystems; //必須

//IEventSystemHandlerを継承させる
public interface RecieveInterface : IEventSystemHandler {
  void OnRecieve();//受け取るようのメソッドを定義
}


上記のコードの通り、メッセージシステムを使う場合は、以下の2点が必須になります。

受信側がIEventSystemHandlerを継承したインターフェースを実装している。

送信側がExecuteEventsを使う。


なお、メッセージシステムを使った場合でも、

受信先がなくてもエラーは出ませんし、

一つのGameObjectに複数の受信先があっても大丈夫です。


インターフェース

そもそも受信側で使ったインターフェースとは以下のようなものです。

メソッドの定義だけを定め、中身は実装しないものを言います。
中身のあるメソッドや、変数は一切定義することができません。
インターフェイスを実装するクラスでは、必ずインターフェイスで定義したメソッドを実装しなければなりません。

C#を攻略しようーインターフェイスー



ほんで、メッセージシステムを使う場合は、

IEventSystemHandlerというインターフェースを継承したインターフェース

を作成し、そこに呼び出したいメソッドを定義し、

受信先ではそのインターフェースを実装する必要があるということですな。


ExecuteEvents

送信側で使うExecuteEventsとは、名前の通りイベントを実行するものらしいです。

例にもあったように、以下のような形式でSendMessageと同じような事が出来ます。

ExecuteEvents.Execute<インターフェース>(
  target: メッセージを送るゲームオブジェクト
  eventData: イベントデータ,
  functor: デリゲート
);


一番分かり難いと思われるのがfunctorで指定する部分で、

ここで実行するメソッドをデリゲートとして設定します。


デリゲートやら匿名関数については以下を参照の事。


以下のように、functorにメソッドを指定することも可能です。

using UnityEngine;
using UnityEngine.EventSystems; //必須

public class Sender : MonoBehaviour {

  private void Start () {

    //ExecuteEvents.Executeを使ってメソッド実行
    ExecuteEvents.Execute<RecieveInterface>(
      target: gameObject, 
      eventData: null, 
      functor: Method
    ); 

  }

  //functorに設定するメソッド
  private void Method(RecieveInterface reciever, BaseEventData eventData){
    reciever.OnRecieve();
  }

}


これをラムダ式ではない匿名メソッドを使うと、以下のようになり、

functor: delegate(RecieveInterface reciever, BaseEventData eventData) { handler.OnRecieve();}


ラムダ式を使うと、最初の例と同じになります。

functor: (reciever, eventData) => reciever.OnRecieve()


なお、RecieveInterfaceと一緒に引数になっているBaseEventDataですが、

これは以下のようなものらしいです。

EventSystem 内の全イベントタイプに共通する基本イベントデータを内包したクラス

Unity - スクリプトリファレンス:BaseEventData


uGUIでイベントを取得する際に使われるようですが、

uGUIはほぼ使ったことがないので詳細は分かりませぬ……。


とりあえず、メッセージシステムをSendMessageの代替として使う場合は

無視しても問題ないっぽいです。便利な使い方があれば御一報を(:3っ)∋〜


利点

SendMessageの代替ができるという事は分かりましたが、

優位性がなければSendMessageでいいじゃんって話になってしまいます。


なので、SendMessageと比較した際のメッセージシステムの利点を3つほどご紹介!


メソッドの指定に文字列を使わない

なんと言っても、これが一番の利点だと思います。

SendMessageのように文字列で指定する形式だと、

タイポしても気付きにくかったり、リファクタリングしにくかったり、良い事ないですからね……。


一応、メソッド名を文字列で取得する方法もありますが、まわりくどいっす。





引数の設定が楽

SendMessageでも引数の設定は出来ますが、個数は一つ、型はobject限定です。

//Method(int num) に送る
gameObject.SendMessage("Method", 1);


それに対してメッセージシステムは普通のメソッドと同様に、好きなように引数を設定出来ます。

//OnRecieve(float f, GameObject target, Vector3 pos) に送る
functor: (reciever, y) => reciever.OnRecieve(1.0f, gameObject, Vector3.one)



privateメソッドに送れない

最初の例にあったように、SendMessageはprivateなメソッドも実行出来てしまいます。

privateメソッドを外部から実行されたら、

思わぬバグの原因になりかねないので、利点の一つに挙げてみました。


テストコードなど、例外はあるかもしれませんが……。


おわりに

インターフェースを作らなければいけないので、若干めんどくさい感じはありますが、

SendMessageを使うよりはメッセージシステム使った方が良さそうですね!


ただ、「メッセージシステムはSendMessageより高速」という記述を結構見かけたんですが、

100万回メソッド実行みたいな比較をしても差はほとんど出ませんでした……。

なにか優位になる条件というか状況があるのか、気になる今日この頃です(:3っ)∋〜