组件化游戏引擎设计中的事件处理

10

我想这个问题或其变体很常见,如果我的话是重复的,答案可以从其他地方获取,请告知。

我一直在研究游戏引擎设计,接触到了组件化实体模型。听起来很有前途,但我仍在研究它的实现。

我正在考虑一个系统,其中引擎由多个“子系统”组成,每个子系统管理某些方面,例如渲染、声音、健康、人工智能等。每个子系统都与一个组件类型相关联,例如健康子系统的健康组件。例如NPC、门、一些视觉效果或玩家等“实体”只是由一个或多个组件组成的,当它们一起时才能使实体具备其功能。

我确定了四个主要的信息传递渠道:组件可以向其当前实体中的所有组件广播、组件可以向其子系统广播、子系统可以向其组件广播,子系统可以向其他子系统广播。

例如,如果用户想要移动他们的角色,他们会按下一个键。输入子系统会捕获此键按事件并将其广播,然后该事件将被玩家子系统捕获。玩家子系统然后将此事件发送给所有玩家组件(因此是这些组件组成的实体),并且这些玩家组件将与其自身实体的位置组件通信以开始移动。

所有这些仅为了一个按键事件似乎有点冗长,我当然愿意改进这种架构。但无论如何,我的主要问题仍然是:

至于事件本身,我考虑将事件行为视为访问者模式。我想要的重要性在于,如果事件遇到它不支持的组件(例如移动事件与AI或健康没有直接关系),则会忽略该组件。如果事件找不到它正在寻找的组件,也无所谓。

访问者模式基本可行。但是,为了每种组件类型(例如visitHealthComponent、visitPositionComponent等)都需要虚函数,即使它与它们无关。我可以将这些函数留空(因此如果遇到这些组件,就会被忽略),但每次添加组件时,我都必须添加另一个函数。

我的希望是能够添加组件而无需必要地添加其他内容,并添加事件而不会干扰其他内容。

因此,我的两个问题是:

  1. 有没有改进我的设计的方法,例如效率、灵活性等?
  2. 处理事件的最佳方法是什么?
4个回答

1

这里描述的架构 http://members.cox.net/jplummer/Writings/Thesis_with_Appendix.pdf,在实际项目中我遇到了至少三个问题:

  1. 系统无法在事件发生时得到通知 - 唯一的方法是询问 - 玩家死亡了?墙壁不可见了?等等 - 为了避免这种情况,可以使用简单的MVC代替观察者模式。
  2. 如果你的对象是一个组合体(即由多个对象组成),系统将遍历整个层次结构并询问组件状态。
  3. 而主要的缺点是这种架构混合在一起 - 例如,为什么玩家需要知道你按下了一个键?

我认为答案是分层架构和抽象表示...


1

使用事件总线,也称为事件聚合器。你需要的是一种不需要子系统之间耦合的事件机制,而事件总线正好可以做到这一点。

http://martinfowler.com/eaaDev/EventAggregator.html http://stackoverflow.com/questions/2343980/event-aggregator-implementation-sample-best-practices

等等


1

请原谅我的糟糕英语。

我正在编写一个基于实体组件系统的灵活可扩展的Java 3D游戏引擎。我已经完成了其中一些基本部分。

首先,我想说一些关于ECS架构的事情。我不同意组件可以与同一实体中的其他组件通信。组件应该只存储数据,而系统则处理它们。

在事件处理部分,我认为基本的输入处理不应该包含在ECS中。相反,我有一个名为Intent System的系统,并有一个名为Intent Component的组件,其中包含许多意图。意图表示实体希望朝向某个实体执行某些操作。当Intent System处理所有意图时,它会将相应的信息广播给其他系统或向实体添加其他组件。

我还编写了一个名为Intent Generator的接口。在本地游戏中,您可以实现键盘输入或鼠标输入生成器,在多人游戏中,您可以实现网络意图生成器。在AI系统中,您还可以生成意图。

您可能认为Intent System在游戏中处理了太多的事情。但实际上,它与其他系统共享了许多处理,并且我还编写了一个脚本系统。对于特定的特殊实体,它具有执行特殊操作的脚本组件。

起初,当我开发某些东西时,我总是想要制作一个包含所有东西的伟大架构。但对于游戏开发来说,有时效率非常低下。不同的游戏对象可能具有完全不同的功能。ECS作为面向数据的编程系统非常出色,但我们不能在其中包含完整的游戏。

顺便说一下,我们基于ECS的游戏引擎将在不久的将来开源,届时您可以阅读它。如果您对此感兴趣,我也邀请您加入我们。


1

我一直在考虑在我的一个项目中使用实体系统,并经历了类似的思考过程。我的最初想法是使用观察者模式来处理事件 - 我也最初考虑了某种访问者模式,但出于你提出的原因而放弃了。

我的想法是子系统将提供一个特定于子系统的发布/订阅接口,因此子系统依赖关系将以“半松散”耦合的方式解决。任何依赖于另一个子系统事件的子系统都将知道该子系统的订阅者接口,因此可以有效地利用它。

不幸的是,这些订阅者如何获取其发布者的句柄仍然是我心中的一个问题。目前,我更倾向于某种动态创建,其中每个子系统都被实例化,然后使用第二阶段来解决依赖关系并将所有子系统置于“就绪状态”。

无论如何,我非常感兴趣您的实践经验和在项目中遇到的任何问题 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接