前言 事件模块是游戏中很重要的一个模块,而GameFramework的事件模块在网上能找到很多文章,但基本都是讲解其应用方面的,并没有对其源码进行分析。 这里阐述此模块通过如下三个方面:基本的类内信息说明,GF里面存储管理事件的形式,事件抛出和取消订阅的冲突问题。 这里就不用应用举例了,有需要可以查看下面参考文档。
Event模块结构
从上面的结构图也可以看出,EventManager作为事件模块的管理器,其作用主要是对于EventPool的功能进行封装,并提供对应的接口给外部使用。因此本模块的核心逻辑主要在EventPool中。
EventPool 作为事件模块的核心, EventPool处理着事件的发布、订阅、移除等功能的同时也会严格检查事件订阅的匹配情况,不允许出现重复订阅,也不允许出现重复取消订阅或取消订阅尚未订阅的事件处理函数,如果出现这些情况,将会抛出异常。 观察者模式可以从“发布者”和“订阅者”两个角度来看,EventPool里面对应的两个方面便是 “事件” 和 “事件处理函数” 。事件处理函数是订阅者注册给发布者的,一个事件可以包含多个事件处理函数。抛出一个事件,会执行其所有订阅者的事件处理函数。
类内信息
字段功能说明:
m_EventHandlers:GF多值字典。key为事件ID,value为事件处理函数的列表,毕竟一个事件可以有多个订阅者,GF是通过多值字典来存储的。
m_Events:事件队列。这里的事件(Event)是EventPool中的一个内部类,主要用于记录当前正在抛出的事件。因为其在Update里面执行,可以确保安全性,保证在主线程中回调处理函数,但事件会在抛出后的下一帧分发。
m_CachedNotes:事件节点缓存字典。缓存正在抛出的事件节点,用于解决正在抛出事件节点和取消订阅事件节点的冲突问题。通过检测m_CachedNotes里面的数据可以判断当前要取消订阅的事件是否正在被抛出。
m_TempNodes:临时事件节点列表。缓存m_CachedNotes里面的节点。在取消订阅的时候,如果当前取消的事件正在被抛出的话,就缓存下一个节点到m_TempNodes,然后给m_cachedNotes里面覆盖赋值,跳过这一个节点的事件派发。
m_EventPoolMode:事件池模式。这里的模式主要用于控制事件池的事件处理函数的执行模型,参考EventPoolMode枚举分为四种:
Default,默认事件池模式,即必须存在有且只有一个事件处理函数。
AllowNoHandler,允许不存在事件处理函数。
AllowMultiHandler,允许存在多个事件处理函数。
AllowDuplicateHandler,允许存在重复的事件处理函数。 一般的话使用的是EventPoolMode.AllowNoHandler | EventPoolMode.AllowMultiHandler 这两个模式
m_DefaultHandler:默认额外事件处理函数。通过给这个委托赋值,可以设置一个额外的的事件处理函数,当抛出事件后,如果有此委托有内容,就会调用,方便进行拓展。
方法说明:
Update:生命周期里轮询方法。这里会检测事件列表里面的事件进行派发,并调用对应的事件处理函数。
Shutdown:关闭并清理事件池。
Clear:清空当前待处理的事件列表。
Count:获取对应事件的事件处理函数的数量。
Check:检查对应事件是否存在事件处理函数。
Subscribe:订阅事件。
Unsubscribe:取消订阅事件。
SetDefaultHandler:设置默认额外事件处理函数。
Fire和FireNow: 都是抛出事件,Fire会走上面的流程。而FireNow则是立刻执行,这个操作不是线程安全的,但事件会立刻分发。
HandleEvent:处理事件和事件函数的核心方法。
EventPool源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 using System;using System.Collections.Generic;namespace GameFramework { internal sealed partial class EventPool <T > where T : BaseEventArgs { private readonly GameFrameworkMultiDictionary<int , EventHandler<T>> m_EventHandlers; private readonly Queue<Event> m_Events; private readonly Dictionary<object , LinkedListNode<EventHandler<T>>> m_CachedNodes; private readonly Dictionary<object , LinkedListNode<EventHandler<T>>> m_TempNodes; private readonly EventPoolMode m_EventPoolMode; private EventHandler<T> m_DefaultHandler; public EventPool (EventPoolMode mode ) { m_EventHandlers = new GameFrameworkMultiDictionary<int , EventHandler<T>>(); m_Events = new Queue<Event>(); m_CachedNodes = new Dictionary<object , LinkedListNode<EventHandler<T>>>(); m_TempNodes = new Dictionary<object , LinkedListNode<EventHandler<T>>>(); m_EventPoolMode = mode; m_DefaultHandler = null ; } public int EventHandlerCount { get { return m_EventHandlers.Count; } } public int EventCount { get { return m_Events.Count; } } public void Update (float elapseSeconds, float realElapseSeconds ) { while (m_Events.Count > 0 ) { Event eventNode = null ; lock (m_Events) { eventNode = m_Events.Dequeue(); HandleEvent(eventNode.Sender, eventNode.EventArgs); } ReferencePool.Release(eventNode); } } public void Shutdown () { Clear(); m_EventHandlers.Clear(); m_CachedNodes.Clear(); m_TempNodes.Clear(); m_DefaultHandler = null ; } public void Clear () { lock (m_Events) { m_Events.Clear(); } } public int Count (int id ) { GameFrameworkLinkedListRange<EventHandler<T>> range = default (GameFrameworkLinkedListRange<EventHandler<T>>); if (m_EventHandlers.TryGetValue(id, out range)) { return range.Count; } return 0 ; } public bool Check (int id, EventHandler<T> handler ) { if (handler == null ) { throw new GameFrameworkException("Event handler is invalid." ); } return m_EventHandlers.Contains(id, handler); } public void Subscribe (int id, EventHandler<T> handler ) { if (handler == null ) { throw new GameFrameworkException("Event handler is invalid." ); } if (!m_EventHandlers.Contains(id)) { m_EventHandlers.Add(id, handler); } else if ((m_EventPoolMode & EventPoolMode.AllowMultiHandler) != EventPoolMode.AllowMultiHandler) { throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not allow multi handler." , id.ToString())); } else if ((m_EventPoolMode & EventPoolMode.AllowDuplicateHandler) != EventPoolMode.AllowDuplicateHandler && Check(id, handler)) { throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not allow duplicate handler." , id.ToString())); } else { m_EventHandlers.Add(id, handler); } } public void Unsubscribe (int id, EventHandler<T> handler ) { if (handler == null ) { throw new GameFrameworkException("Event handler is invalid." ); } if (m_CachedNodes.Count > 0 ) { foreach (KeyValuePair<object , LinkedListNode<EventHandler<T>>> cachedNode in m_CachedNodes) { if (cachedNode.Value != null && cachedNode.Value.Value == handler) { m_TempNodes.Add(cachedNode.Key, cachedNode.Value.Next); } } if (m_TempNodes.Count > 0 ) { foreach (KeyValuePair<object , LinkedListNode<EventHandler<T>>> cachedNode in m_TempNodes) { m_CachedNodes[cachedNode.Key] = cachedNode.Value; } m_TempNodes.Clear(); } } if (!m_EventHandlers.Remove(id, handler)) { throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not exists specified handler." , id.ToString())); } } public void SetDefaultHandler (EventHandler<T> handler ) { m_DefaultHandler = handler; } public void Fire (object sender, T e ) { if (e == null ) { throw new GameFrameworkException("Event is invalid." ); } Event eventNode = Event.Create(sender, e); lock (m_Events) { m_Events.Enqueue(eventNode); } } public void FireNow (object sender, T e ) { if (e == null ) { throw new GameFrameworkException("Event is invalid." ); } HandleEvent(sender, e); } private void HandleEvent (object sender, T e ) { bool noHandlerException = false ; GameFrameworkLinkedListRange<EventHandler<T>> range = default (GameFrameworkLinkedListRange<EventHandler<T>>); if (m_EventHandlers.TryGetValue(e.Id, out range)) { LinkedListNode<EventHandler<T>> current = range.First; while (current != null && current != range.Terminal) { m_CachedNodes[e] = current.Next != range.Terminal ? current.Next : null ; current.Value(sender, e); current = m_CachedNodes[e]; } m_CachedNodes.Remove(e); } else if (m_DefaultHandler != null ) { m_DefaultHandler(sender, e); } else if ((m_EventPoolMode & EventPoolMode.AllowNoHandler) == 0 ) { noHandlerException = true ; } ReferencePool.Release(e); if (noHandlerException) { throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not allow no handler." , e.Id.ToString())); } } } }
多值字典和链表范围 GameFramework的多值字典(GameFrameworkMultiDictionary)和链表范围(GameFrameworkLinkedListRange)是GameFramework的基础模块。这里的目的是为了满足同一事件多个事件处理函数的需求。是GF里面存储管理事件的形式。
先说链表范围,链表范围这个说法顾名思义,表示的就是“链表中的某个范围”。通过确定起始节点和终止节点,就可以确定一个链表范围。这里的作用是用于确定单个事件所包含哪些事件处理函数。 多值字典里面包含两个定义m_LinkedList 和 m_Dictionary:
1 2 private readonly GameFrameworkLinkedList<TValue> m_LinkedList;private readonly Dictionary<TKey, GameFrameworkLinkedListRange<TValue>> m_Dictionary;
通过m_LinkedList链表来存储所有的事件处理函数,m_Dictionary则是根据事件ID来存储对应事件处理函数的链表范围。这样做的好处便是,只需要通过一条链表就可以确定好一个事件有哪些事件处理函数,而不需要每一个事件都去单独创建一个链表去填充。
源码: GameFrameworkMultiDictionary:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 using System.Collections;using System.Collections.Generic;using System.Runtime.InteropServices;namespace GameFramework { public sealed class GameFrameworkMultiDictionary <TKey , TValue > : IEnumerable <KeyValuePair <TKey , GameFrameworkLinkedListRange <TValue >>>, IEnumerable { private readonly GameFrameworkLinkedList<TValue> m_LinkedList; private readonly Dictionary<TKey, GameFrameworkLinkedListRange<TValue>> m_Dictionary; public GameFrameworkMultiDictionary () { m_LinkedList = new GameFrameworkLinkedList<TValue>(); m_Dictionary = new Dictionary<TKey, GameFrameworkLinkedListRange<TValue>>(); } public int Count { get { return m_Dictionary.Count; } } public GameFrameworkLinkedListRange<TValue> this [TKey key] { get { GameFrameworkLinkedListRange<TValue> range = default (GameFrameworkLinkedListRange<TValue>); m_Dictionary.TryGetValue(key, out range); return range; } } public void Clear () { m_Dictionary.Clear(); m_LinkedList.Clear(); } public bool Contains (TKey key ) { return m_Dictionary.ContainsKey(key); } public bool Contains (TKey key, TValue value ) { GameFrameworkLinkedListRange<TValue> range = default (GameFrameworkLinkedListRange<TValue>); if (m_Dictionary.TryGetValue(key, out range)) { return range.Contains(value ); } return false ; } public bool TryGetValue (TKey key, out GameFrameworkLinkedListRange<TValue> range ) { return m_Dictionary.TryGetValue(key, out range); } public void Add (TKey key, TValue value ) { GameFrameworkLinkedListRange<TValue> range = default (GameFrameworkLinkedListRange<TValue>); if (m_Dictionary.TryGetValue(key, out range)) { m_LinkedList.AddBefore(range.Terminal, value ); } else { LinkedListNode<TValue> first = m_LinkedList.AddLast(value ); LinkedListNode<TValue> terminal = m_LinkedList.AddLast(default (TValue)); m_Dictionary.Add(key, new GameFrameworkLinkedListRange<TValue>(first, terminal)); } } public bool Remove (TKey key, TValue value ) { GameFrameworkLinkedListRange<TValue> range = default (GameFrameworkLinkedListRange<TValue>); if (m_Dictionary.TryGetValue(key, out range)) { for (LinkedListNode<TValue> current = range.First; current != null && current != range.Terminal; current = current.Next) { if (current.Value.Equals(value )) { if (current == range.First) { LinkedListNode<TValue> next = current.Next; if (next == range.Terminal) { m_LinkedList.Remove(next); m_Dictionary.Remove(key); } else { m_Dictionary[key] = new GameFrameworkLinkedListRange<TValue>(next, range.Terminal); } } m_LinkedList.Remove(current); return true ; } } } return false ; } public bool RemoveAll (TKey key ) { GameFrameworkLinkedListRange<TValue> range = default (GameFrameworkLinkedListRange<TValue>); if (m_Dictionary.TryGetValue(key, out range)) { m_Dictionary.Remove(key); LinkedListNode<TValue> current = range.First; while (current != null ) { LinkedListNode<TValue> next = current != range.Terminal ? current.Next : null ; m_LinkedList.Remove(current); current = next; } return true ; } return false ; } public Enumerator GetEnumerator () { return new Enumerator(m_Dictionary); } IEnumerator<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>> IEnumerable<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>>.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } [StructLayout(LayoutKind.Auto) ] public struct Enumerator : IEnumerator<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>>, IEnumerator { private Dictionary<TKey, GameFrameworkLinkedListRange<TValue>>.Enumerator m_Enumerator; internal Enumerator (Dictionary<TKey, GameFrameworkLinkedListRange<TValue>> dictionary ) { if (dictionary == null ) { throw new GameFrameworkException("Dictionary is invalid." ); } m_Enumerator = dictionary.GetEnumerator(); } public KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>> Current { get { return m_Enumerator.Current; } } object IEnumerator.Current { get { return m_Enumerator.Current; } } public void Dispose () { m_Enumerator.Dispose(); } public bool MoveNext () { return m_Enumerator.MoveNext(); } void IEnumerator.Reset() { ((IEnumerator<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>>)m_Enumerator).Reset(); } } } }
GameFrameworkLinkedListRange:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 using System.Collections;using System.Collections.Generic;using System.Runtime.InteropServices;namespace GameFramework { [StructLayout(LayoutKind.Auto) ] public struct GameFrameworkLinkedListRange<T> : IEnumerable<T>, IEnumerable { private readonly LinkedListNode<T> m_First; private readonly LinkedListNode<T> m_Terminal; public GameFrameworkLinkedListRange (LinkedListNode<T> first, LinkedListNode<T> terminal ) { if (first == null || terminal == null || first == terminal) { throw new GameFrameworkException("Range is invalid." ); } m_First = first; m_Terminal = terminal; } public bool IsValid { get { return m_First != null && m_Terminal != null && m_First != m_Terminal; } } public LinkedListNode<T> First { get { return m_First; } } public LinkedListNode<T> Terminal { get { return m_Terminal; } } public int Count { get { if (!IsValid) { return 0 ; } int count = 0 ; for (LinkedListNode<T> current = m_First; current != null && current != m_Terminal; current = current.Next) { count++; } return count; } } public bool Contains (T value ) { for (LinkedListNode<T> current = m_First; current != null && current != m_Terminal; current = current.Next) { if (current.Value.Equals(value )) { return true ; } } return false ; } public Enumerator GetEnumerator () { return new Enumerator(this ); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } [StructLayout(LayoutKind.Auto) ] public struct Enumerator : IEnumerator<T>, IEnumerator { private readonly GameFrameworkLinkedListRange<T> m_GameFrameworkLinkedListRange; private LinkedListNode<T> m_Current; private T m_CurrentValue; internal Enumerator (GameFrameworkLinkedListRange<T> range ) { if (!range.IsValid) { throw new GameFrameworkException("Range is invalid." ); } m_GameFrameworkLinkedListRange = range; m_Current = m_GameFrameworkLinkedListRange.m_First; m_CurrentValue = default (T); } public T Current { get { return m_CurrentValue; } } object IEnumerator.Current { get { return m_CurrentValue; } } public void Dispose () { } public bool MoveNext () { if (m_Current == null || m_Current == m_GameFrameworkLinkedListRange.m_Terminal) { return false ; } m_CurrentValue = m_Current.Value; m_Current = m_Current.Next; return true ; } void IEnumerator.Reset() { m_Current = m_GameFrameworkLinkedListRange.m_First; m_CurrentValue = default (T); } } } }
解决抛出和取消订阅同一事件的冲突 上面也说到,在事件抛出的时候,如果收到了此事件的取消订阅,这时候就会出现冲突,这里的处理方式是通过m_CachedNodes和m_TempNodes两个事件节点缓存字典来解决。如果但看HandleEvent方法,就会发现这里的缓存其实是没有绝对的功能执行必要的,而之所以添加的目的也就是添加判断冲突存在的“标识”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 private void HandleEvent (object sender, T e ){ bool noHandlerException = false ; GameFrameworkLinkedListRange<EventHandler<T>> range = default (GameFrameworkLinkedListRange<EventHandler<T>>); if (m_EventHandlers.TryGetValue(e.Id, out range)) { LinkedListNode<EventHandler<T>> current = range.First; while (current != null && current != range.Terminal) { m_CachedNodes[e] = current.Next != range.Terminal ? current.Next : null ; current.Value(sender, e); current = m_CachedNodes[e]; } m_CachedNodes.Remove(e); } else if (m_DefaultHandler != null ) { m_DefaultHandler(sender, e); } else if ((m_EventPoolMode & EventPoolMode.AllowNoHandler) == 0 ) { noHandlerException = true ; } ReferencePool.Release(e); if (noHandlerException) { throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not allow no handler." , e.Id.ToString())); } } public void Unsubscribe (int id, EventHandler<T> handler ){ if (handler == null ) { throw new GameFrameworkException("Event handler is invalid." ); } if (m_CachedNodes.Count > 0 ) { foreach (KeyValuePair<object , LinkedListNode<EventHandler<T>>> cachedNode in m_CachedNodes) { if (cachedNode.Value != null && cachedNode.Value.Value == handler) { m_TempNodes.Add(cachedNode.Key, cachedNode.Value.Next); } } if (m_TempNodes.Count > 0 ) { foreach (KeyValuePair<object , LinkedListNode<EventHandler<T>>> cachedNode in m_TempNodes) { m_CachedNodes[cachedNode.Key] = cachedNode.Value; } m_TempNodes.Clear(); } } if (!m_EventHandlers.Remove(id, handler)) { throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not exists specified handler." , id.ToString())); } }
参考文档