該博客,只爲解析,解析,解析,已經整理好,已經整理好,已經整理好。代碼核心原理套用網上最流行的那一套,也是最經常使用遊戲開發適用的消息機制。這裏面加上本身的一些優化,極大的修正(哈哈),實測,沒問題。萬一要是出現問題,歡迎童鞋能夠留言給我修正。html
有童鞋可能會好奇,unity裏面不是有本身的一套消息發送, 例如什麼SendMessage,這...這個幾乎是不能用的。設計模式
爲啥不能用,看看如下是網上給的解釋,本身玩玩demo仍是能夠用,可是實際開發,是幾乎不能用的。函數
I:它實現的是一種僞監聽者模式,利用的是反射機制。優化
II:SendMessage效率不高,由於每次調用的時候都會去遍歷檢測自身或者子節點上要調用的方法。ui
III:須要知道響應事件的物件,還須要一個響應函數的函數名字符串做爲參數,若是咱們有多個物件都要響應某個事件怎麼辦呢,或者咱們不知道有哪些物件要響應事件怎麼辦呢。(前面兩句話比較抽象,這句話總能看的懂吧)this
(若有不理解委託,事件能夠參考個人這篇帖子:https://www.cnblogs.com/u3ddjw/p/9920994.html)spa
通俗易懂點講,就是 一個物體發出消息,另一個,或者幾個物體能夠同時接收到這一消息並做出各自不一樣的行爲(反饋,處理)。設計
那麼,首先,咱們想到,須要什麼?3d
I: 咱們須要的是消息(實例),發送者。 消息(實例)+發送者=咱們須要的消息,就可以處理任何消息。code
II:怎麼把這個消息發送出去(消息處理中心)。
III:發送者發送(分發)消息的行爲
IV:接收消息。
換一種說法:發佈-訂閱模式。舉例就是定報紙,你跟郵局定了報紙,郵局就會在指定時間把報紙發下來給你;中間若是你不須要報紙了,那麼你就取消這個訂閱,郵局就不會發給你了。
圖解:
public class Notification { /// <summary> /// 發送者 /// </summary> public GameObject sender; /// <summary> /// 消息內容 /// </summary> public EventArgs param; /// <summary> /// 構造函數 (初始化) /// </summary> ///<param name="sender">通知發送者 ///<param name="param">通知內容 public Notification(GameObject sender, EventArgs param) { this.sender = sender; this.param = param; }
public Notification() { } /// <summary> /// 構造函數 /// </summary> ///<param name="param"> public Notification(EventArgs param) { this.sender = null; this.param = param; } } /// <summary> /// 傳遞的消息,這個是消息類中的具體消息種類 類 /// </summary> public class EventArgsTest : EventArgs { public int id; public string name; }
Notification是一個稍微抽象一點的消息類,要傳遞一個消息(類),我前面說到了,確定是須要知道具體發送者和具體消息類的。
而具體消息類,就是後面的EventArgsTest,這個是繼承於System.EventArgs,該類是自定義類,看到後面,可能會理解爲何這樣繼承。
public delegate void NotificationDelegate(Notification notific);
聲明一個委託傳遞上面所說的消息類的委託,這邊通俗一點來說就是:聲明一個能夠傳遞Notification 參數的方法。至於委託的用法這裏就不詳訴了。
public class NotificationCenter { private static NotificationCenter instance = null; public static NotificationCenter Get() { if (instance == null) { instance = new NotificationCenter(); return instance; } return instance; } private Dictionary<uint, NotificationDelegate> eventListeners = new Dictionary<uint, NotificationDelegate>(); public void AddEventListener(uint eventKey, NotificationDelegate listener) { if (!HasEventListener(eventKey)) { NotificationDelegate del = null; //定義方法 eventListeners[eventKey] = del;// 給委託變量賦值 } eventListeners[eventKey] += listener; //註冊接收者的監聽 } public void RemoveEventListener(uint eventKey,NotificationDelegate listener) { if (!HasEventListener(eventKey)) return; eventListeners[eventKey] -= listener; if (eventListeners[eventKey] == null) { RemoveEventListener(eventKey); } } public void RemoveEventListener(uint eventKey) { eventListeners.Remove(eventKey); } /// <summary> /// 分發事件,不須要知道發送者的狀況 /// </summary> /// <param name="eventKey"></param> /// <param name="notific"></param> public void PostDispatchEvent(uint eventKey, Notification notific) { if (!HasEventListener(eventKey)) return; // eventListeners[eventKey].Invoke(notific); eventListeners[eventKey](notific); } /// <summary> /// 分發事件,須要知道發送者,具體消息的狀況 /// </summary> ///<param name="eventKey">事件Key ///<param name="sender">發送者 ///<param name="param">通知內容 public void PostDispatchEvent(uint eventKey, GameObject sender, EventArgs param) { if (!HasEventListener(eventKey)) return; eventListeners[eventKey](new Notification(sender, param)); } public void PostDispatchEvent(uint eventKey) { if (!HasEventListener(eventKey)) return; eventListeners[eventKey](new Notification()); } /// <summary> /// 分發事件,不須要知道任何,只須要知道發送過來消息了 /// </summary> ///<param name="eventKey">事件Key ///<param name="param">通知內容 public void PostDispatchEvent(uint eventKey, EventArgs param) { if (!HasEventListener(eventKey)) return; eventListeners[eventKey](new Notification(param)); } /// <summary> /// 是否存在指定事件的監聽器 /// </summary> public bool HasEventListener(uint eventKey) { return eventListeners.ContainsKey(eventKey); } }
該消息機制的核心,難點也就是在這裏了。
首先,既然是消息處理中心,確定是須要一個存放傳遞消息(上面那個聲明的委託)的容器,因而聲明一個
private Dictionary<uint, OnNotification> eventListeners = new Dictionary<uint, OnNotification>();
增長,移除 傳遞消息(上面那個聲明的委託),不就是如下代碼,須要注意的是
eventListeners[eventKey] -= listener;//取消接收者的監聽 eventListeners.Remove(eventKey);//移除存放在在eventListeners爲eventKey的傳遞消息(上面那個委託)
if (!HasEventListener(eventKey)) { eventListeners[eventKey] = listener; //註冊接收者的監聽 } else { eventListeners[eventKey] += listener; //註冊接收者的監聽,這個用法,是委託的一種機制,不理解的本身去百度看看委託咋回事。 }
這樣,如何存儲消息作完了。
/// <summary> /// 消息類型,枚舉列出,調用時須要強轉爲uint /// </summary> public enum ENotificationMsgType // 消息發送的枚舉值,應該轉爲uint型 { ENull = 0, //Test
ELoadResProgress = 1, }
以上代碼,寫枚舉,純是爲了提升代碼可讀性及可維護性,C#中多寫枚舉,少寫那種莫名其妙的 int變量,真心感謝第一家公司對個人影響,保持良好的代碼可讀性。
EventArgsTest args = new EventArgsTest(); args.id = 3; args.name = "我是Test發送的 name 消息哦"; NotificationCenter.Get().PostDispatchEvent((uint)ENotificationMsgType.ENull, args); // NotificationCenter.Get().PostDispatchEvent((uint)ENotificationMsgType.ENull); //我就是通知,不發送具體啥消息,也是能夠的哦
這邊須要理解的是 PostDispatchEvent,這個方法,這邊我 寫了三重重載,由於發送消息分三種狀況,如註釋那樣
{
只須要通知發送,不須要知道發送的具體消息類型,也不須要發送者。
只須要發送具體消息類型,不須要發送者。
須要發送具體消息類型,須要發送者。
}
void Awake() { NotificationCenter.Get().AddEventListener((uint)ENotificationMsgType.ENull, UpdateTest); } void OnDestroy() { NotificationCenter.Get().RemoveEventListener((uint)ENotificationMsgType.ENull, UpdateTest); } void UpdateTest(Notification e) { EventArgsTest args = e.param as EventArgsTest; if (args != null) { string strName = args.name; int strId = args.id; } }
可能你會奇怪,註冊事件和移除事件爲何這樣寫。這是一種標準寫法。
寫初始(Start),結束(OnDestroy),使得每一個消息擁有一個本身的生命週期。
(2018年12月15日,補充更新,推薦使用這個版本,可是也上述比較本質都是基本同樣的)
該版原本自siki學院:給出視頻連接 http://www.sikiedu.com/my/course/304
該版本優點:①極大減小代碼量。
②極大節省人力。
上述版本傳遞消息,自定義 EventArgs的子類,遇到特殊類型,聲明的類的類型不少,增大了代碼量,這是極大的弊端。
該版本也是我無心間看到的,感受很不錯,特來補充。
最終,如如有講述不清,錯誤之處,歡迎指正。