咱們知道Unity3D自身有SendMessage向對象之間發送消息,但這個消耗是比較大的,由於它很大程度上涉及了Reflection發射機制。 php
如何變動思路,結合C#自帶的消息系統delegate委託事件,對此進行優化: 數組
咱們看如下一個簡單的delegate使用: post
- public class DelegateBasic : MonoBehaviour {
-
- //define my delegate statement.
- public delegate void MyDelegate(string arg1);
-
- //create my delegate object
- public MyDelegate myDelegate;
-
- // Use this for initialization
- void Start () {
- myDelegate += myFunciton1;
- myDelegate += myFunciton2;
- }
-
- // Update is called once per frame
- void Update () {
-
- }
-
-
- void OnGUI()
- {
- if(GUILayout.Button("INVOKE"))
- {
- myDelegate("Invoke....");
- }
-
- }
-
- void myFunciton1(string s)
- {
- Debug.Log("myFunciton1 " + s);
- }
-
- void myFunciton2(string s)
- {
- Debug.Log("myFunciton2 " + s);
- }
-
-
- }
以上只是實現myDelegate一旦調用,那麼鏈接了該委託的事件的方法都會被調用。 學習
可是以上咱們看到,這只是1個delegate類型對應 —— 多個與該委託定義形式一致的方法;假如我須要不一樣的delegate呢?並且這些方法形式定義不同,也或者說咱們須要方法傳參不同呢? 優化
1、Notification通知中心結構:參考http://wiki.unity3d.com/index.php/Category:Messaging 這裏有不少個寫好的例子可使用。 this
- using UnityEngine;
- using System.Collections;
-
-
- // Each notification type should gets its own enum
- public enum NotificationType {
- OnStuff,
- OnOtherStuff,
- OnSomeEvent,
- TotalNotifications
- };
-
- public delegate void OnNotificationDelegate( Notification note );
-
- public class NotificationCenter
- {
- private static NotificationCenter instance;
-
- private OnNotificationDelegate [] listeners = new OnNotificationDelegate[(int)NotificationType.TotalNotifications];
-
- // Instead of constructor we can use void Awake() to setup the instance if we sublcass MonoBehavoiur
- public NotificationCenter()
- {
- if( instance != null )
- {
- Debug.Log( "NotificationCenter instance is not null" );
- return;
- }
- instance = this;
- }
-
-
- ~NotificationCenter()
- {
- instance = null;
- }
-
-
- public static NotificationCenter defaultCenter
- {
- get
- {
- if( instance == null )
- new NotificationCenter();
- return instance;
- }
- }
-
-
- public void addListener( OnNotificationDelegate newListenerDelegate, NotificationType type )
- {
- int typeInt = (int)type;
- listeners[typeInt] += newListenerDelegate;
- }
-
-
- public void removeListener( OnNotificationDelegate listenerDelegate, NotificationType type )
- {
- int typeInt = ( int )type;
- listeners[typeInt] -= listenerDelegate;
- }
-
-
- public void postNotification( Notification note )
- {
- int typeInt = ( int )note.type;
-
- if( listeners[typeInt] != null )
- listeners[typeInt](note);
- }
-
-
- }
-
-
-
-
- // Usage:
- // NotificationCenter.defaultCenter.addListener( onNotification );
- // NotificationCenter.defaultCenter.sendNotification( new Notification( NotificationTypes.OnStuff, this ) );
- // NotificationCenter.defaultCenter.removeListener( onNotification, NotificationType.OnStuff );
如上是NotificationCenter,它的工做呢就是:
1.一、建立多個監聽者,就至關於有多個類型的監聽器。 spa
- OnNotificationDelegate [] listeners
1.二、每一個監聽者可以「發送」屬於本身類型的方法便是通知。例若有一個「登陸按鈕」,這個按鈕有一個監聽者,那麼咱們就能夠經過Notification這個中心去向這個按鈕類型的監聽者註冊信息(或者移除監聽)。 .net
1.三、除了以上2點只是可以實現咱們前面說起的須要多個Delegate(其實就是建立多個監聽者類型這個數組),那麼咱們須要方法的傳參類型也不同呢? 3d
- public delegate void OnNotificationDelegate( Notification note );
咱們看到單個delegate的定義,傳參是Notification note;這裏建立了一個消息的基類;全部類型的監聽者[] 接收的傳參消息都要屬於單個Notification。
- // Standard notification class. For specific needs subclass
- public class Notification
- {
- public NotificationType type;
- public object userInfo;
-
- public Notification( NotificationType type )
- {
- this.type = type;
- }
-
-
- public Notification( NotificationType type, object userInfo )
- {
- this.type = type;
- this.userInfo = userInfo;
- }
- }
如上圖,就是基本的通知內容體的類定義,若是要保存更復雜或者說更多類型的信息,那麼咱們能夠繼承這個消息基類。
- public class SuperNotification : Notification
- {
- public float varFloat;
- public int varInt;
-
-
- public SuperNotification( NotificationType type, float varFloat, int varInt ) : base( type )
- {
- this.varFloat = varFloat;
- this.varInt = varInt;
- }
- }
如上圖,就是繼承了基類Notification實現更多消息載體定義。能夠強制轉換類型傳給要調用的委託者,當該委託類型的方法接受到這個基類能夠從新轉化成它原來的類。
1.3.一、思考:若是使用Hashtable傳參呢?效率會不會快一點,並且咱們不須要太多的建立類。
1.3.二、思考:由於NotificationCenter中初始化這些監聽者數量就直接new數組,那麼假如我定義了不少這些監聽者類型。是否沒有使用到的監聽者就不須要建立了呢?把數組改爲List動態增長,效率是否會提升?
- private OnNotificationDelegate [] listeners = new OnNotificationDelegate[(int)NotificationType.TotalNotifications];
1.3.三、當切換場景時候必定要取消已經註冊的監聽者信息。
2、結合Notification的統一管理、範型傳參定義監聽方法的傳參數量分類、區分不一樣監聽者使用字符串名稱;參考:http://wiki.unity3d.com/index.php/Advanced_CSharp_Messenger 對象
這個結構製做的Message系統我以爲比前面的更增強大,並且不須要建立更多的消息內容體類,由於採用固定數量的範型傳參,通常來講傳參3個基本上就不少了足夠了,甚至能夠傳參就是前面咱們定義的Notification類,這就是範型的強大。
這裏就再也不闡述了,記住和前面的區別:
一、監聽者區分不用enum類型,而直接使用string名稱定義,經過鍵值對應儲存;
二、不用擔憂監聽的方法傳參問題,只須要區分是多少個參數來區分;
三、要支持自動切換場景時候進行這些監聽者的消除;
2.一、思考 :若是調用時候直接用string命名來區分不一樣的監聽者,那麼這樣很容易形成混亂,並且你代碼多了就不知道你前面定義的這個監聽者是什麼了。因此能夠統一固定一個enum區分或者監聽者身份類。
3、除了以上2個實現新的消息系統外,還有一種學習QT的消息槽結構來實現,參考:http://www.cocoachina.com/bbs/read.php?tid=68048&page=1 固然本人不熟悉QT的機制,稍微看了後,做爲一個區分來研究下。其實這種方式去實現delegate只是一種習慣了,還不能徹底是QT的消息槽機制。
- using UnityEngine;
- using System.Collections;
-
- public class ZObject : MonoBehaviour {
-
- public delegate void SIGNAL(Hashtable args);
-
- public virtual void CONNECT(ref SIGNAL signal, SIGNAL slot){
- signal += slot;
- }
-
- public virtual void DISCONNECT(ref SIGNAL signal, SIGNAL slot){
- signal -= slot;
- }
-
- public void EMIT(SIGNAL signal, Hashtable args){
- if(signal != null)
- signal(args);
- }
- }
以上首先聲明一個消息槽機制的基礎對象,它必須能夠CONNECT和DISCONNECT、EMIT(發射信號);想象一下,有個信號源向天空發射了一枚煙花信號是救命的,而後不少我的(就是槽)就會接受到這個信號的內容而且調用。
咱們定義一個信號發射體,例如一個按鈕:
- public class ClassA : ZObject {
-
- public SIGNAL mouseClickSignal;
-
- void OnMouseDown(){
- EMIT(mouseClickSignal, new Hashtable(){
- {"sender", gameObject},
- {"position", Vector3.one},
- {"string", "hello world"},
- {"time", Time.time}
- });
- }
- }
咱們看到,這個按鈕有一個點擊的信號,當點擊時候發射這個信號。
疑問:那麼誰要知道它的信號呢?就是-槽了。對,接下來只要把這個信號把須要獲取這個信號的槽鏈接起來,造成信號槽系統便可!
- public class ControllerB : ZObject {
-
- public ClassA objectA;
- public ClassA objectB;
- void Awake () {
-
- CONNECT(ref objectA.mouseClickSignal, //sender's signal
- SLOT_MouseClicked //receiver's slot method
- );
-
- CONNECT(ref objectB.mouseClickSignal, //sender's signal
- SLOT_MouseClicked //receiver's slot method
- );
-
- }
-
- //SLOT method
- //A slot is a function that is called in response to a particular signal
- void SLOT_MouseClicked(Hashtable args){
-
- GameObject sender = (GameObject)args["sender"];
- string str = args["string"].ToString();
- float _time = (float)args["time"];
-
- Debug.Log("RECEIVE SIGNAL : "+sender.name + " Say:"+str +" at:"+_time + " , call SLOT_MouseClicked");
- }
-
- }
如上,咱們看到這個槽方法是:
- void SLOT_MouseClicked(Hashtable args){
咱們甚至還能夠新建不少個其餘槽方法,來鏈接這個信號便可。
3.一、思考,這個QT信號槽機制仍是很不錯的,很好理解。可是呢,若是切換場景的時候,要把這些信號給DISCONNECT掉就比較麻煩了。由於它們的信號有多是分散到各地,惟一要注意就是你在哪裏CONNECT了信號,就應該在這個COMPONENT裏面DISCONNECT掉它。
4、總結:
經過以上3點的C#支持的delegate消息系統強化後,能替代Unity3d的SendMessage這種消耗巨大的方式。
5、補充:
待續