在Unity提供的事件注册基础上,实现一个更加灵活的事件系统。
首先,为所有可能的事件定义一个公用的事件基类。该基类包含:事件的发起者(UNITY对象)、要求响应的方式(同步或异步)以及事件是否在处理后销毁。
public abstract class Event : IDisposable
{
private GameObject publisher;
private bool responce;
private bool disposeAfterUsed;
}
那么,任何类型只要继承自Event,就可以作为一个事件在MessageSystem里面进行发布和监听。当然,如果每种事件都单独写一个类型,或许会使得类的数量过多,可以将同模块下的所有事件封装到一个Event的子类当中,再通过字符串或枚举等方式进行二次查找。比如如下方式:
public class NetWorkConnectionEvent : Event{
...
enum Type{
EventType1,
EventType2,
...
}
...
}
如果使用这种写法,MessageSystem会更加复杂,为了方便,我们在下面假设所有的事件都继承自Event,每个Event代表且仅代表一个事件。
一个类型实现该接口,以表明自己是一个可以监听事件的类型。该接口需要实现监听函数
public interface IEventListener
{
bool OnEvent(Event e);
}
一个基础的事件系统应当至少能够实现事件的发布和订阅功能。首先,我们定义以下几个列表,来储存订阅者信息。
private Dictionary<Event, List<IEventListener>> Listeners;
private List<KeyValuePair<Event, IEventListener> subscribeRequests;
private List<KeyValuePair<Event, IEventListener> > unsubscribeRequests
字典Listeners储存了注册到某个事件的所有监听者,用法很清晰,那么下面两个列表用处何在呢?由于场景中随时都会存在动态地事件注册和取消注册,因此,下面两个变量用于在MessageSystem.Update函数中,将新注册的Listener更新到Listener中,或从Listener中移除。当Listener的某个Key对应的Value为空时(即某个Event未被任何监听者注册),那么这个KEY将会被移除,相应地,而若新注册的监听者发现Listener中没有对应的Event,也会新增一个对应的Key。用这种动态的注册方法,可以避免事件系统中出现大量的无用注册信息以提高运行效率。
在Subscribe/Unsubscribe中,将需要Subscribe/Unsubscribe的监听者加入对应的列表。
subscribeRequests.Add(new KeyValuePair<Event, IEventListener>(e, listener);
unsubscribeRequests.Add(new KeyValuePair<Event, IEventListener>(e, listener);
在Update中,将它们更新到Listener:
foreach (var subReq in subscribeRequests)
{
List<IEventListener> callbacks = null;
if (Listeners.TryGetValue(subReq.Key, out callbacks))
{
callbacks.Add(subReq.Value);
}
else
{
callbacks = new List<IEventListener>();
callbacks.Add(subReq.Value);
listeners.Add(subReq.Key, callbacks);
}
}
subscribeRequests.Clear();
var infoToUnsubscribe = new SubscribeCallbackInfo(null, false);
foreach (var unsubReq in unsubscribeRequests)
{
List<IEventListener> callbacks = null;
if (Listeners.TryGetValue(unsubReq.Key, out callbacks))
{
if (callbacks.Remove(unsubReq.Value))
{
if (callbacks.Count == 0)
{
subscribeCallbacks.Remove(unsubReq.Key);
}
}
}
}
unsubscribeRequests.Clear();
现在已经有了订阅信息,需要一个Publish函数来发布事件。
public void Publish(Event e);
发布者决定了事件的处理方式(同步-异步)以及销毁方式,对于异步调用,直接循环遍历Listener中对应的List即可:
List<IEventListener> listeners;
if(Listeners.TryGetValue(e, out listeners)){
for(listener in listeners){
listener.OnEvent(e);
}
}
对于同步调用,用StartCoroutine协程代替直接调用:
StartCoroutine(listener.OnEvent(e));
有些时候,我们只需要事件被任何一个监听者处理掉就够了,而不需要所有监听者都去处理它,因此,发布者可以将Event.disposeAfterUsed置为true,在publish中,只要某一个监听者处理了事件,那么销毁该事件,不需要将其向下传递。
List<IEventListener> listeners;
if(Listeners.TryGetValue(e, out listeners)){
for(listener in listeners){
if(listener.OnEvent(e)){
e.dispose();
break;
}
}
}
更新时间:2022.7.17