Unity3D 全局逻辑事件/消息派发 实现
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就行。
posted on 2016-04-14 18:06 kyokuhuang 阅读(3256) 评论(0) 编辑 收藏 举报