Unity简单事件系统管理
发表于2019-01-28
事件系统的好处很多,最主要的还是能够最大限度的降低模块间的耦合度,这里实现一个很简单的事件管理系统,主要功能就是对事件进行分发,以达到解偶的目的。事件系统很多新手好像不太适应,但是用的多了,就会发现它的神奇之处。
直接上代码:
public class EventManager : MonoBehaviour{ Dictionary<EventID,List<EventObserver>> observerList = new Dictionary<EventID,List<EventObserver>>(); Queue eventQueue = new Queue(); //消息队列 private static EventManager _instance = null; public static EventManager instance() { return _instance; } void Awake(){ Debug.Log("===========启动消息系统==========="); _instance = this; } void Update(){ while(eventQueue.Count > 0){ EventData eve = (EventData)eventQueue.Dequeue(); if(!observerList.ContainsKey(eve.eid)){ continue; } List<EventObserver> observers = observerList[eve.eid]; for(int i = 0 ; i < observers.Count ; i ++){ if(observers[i] == null) continue; observers[i].HandleEvent(eve); } } } //发送事件 public void SendEvent(EventData eve){ eventQueue.Enqueue(eve); } //添加监听者 void RegisterObj(EventObserver newobj,EventID eid){ if(!observerList.ContainsKey(eid)){ List<EventObserver> list = new List<EventObserver>(); list.Add(newobj); observerList.Add(eid,list); }else{ List<EventObserver> list = observerList[eid]; foreach(EventObserver obj in list){ if(obj == newobj){ return; } } list.Add(newobj); } } //移除监听者 void RemoveObj(EventObserver removeobj){ foreach (KeyValuePair<EventID,List<EventObserver>> kv in observerList) { List<EventObserver> list = kv.Value; foreach(EventObserver obj in list){ if(obj == removeobj){ list.Remove(obj); break; } } } } /// <summary> /// 移除一个监听者 /// </summary> /// <returns>The remove.</returns> /// <param name="removeobj">Removeobj.</param> public static void Remove(EventObserver removeobj){ if(EventManager.instance() == null)return; EventManager.instance().RemoveObj(removeobj); } /// <summary> /// 监听者在这里注册 /// </summary> /// <returns>The register.</returns> /// <param name="newobj">Newobj.</param> /// <param name="eids">需要监听的事件列表.</param> public static void Register(EventObserver newobj,params EventID[] eids){ if(EventManager.instance() == null)return; foreach (EventID eid in eids) { EventManager.instance().RegisterObj(newobj, eid); } } }
EventManager必须要挂载到unity物体上,并且不能被销毁。这里应该也可以独立开启一个线程来进行消息的分发,但是应该会更复杂一些,因为涉及到线程间的数据处理。以后有机会我也会实现一下。
提供给用户访问的就2个静态方法,注册和注销监听者。
我们还需要定义事件id,这里用枚举来实现:
public enum EventID { EVENT_1 = 10001, EVENT_2 = 10002, }
实现消息的实体:
public class EventData { public EventID eid; public EventData(EventID eid){ this.eid = eid; } public void Send(){ if(EventManager.instance() != null)EventManager.instance().SendEvent(this); } public static EventData CreateEvent(EventID eventid){ EventData data = new EventData(eventid); return data; } } public class Event1 : EventData { public int t1 { get; private set; } public int t2 { get; private set; } public string s1 { get; private set; } public Event1(int t1,int t2,string s1) : base(EventID.EVENT_1) { this.t1 = t1; this.t2 = t2; this.s1 = s1; } }
这里EventData是基础的消息类,一些不需要参数的消息可以直接使用,下面我们还实现的一个Event1,代参数的消息,所有消息都需要继承EventData类。
接下来我们还需要定义一个监听者用来接收消息的接口:
public interface EventObserver { void HandleEvent(EventData resp); }
所有的监听者都要实现HandleEvent方法。
下面我们来看一下如何接收和发送消息,创建脚本EventTest:
public class EventTest : MonoBehaviour ,EventObserver{
public string uname = "aaa";
public string password = "bbb";
// Use this for initialization
void Start () {
EventManager.Register(this,EventID.EVENT_1,EventID.EVENT_2);
}
// Update is called once per frame
void Update () {
}
void OnGUI ()
{
if (GUI.Button (new Rect (10,10,150,50), "send eve1"))
{
Event1 eve = new Event1(1,2,"123");
eve.Send();
}
if (GUI.Button(new Rect(10, 70, 150, 50), "send eve2"))
{
EventData.CreateEvent(EventID.EVENT_2).Send();
}
}
public void HandleEvent(EventData data){
switch(data.eid){
case EventID.EVENT_1:
Event1 eve = data as Event1;
Debug.Log("receive event_1 msg!");
Debug.Log(string.Format("t1:{0},t2:{1},s1:{2}",eve.t1,eve.t2,eve.s1) );
break;
case EventID.EVENT_2:
Debug.Log("receive event_2 msg!");
break;
}
}
void OnDestroy(){
EventManager.Remove(this);
}
}
首先要继承EventObserver接口,然后在Start方法中进行注册,OnDestroy方法中进行注销。然后实现HandleEvent方法对消息进行处理。
把EventTest放到任意物体上并运行场景:
当分别点击2个按钮时我们可以在控制台看到相应的输出: