设计事件管理器系统时应考虑什么?


9

我一直在研究Java游戏引擎的基础知识,并且已经准备好添加到Event Manager系统中。

从理论上讲,我知道事件管理器应该做什么:允许对象为某些事件“注册”,并且每当事件管理器收到事件通知时,就将事件广播给“注册”的侦听器。我感到很困惑的是如何开始实施它。

我无法从网上找到任何有关从头开始实施事件系统的信息,因此我正在寻找有关这种情况下最佳实践的信息-我应该不应该做什么做应该做的事情。

例如,我的每个游戏对象是否真的都必须有一个EventManager字段?由于我所有的游戏对象都继承自一个抽象的父类,因此我认为我应该能够使用静态引用,以便在所有游戏对象之间共享一个事件管理器实例。我已经使用Applet进行了类似的操作,以用于渲染每个对象。

我想我必须为每个可能的订阅事件维护某种类型的集合-根据需要在列表中添加和删除游戏对象。我认为应该使需要广播的事件队列成为可能,在这种情况下,我可以简单地在主游戏循环中添加“ EventManager.Update()”,并让该Update()方法广播最后发生的事件每帧。最后,每个对象都有一个HandleEvent(Event e)方法,然后可以对其进行适当地解析和响应。


这听起来像是实现此类系统的正确方向,还是我偏离了正常轨道和/或缺少了一些显而易见的东西?


2
gamedev.stackexchange.com/questions/7718/…这是先前提供的Q / AI,可以帮助您入门。
詹姆斯

@James Ah-看起来是一个不错的链接。谢谢!我想我早些错过了。
Raven Dreamer

4
一些其他提示:决定是否某些事件实际上应该立即生效而不是在帧结束时生效,特别是如果事件处理程序触发了其他事件时;想一想当事件循环发生时会发生什么;如果您要排队,则可能需要优先级系统或绕过队列的方法。
sam hocevar 2011年

Answers:


6

这可以很简单。

for each object in the subscriber list:
    object.notify(this event) // (or 'HandleEvent' if you prefer)

不要尝试弄清楚事件管理器“应该”做什么—弄清楚您需要它做什么。其余的应该从那里开始,或者至少应该提出一些更具体的问题。


3
+1表示“不要尝试确定X应该做什么-确定您需要它做什么。” 实际上,在您感觉需要某种尚未耦合的集中式方法来调用函数之前,根本不要添加事件管理器。

@JoeWreschnig哦,上帝是=)“确定您需要做的事情”这是任何开发人员都应该铭记的三大建议之一。
Patrick Hughes

起初,我想不同意关于不添加事件管理器的第二句话,因为大多数游戏可以受益于一种尚未耦合的集中式调用功能,但是后来我想到了我制作了多少个小型游戏却没有有任何类型的事件管理器。所以,是的
2011年

3

事件系统需要的三个基本方法是addListener()方法,removeListener()方法和dispatchEvent()方法。也就是说,方法对象用于注册事件,方法对象用于取消注册,以及用于向所有侦听器实际广播事件的方法。其他一切都是肉汁。

如您所述,您肯定需要某种数据结构来跟踪已注册的侦听器。最简单的方法是将向量(或取决于语言的简单Array)与事件相关联的关联数组(字典或JavaScript中的Object)。该向量是所有已注册侦听器的列表。将侦听器添加到列表中,或在add / removeListener()方法中将其删除。

要在dispatchEvent()中广播事件,可以像遍历向量一样简单。您可以通过对事件的优先级或其他进行排序来增加调度的复杂性,但是在您需要它之前不要担心。在许多情况下,您将不需要它。

当您考虑将哪些数据与事件一起传递时,dispatchEvent()会有一些细微差别。在最基本的级别上,您可能不会传递任何其他数据。收听者只需知道事件已发生即可。但是,大多数事件都附带有其他数据(例如,事件发生的位置),因此您需要使dispatchEvent()接受并传递一些参数。

例如,对于每个GameObject,是否真的需要一个“ EventManagner”字段?由于我所有的GameObjects都继承自一个抽象的父类,因此我认为我应该能够使用静态引用,以便在所有游戏对象之间共享EventManager的一个实例。

有很多方法可以为您的所有游戏对象提供对EventManager类的引用。静态引用当然是一种方法。另一个是与单例。但是,这两种方法都不太灵活,因此此处的大多数人都建议使用服务定位器或依赖项注入。我一直在进行依赖注入;这确实意味着每个对象都有一个单独的EventManager字段,这似乎是您想要避免的,但是我不确定为什么会出现问题。存储一堆指针并不会带来很多开销。


是的,我第一次真正掌握并实现依赖项注入是针对EventDispatcher对象。一旦意识到这一点,我就一直渴望用这种模式来重构很多东西。
2011年

0

免责声明

我不是Java程序员,但我写了一篇文章解释如何在C89(一种最简单的语言(IMHO))中创建事件系统,请查看

https://prdeving.wordpress.com/2017/04/03/event-driven-programming-with-c-89/

事件处理的基础非常简单。

  • 用回调注册事件
  • 触发事件
  • 遍历听众以查看是否存在匹配项
  • 消防员(如果找到)
  • 重复

知道这一点(但不知道如何在Java中实现)很容易理解,您需要一些处理程序(用于处理事件的函数)并将它们添加到与事件名称配对的数组(带有C语言的指针)中。触发事件后,将触发的事件名称及其参数存储在堆栈中,这称为事件池。

然后,您有了一个事件摘要(一个简单的循环),该事件摘要将弹出该堆栈中的第一个事件,并尝试为其找到处理程序,如果有处理程序,则使用参数运行它。

当然,可以随时触发此事件摘要,例如,调用Update()函数后每帧一次。


-2

对于EventManager字段,请使用静态变量。EventManager的Singleton也是一个好主意。

您的方法听起来不错,但请不要忘记使其成为线程安全的。

最好在“ GameEvent”之前或之后执行IO事件,因此,每个“ GameEvent”都在一帧中处理相同的数据。


“线程节省”?嗯
U62 2011年

-1表示完全不必要和不合理的单例建议。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.