有没有更好的方法来建立事件系统?


9

事件系统令人惊叹,它们使代码变得极其笨拙,并且确实允许通过对象之间的轻松通信和游戏循环来动态创建游戏。我目前的执行效率很难。目前,我对将对象列表分离为它们响应的事件进行了一些细微的优化,这确实产生了奇迹,但是我应该做的还很多。

目前,我有两种方法:

  1. 最简单:发送事件时,所有对象都添加到向量中。所有对象都通过其handle_event()方法发送事件

  2. 更复杂:我有一个以字符串为键的映射,以int为值的映射。添加事件类型后,会将其添加到此地图,只需将int简单地增加(必须有更好的方法)
    ,然后将对象向量的向量推回新的向量以处理该事件类型。
    调用事件时,它只需将eventTypes映射中的相应int映射到对象向量的vector内的类型,然后将该事件发送给处理该事件类型的每个对象。

对于许多对象,这第一种方法相当慢(很明显),但是对于很少的对象来说却相当快。而对于要处理不同类型事件的大型对象,第二种方法相当快,但对于处理相同类型事件的对象,每个对象的第二种方法却较慢。

有没有更快的方法?有没有一种更快的方法来从字符串类型查找int?(起初,我有一个枚举,但是它不允许使用自定义类型,这是必需的,因为需要具有一定的动态性。)


1
这就是哈希映射(或哈希表)的用途,该字符串被计算为哈希数,然后将其直接用于在数组中查找。zh.wikipedia.org/wiki/Hash_table
Patrick Hughes

一个好问题是:这确实是性能瓶颈,还是您只是过早担心?
贾里·孔帕

当使用许多对象时,它已经实现,并且存在瓶颈。我之前关于缺少瓶颈的评论不是在应用程序本身中,而是在上面两个实际处理相同数量对象的实现之间的速度差异。我希望创建事件系统的其他方法...但是,该线程中的某些想法会大大提高速度,尤其是在事件系统的加载中(使用100000个对象加载11-25整秒的时间)
ultifinitus

仅仅一场游戏,100K的物体听起来太可怕了。这是服务器还是最终用户客户端应用程序?
Patrick Hughes

这是我的引擎,所以我真的想保持灵活性。它将用于服务器应用程序和最终用户应用程序(较少时用于第一个优化)
ultifinitus 2011年

Answers:


5

听起来您好像在说最大的性能瓶颈是从其字符串名称中查找事件ID(整数)。您可以对游戏数据进行预处理,以在运行游戏之前或可能在加载关卡时将所有事件名称转换为整数。那么您在游戏过程中就无需进行任何转换。

如果经常创建和销毁对象,则对象向量中可能会有很多搅动。在这种情况下,您可以通过使用链接列表而不是向量来受益。它们的插入和删除速度更快。


瓶颈并不大(对于100,000个对象,它损失.0000076 ms /对象),但是我认为您的想法是个好主意!我想我实际上将对id进行一次查找,并将eventID存储为int而不是原始字符串数据。而且我实际上没有想到过链接列表,好主意。
ultifinitus 2011年

1
+1用于预处理ID。您还可以通过具有每个模块都可以通过类似获取的EventType类型来懒惰地执行此操作EventType takeDamageEvent = EventSystem::CacheEventType("takeDamageEvent");。使其成为类的静态成员,并且在每个需要它的类中只有一个副本。
michael.bartnett

2
请注意,存储id而不是字符串将使调试有些困难。能够看到一些指定对象来源的文本总是有用的。您可以通过存储ID和字符串来进行一半操作,这些ID和字符串都保留用于调试目的(也许甚至在发行版本中已删除)。
Nicol Bolas

2

好吧,让我们先把简单的东西弄清楚。您可以map在字符串(大概是事件的名称)和整数(已注册的事件侦听器的索引)之间使用它。

中的查找时间map基于两件事:映射中的项目数,以及在两个键之间进行比较所需的时间(忽略缓存问题)。如果查找时间有问题,一种解决方法是通过更改字符串类型来更改比较函数。

假设您正在使用std::stringoperator<进行比较。这是非常低效的;它按字节进行比较。您不在乎真正的字符串小于比较;您只需要某种比较即可给出严格弱的排序(因为map否则无法正常工作)。

因此,您应该使用32字节固定长度的字符串代替std::string。我将这些用作标识符。这些固定字符串的比较测试不进行字节比较。他们改为进行32位(或64位)比较。它以每4个字节为一个无符号整数,并将其与另一个字符串的对应4个字节进行比较。这样,比较最多只需要8个比较。它确保了严格的弱排序,尽管该排序与作为字符的数据无关。

存储长度超过31个字节的字符串(需要NULL字符)会截断字符串(但从中间开始,而不是从结尾开始。我发现熵在开头和结尾处往往是最大的)。短于该字符串的字符串用填充其余字符\0

现在,您可以map完全放弃并使用哈希表。如果您确实有超过100,000种不同类型的事件类型,那么这可能是个好主意。但是我不知道在哪一款游戏中这是很合理的事情。


因此,您应该使用32字节固定长度的字符串而不是std :: string。太棒了!我没想到要更改字符串类型。
ultifinitus 2011年

0

要回答一般性问题:

建立事件系统的更好方法

空无一人。您所要做的就是确定事件系统的特定需求(可以有几种不同的需求),然后使用正确的工具完成正确的工作。

我已经实现并尝试归纳了大量事件系统,并且得出了这个结论。因为如果使之成为编译时或运行时,或者使用对象层次结构或黑板等,那么差错实际上是不同的。

最好的方法是研究不同类型的事件系统,然后您将获得有关在哪种情况下使用哪个事件系统的线索。

现在,如果您要使用最灵活的系统,请使用具有事件动态属性的黑板系统(运行时),您将拥有非常灵活但可能很慢的功能。

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.