1.先看下怎么使用的
1)腳本中注冊邏輯事件
void Start () { m_pGUICanvasObj = GameObject.Find("Canvas"); if(m_pGUICanvasObj != null) { m_pGUICanvasObj.SetActive(false); } //為當前MonoBehaviour腳本注冊對於MENU_VISIBLE_CHANGED事件的監聽器 EventDispatchModule.GetSingletonInstance().RegisterEventHandler(LOGIC_EVENT_ID.MENU_VISIBLE_CHANGED,this); }
2)腳本銷毀時移除
void OnDestroy() {
//當腳本銷毀時,移除監聽器 EventDispatchModule.GetSingletonInstance().RemoveEventHandler(LOGIC_EVENT_ID.MENU_VISIBLE_CHANGED,this); }
3)發送全局邏輯事件
public void Btn_BackToGame() { m_bShowMenu = false; //這里全局邏輯事件傳遞的參數可以是任意類型的,以引用傳遞 EventDispatchModule.SendWrap<bool>(LOGIC_EVENT_ID.MENU_VISIBLE_CHANGED,ref m_bShowMenu); }
4)響應邏輯事件(這里暫時比較挫,沒有以回調的形式體現,后面再改)
public bool HandMessage(LOGIC_EVENT_ID iLogicEventID, ref object obj) { switch(iLogicEventID) { case LOGIC_EVENT_ID.MENU_VISIBLE_CHANGED: { OnMenuVisibleChanged(); return true;//return true 表示其它監聽器可以繼續處理該事件,false表示吞噬事件 } default: break; } return true; }
2.實現細節
1)所有需要監聽消息的類,實現EventListener接口
public interface EventListener { //return true-continue. //return false-end. bool HandMessage(LOGIC_EVENT_ID iLogicEventID,ref object obj); }
2)附上上文的XCraft.CtrlScript腳本代碼

using UnityEngine; using System.Collections; using XCraft.HighLevelManager; using XCraft.Module; namespace XCraft.CtrlScript { public class GUICtrl : MonoBehaviour ,EventListener{ private bool m_bShowMenu = false; private GameObject m_pGUICanvasObj = null; public bool HandMessage(LOGIC_EVENT_ID iLogicEventID, ref object obj) { switch(iLogicEventID) { case LOGIC_EVENT_ID.MENU_VISIBLE_CHANGED: { OnMenuVisibleChanged(); return true; } default: break; } return true; } void OnDestroy() { EventDispatchModule.GetSingletonInstance().RemoveEventHandler(LOGIC_EVENT_ID.MENU_VISIBLE_CHANGED,this); } // Use this for initialization void Start () { m_pGUICanvasObj = GameObject.Find("Canvas"); if(m_pGUICanvasObj != null) { m_pGUICanvasObj.SetActive(false); } EventDispatchModule.GetSingletonInstance().RegisterEventHandler(LOGIC_EVENT_ID.MENU_VISIBLE_CHANGED,this); } // Update is called once per frame void Update () { ApplicationManager.GetSingletonInstance().UpdateInstances(Time.deltaTime); if(Input.GetKeyDown(KeyCode.Escape)) { m_bShowMenu = !m_bShowMenu; EventDispatchModule.SendWrap<bool>(LOGIC_EVENT_ID.MENU_VISIBLE_CHANGED,ref m_bShowMenu); } } private void OnMenuVisibleChanged() { if(m_bShowMenu == true) { Debug.Log("Show Menu"); if(m_pGUICanvasObj != null) { m_pGUICanvasObj.SetActive(true); } } else { Debug.Log("Close Menu"); if(m_pGUICanvasObj != null) { m_pGUICanvasObj.SetActive(false); } } } public void Btn_BackToGame() { m_bShowMenu = false; EventDispatchModule.SendWrap<bool>(LOGIC_EVENT_ID.MENU_VISIBLE_CHANGED,ref m_bShowMenu); } public void Btn_SaveAndReturn() { Application.LoadLevel("TitleScene"); } } }
3)消息派發中心EventDispatchModule
用Dictionary保存關於邏輯事件ID和事件監聽者列表的 鍵值對,監聽者列表用HashSet

1 using UnityEngine; 2 using System.Collections.Generic; 3 using UnityEngine.Assertions; 4 public interface EventListener 5 { 6 //return true-continue. 7 //return false-end. 8 bool HandMessage(LOGIC_EVENT_ID iLogicEventID,ref object obj); 9 } 10 namespace XCraft.Module 11 { 12 public class EventDispatchModule : Singleton<EventDispatchModule>,ModuleInterface 13 { 14 private Dictionary< LOGIC_EVENT_ID,HashSet<EventListener> > m_mapEventListners = null; 15 public EventDispatchModule() 16 { 17 m_mapEventListners = new Dictionary< LOGIC_EVENT_ID, HashSet<EventListener> >(); 18 } 19 20 ~EventDispatchModule(){} 21 22 public void Init() 23 { 24 25 } 26 27 public void Start() 28 { 29 30 } 31 32 public void Update(float deltaSecond) 33 { 34 35 } 36 37 //In order to improve the efficiency, use generic and reference --HuangXufeng 38 static public void SendWrap<T>(LOGIC_EVENT_ID iLogicEventID,ref T args) 39 { 40 EventArgs<T> evtArgs = new EventArgs<T>(iLogicEventID,ref args); 41 object evtObj = (object)evtArgs; 42 EventDispatchModule.GetSingletonInstance().SendMessage(iLogicEventID,ref evtObj); 43 } 44 45 public void SendMessage(LOGIC_EVENT_ID iLogicEventID,ref object objArgs) 46 { 47 HashSet<EventListener> listenerSet = null; 48 if(m_mapEventListners.TryGetValue(iLogicEventID,out listenerSet)) 49 { 50 if(listenerSet == null) 51 { 52 Assert.IsTrue (false); 53 return; 54 } 55 56 foreach(EventListener evtListener in listenerSet) 57 { 58 if(false == evtListener.HandMessage(iLogicEventID,ref objArgs)) 59 { 60 break; 61 } 62 } 63 } 64 else 65 { 66 //iLogicEventID do not Register Listener 67 Assert.IsTrue(false); 68 } 69 } 70 71 public void RegisterEventHandler(LOGIC_EVENT_ID iLogicEventID,EventListener IEventListenerInstance) 72 { 73 HashSet<EventListener> listenerSet = null; 74 if(m_mapEventListners.TryGetValue(iLogicEventID,out listenerSet)) 75 { 76 if(listenerSet == null) 77 { 78 Assert.IsTrue(false); 79 return; 80 } 81 82 Assert.IsTrue(listenerSet.Add(IEventListenerInstance)); 83 } 84 //if listener list == null ,create one 85 else 86 { 87 HashSet<EventListener> newHashSet = new HashSet<EventListener>(){IEventListenerInstance}; 88 m_mapEventListners.Add(iLogicEventID,newHashSet); 89 } 90 } 91 92 public void RemoveEventHandler(LOGIC_EVENT_ID iLogicEventID,EventListener IEventListenerInstance) 93 { 94 HashSet<EventListener> listenerSet = null; 95 if(m_mapEventListners.TryGetValue(iLogicEventID,out listenerSet)) 96 { 97 if(listenerSet == null) 98 { 99 Assert.IsTrue(false); 100 return; 101 } 102 103 Assert.IsTrue(listenerSet.Remove(IEventListenerInstance)); 104 } 105 else 106 { 107 Assert.IsTrue(false); 108 } 109 } 110 } 111 }
3.小結
為了支持邏輯事件參數可以是基礎類型、類類型,用了C#的泛型,感覺相比C++仍缺失很多靈活度(如果是C++中可以直接將任意類型轉Void*,然后在HandleMessage處再強轉回去,靈活許多,而且C#引用 ref關鍵字 調用和函數聲明處都得寫- -!!)
上文舉例時用了繼承MonoBehaviour的GUICtrl作為示范,當然不繼承MonoBehaviour也是可以的,只是需要注意在監聽者生命周期結束后,RemoveEvent就行。