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");
        }

    }
}
    
View Code

 

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 }
View Code

 

 

 

 

3.小结

为了支持逻辑事件参数可以是基础类型、类类型,用了C#的泛型,感觉相比C++仍缺失很多灵活度(如果是C++中可以直接将任意类型转Void*,然后在HandleMessage处再强转回去,灵活许多,而且C#引用 ref关键字 调用和函数声明处都得写- -!!)

上文举例时用了继承MonoBehaviour的GUICtrl作为示范,当然不继承MonoBehaviour也是可以的,只是需要注意在监听者生命周期结束后,RemoveEvent就行。

 

posted on 2016-04-14 18:06  kyokuhuang  阅读(3256)  评论(0编辑  收藏  举报

导航