事件

游戏逻辑监听、抛出事件的机制。

Game Framework 中的很多模块在完成操作后都会抛出内置事件,监听这些事件将大大解除游戏逻辑之间的耦合。

除了 Game Framework 内置事件外,使用者也可以定义自己的游戏逻辑事件,游戏中所有事件均派生自 GameEventArgs 类,事件对象使用了引用池技术,以避免使用事件过程中频繁的内存分配。

常规用法

获取事件组件

订阅事件

这里以订阅“加载数据表成功事件”为例。

取消订阅事件

这里以取消订阅“加载数据表成功事件”为例。

检查是否订阅事件

这里以检查是否订阅“加载数据表成功事件”为例。

实现事件处理函数

这里以实现“加载数据表成功事件”处理函数为例。

抛出事件

当事件被抛出并分发后,订阅过此事件的所有事件处理函数都会被调用。

自定义游戏逻辑事件

如玩家的名字发生变化的时候,抛出一个自定义事件 PlayerNameChangedEventArgs,由相关的界面(可能有多个)去订阅这个事件,当接收到这个事件时,这些界面刷新显示数据,从而解除游戏逻辑层和界面显示层的耦合关系。

由于事件实例使用了引用池技术,请注意正确实现 Clear 方法进行事件实例的清理操作。

获取某个事件的事件处理函数数量

这里以获取“加载数据表成功事件”的事件处理函数数量为例。

获取待处理事件数量

设置默认事件处理函数

定义并设置默认事件处理函数,当任意事件被抛出且不存在任何事件处理函数时,默认事件处理函数将被调用。

参考手册

最佳实践

提取 EventHandler 降低内存分配

由于将方法转换为 EventHandler 时会有内存分配,所以建议将 EventHandler 预定义为临时变量甚至所在类的成员变量,来降低内存开销。

常见问题

事件订阅与取消订阅不匹配

事件的订阅与取消订阅在使用生命周期内,应当成对出现。比如实体显示时订阅、隐藏时取消订阅,界面打开时订阅、关闭时取消订阅。

Game Framework 会严格检查事件订阅的匹配情况,不允许出现重复订阅,也不允许出现重复取消订阅或取消订阅尚未订阅的事件处理函数,如果出现这些情况,将会抛出异常。

有时,使用者在对象生命期结束前,的确写有取消订阅的代码,但非预期的逻辑导致逻辑块提前跳出,取消订阅的代码最终未被调用,进而下一次订阅时出现重复订阅。

错误地缓存了事件实例

事件处理函数处理事件时,不应该缓存 GameEventArgs 实例,GameEventArgs 实例的有效生命周期,仅限于事件处理函数内。

Game Framework 为了降低内存分配,事件实例使用了引用池技术。一个事件被其所有事件处理函数处理完成后,事件实例会被立刻清理并回收(调用 GameEventArgs 中的 Clear 方法)。后续逻辑访问缓存的事件实例时,将无法访问到任何有效数据,甚至访问到此实例被复用后的数据。

因此,缓存事件实例是完全错误的,这将导致难以追踪的 BUG。

自定义事件类的 Clear 方法实现不完整

自定义事件类使用了引用池技术,事件实例是会被复用的。当回收某个事件实例时,不完整的 Clear 实现无法清理干净事件实例中的数据,进而将脏数据带到下一次复用事件实例的逻辑中去,这可能导致难以追踪的 BUG。

手误导致的事件处理函数不生效

订阅成功事件时,同时订阅相匹配的失败事件是一个好习惯,但有时复制粘贴代码会惹祸,如以下示例。

事件模块不工作

事件模块需要被轮询才能正常调用事件处理函数,请确认正确初始化了 Game Framework。