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个按钮时我们可以在控制台看到相应的输出:

