在游戏开发过程中,经常会出现不同板块之间的信息交流,或是存在“当...,就...”的情况,事件队列编程模式可以有效解决消息传递中产生的脚本耦合问题,让同一个板块的脚本更加单纯,不包含其他脚本的杂质内容,使脚本更容易最大程度的复用。
事件队列模式的运行流程如下:
1.当一个行为(Action)触发了某一事件(Event)后,不是直接调用该事件,而是改为申请将其提交给广播中心,也就是将自己的行为推入广播材料的队列末尾。
2.由中间的的广播中心(事件队列处理系统)统一管理播报这些行为,并且按队列顺利先后播报,播报完了没有广播材料(队列为空)了就停下来摸鱼。
3.关心这些行为的观众会向广播中心注册一个侦听器(买个收音机听广播中心的播报),听到自己感兴趣的,就自发执行相应事件。
4.哪一天这个观众烦了就把收音机砸了,这样侦听器就被移除了,以后无论再发生什么都跟自己没关系。
所以,核心就是要建立这么个广播中心,这个广播中心要能:
1.把稿子交过来(事件队列入队)
2.广播材料,例如不好啦京阿尼被烧了,播完后把稿子扔了(触发事件,事件队列出队)
3.查看,管理收听情况,谁谁谁在听啥(申请注册,移除)
知道这些之后,就可以来建造这个一个广播中心了,为了提升人气,谁都可以来一下,这个广播中心需要接受各式各样的爆料,所以要用到泛型委托;
而且这个广播中心是全世界独一无二的,不能有好几个实例,大家都要从我这过,所以要用到单例;
关于单例,可以看之前写的博客,非常好用的单例:
https://www.cnblogs.com/koshio0219/p/11203631.html
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.Events; 5 using System; 6 7 public class GameEvent:UnityEvent{ } 8 9 public class EventQueueSystem : MonoSingleton<EventQueueSystem> 10 { 11 public delegate void EventDelegate<T>(T e) where T : GameEvent; 12 13 private delegate void InternalEventDelegate(GameEvent e); 14 15 private Dictionary<Type, InternalEventDelegate> delegates = new Dictionary<Type, InternalEventDelegate>(); 16 private Dictionary<Delegate, InternalEventDelegate> delegateLookup = new Dictionary<Delegate, InternalEventDelegate>(); 17 private Dictionary<InternalEventDelegate, Delegate> delegateLookOnce = new Dictionary<InternalEventDelegate, Delegate>(); 18 19 private Queue eventQueue = new Queue(); 20 21 public bool bLimitQueueProcessing = false; 22 public float limitQueueTime = 0.1f; 23 24 //注册侦听事件(持续) 25 public static void AddListener<T>(EventDelegate<T> del) where T : GameEvent 26 { 27 Instance.AddDelegate(del); 28 } 29 30 //注册侦听事件(一次) 31 public static void AddListenerOnce<T>(EventDelegate<T> del) where T : GameEvent 32 { 33 var result = Instance.AddDelegate(del); 34 if (result != null) 35 Instance.delegateLookOnce[result] = del; 36 } 37 38 //判定侦听事件是否存在 39 public static bool HasListener<T>(EventDelegate<T> del) where T : GameEvent 40 { 41 return Instance.delegateLookup.ContainsKey(del); 42 } 43 44 //移除侦听事件 45 public static void RemoveListener<T>(EventDelegate<T> del) where T : GameEvent 46 { 47 if (Instance == null) 48 return; 49 if (Instance.delegateLookup.TryGetValue(del, out InternalEventDelegate eventDelegate)) 50 { 51 if (Instance.delegates.TryGetValue(typeof(T), out InternalEventDelegate temp)) 52 { 53 temp -= eventDelegate; 54 if (temp == null) 55 Instance.delegates.Remove(typeof(T)); 56 else 57 Instance.delegates[typeof(T)] = temp; 58 } 59 Instance.delegateLookup.Remove(del); 60 } 61 } 62 63 public static void RemoveAll() 64 { 65 if (Instance != null) 66 { 67 Instance.delegates.Clear(); 68 Instance.delegateLookup.Clear(); 69 Instance.delegateLookOnce.Clear(); 70 } 71 } 72 73 private InternalEventDelegate AddDelegate<T>(EventDelegate<T> del) where T : GameEvent 74 { 75 if (delegateLookup.ContainsKey(del)) 76 return null; 77 void eventDelegate(GameEvent e) => del((T)e); 78 delegateLookup[del] = eventDelegate; 79 80 if (delegates.TryGetValue(typeof(T), out InternalEventDelegate temp)) 81 delegates[typeof(T)] = temp += eventDelegate; 82 else 83 delegates[typeof(T)] = eventDelegate; 84 return eventDelegate; 85 } 86 87 //单个事件触发 88 private static void TriggerEvent(GameEvent e) 89 { 90 var type = e.GetType(); 91 if(Instance.delegates.TryGetValue(type,out InternalEventDelegate eventDelegate)) 92 { 93 eventDelegate.Invoke(e); 94 //移除单一侦听 95 foreach(InternalEventDelegate item in Instance.delegates[type].GetInvocationList()) 96 { 97 if (Instance.delegateLookOnce.TryGetValue(item,out Delegate temp)) 98 { 99 Instance.delegates[type] -= item; 100 if (Instance.delegates[type] == null) 101 Instance.delegates.Remove(type); 102 Instance.delegateLookup.Remove(temp); 103 Instance.delegateLookOnce.Remove(item); 104 } 105 } 106 } 107 } 108 109 //外部调用的推入事件队列接口 110 public static void QueueEvent(GameEvent e) 111 { 112 if (!Instance.delegates.ContainsKey(e.GetType())) 113 return; 114 Instance.eventQueue.Enqueue(e); 115 } 116 117 //事件队列触发处理 118 void Update() 119 { 120 float timer = 0.0f; 121 while (eventQueue.Count > 0) 122 { 123 if (bLimitQueueProcessing) 124 if (timer > limitQueueTime) 125 return; 126 var e = eventQueue.Dequeue() as GameEvent; 127 TriggerEvent(e); 128 if (bLimitQueueProcessing) 129 timer += Time.deltaTime; 130 } 131 } 132 133 private void OnApplicationQuit() 134 { 135 RemoveAll(); 136 eventQueue.Clear(); 137 } 138 }