游戏项目中的事件系统

🗨️字数统计=1.1k字 ⏳阅读时长≈5分钟

项目中的事件系统

成熟的项目中对自己的事件都会进行封装,使得过程透明,使用者不必去了解其构成,只需要关心分发出来的事件,而事件的发出者也不关心谁会用到,只需要将事件发出即可。事件系统维系着项目中所有事件的分发,将游戏逻辑解耦,使得项目逻辑清晰,代码简单易懂。

通常事件系统并不复杂,反而很简单,包含了三个核心的方法:

  • 注册 AddHander

  • 注销 RemoveHander

  • 发送 SendEvent

使用枚举来作为事件类型

1
public enum EEventType{}

事件是基于委托的,一个事件对应于多个委托delegate,利用委托的性质,事件系统将更加的简单明了。

1
2
3
4
//委托 事件的回调 对应于事件的数据结构
public delegate void EventSysCallBack(EEventType eventId, object param1, object param2);
public delegate void EventSysCallBack_(uint eventId, object param1, object param2);
public delegate void EmptyCallBack();

事件类的数据结构定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class EventParamData
{
private EEventType m_eventId = 0;
private object m_param1 = null;
private object m_param2 = null;

public EventParamData(EEventType eventId, object param1, object param2)
{
m_eventId = eventId;
m_param1 = param1;
m_param2 = param2;
}

public EEventType GetEventId() { return m_eventId; }
public object GetParam1() { return m_param1; }
public object GetParam2() { return m_param2; }
}

接下来就是对于事件系统类EventSys的实现,整个系统继承于ISystem,ISystem继承于MonoBehaviour。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ISystem : MonoBehaviour
{
public virtual void Init() { }
public void BaseReset() { Reset(); }
public virtual void Reset() { }
public virtual void Release() { }
public virtual void SysUpdate() { }
public virtual void SysLateUpdate() { }
public virtual void SysFixedUpdate() { }
protected bool m_isControlUpdate = false;
private int _m_randomNum = 0;
public int m_randomNum
{
get{ return _m_randomNum; }
set{ _m_randomNum = value; }
}

在ISystem类中有两个虚方法需要在EventSys中实现,这两个虚方法分别是

1
2
3
4
5
//初始化方法
public virtual void Init() {}

//更新
public virtual void SysUpdate() {}

事件系统中的事件是一个队列Queue,对列中存放的是一个EventParamData类型的数据,在每次执行SysUpdate的时候对队列中的所有元素执行出队操作,及时向其它系统发送事件,有新事件的时候执行入队操作,在注销事件时删除队列内相关的事件。为确保同一时间内能完整的运行完同一套事件,故而对事件加锁Lock,保证线程安全。

具体的代码如下

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
public class EventSys : ISystem
{
private static String ms_lock = "lock";
private List<EventSysCallBack> m_allHander = new List<EventSysCallBack> ();
private List<EventSysCallBack>[] m_eventArray = new List<EventSysCallBack>[(int)EEventType.Count];
private int m_eventNum = 0;

private Queue m_eventQueue = new Queue();
public static EventSys Instance = null;

private bool m_hasInit = false;
public override void Init()
{
if(m_hasInit)
{
return;
}

m_hasInit = true;
m_eventQueue.Clear();
Instance = this;
m_eventNum = (int)EEventType.Count;
}

public override void SysUpdate()
{
lock (ms_lock)
{
while (m_eventQueue.Count > 0)
{
EventParamData data = m_eventQueue.Dequeue() as EventParamData;
EEventType eventId = data.GetEventId();
SendEvent(eventId, data.GetParam1(), data.GetParam2());
}
}
}

public void AddAllHander(EventSysCallBack callBack) {
lock (ms_lock) {
m_allHander.Add (callBack);
}
}

public void AddUintHander(uint eventid, object instance, EventSysCallBack_ callBack)
{
AddHander((EEventType)eventid, (EEventType eventType, object param1, object param2) =>
{
callBack((uint)eventType, param1, param2);
});
}

public void AddHander(EEventType eventId, EventSysCallBack callBack)
{
lock (ms_lock)
{

if ((int)eventId < m_eventNum)
{
var eventList = m_eventArray[(int)eventId];
if (eventList != null)
{
int count = eventList.Count;
for (int i = 0; i < count; i++)
{
if(callBack == eventList[i])
{
return;
}
}
eventList.Add(callBack);
return;
}
}

List<EventSysCallBack> t = new List<EventSysCallBack>();
t.Add(callBack);
m_eventArray[(int)eventId] = t;
}
}

public void RemoveHander(object target)
{
lock (ms_lock)
{
for (int i = m_eventNum - 1; i >= 0; i--)
{
var eventList = m_eventArray[i];
if (eventList != null)
{
var eventCount = eventList.Count;
for(int j = eventCount - 1; j >= 0; j--)
{
if(target == eventList[j].Target)
{
eventList.RemoveAt(j);
}
}
}
}
}
}

public void AddEvent(EEventType eventId, object param1=null, object param2=null)
{
lock (ms_lock)
{
m_eventQueue.Enqueue( new EventParamData(eventId, param1, param2 ) );
}
}

public void AddUintEvent(uint eventId, object param1 = null, object param2 = null)
{
AddEvent((EEventType)eventId, param1, param2);
}

//此函数只能在主线程调用
public void AddEventNow(EEventType eventId, object param1=null, object param2=null)
{
SendEvent (eventId, param1, param2);
}

public void AddUintEventNow(uint eventId, object param1 = null, object param2 = null)
{
AddEventNow((EEventType)eventId, param1, param2);
}

private void SendEvent(EEventType eventId, object param1, object param2)
{
for(int i = 0; i < m_allHander.Count; i++)
{
try
{
m_allHander[i](eventId, param1, param2);
}
catch (Exception e)
{
CNetSys.Instance.SendImportantLogWithBugly(true, "[Exception]" + e.ToString());
}
}

int targetIndex = (int)eventId;
if(targetIndex < m_eventNum)
{
var eventList = m_eventArray[targetIndex];
if(eventList != null)
{
int count = eventList.Count;
for(int i = 0; i < count; i++)
{
try
{
if(eventList != null && i < eventList.Count)
{
eventList[i](eventId, param1, param2);
}
}
catch (System.Exception e)
{
CNetSys.Instance.SendImportantLogWithBugly(true, "[Exception]" + e.ToString());
}
}
}
}
}
}

大致流程

业务逻辑需要发事件时,调用AddEvent(…)接口,当下一帧到来,sysupdata(…)方法执行,事件被SendEvent(…)发送出去,设置了AddHander(…)的业务逻辑回调就会触发,进行相关的逻辑操作。

分享到