C++:设计基于组件的实体系统 - 高级问题

12

在我用C++编写的游戏引擎中,我已经摒弃了传统的层级式实体系统,改用基于组件的系统。它的大致工作方式如下:

实体仅是组件的容器。一些示例组件包括:点(Point)、精灵(Sprite)、物理(Physics)、发射器(Emitter)。

每个实体最多可以持有每种类型的一个组件。某些组件依赖于另一个组件,例如物理和精灵依赖于点,因为它们需要由其提供位置和角度。

因此,使用组件系统一切都运作良好,但现在我遇到了实现更专业化实体的困难,例如:

  • 相机(Camera),需要额外的功能来处理移动和缩放
  • 玩家(Player),需要支持用户输入并进行移动

现在,我可以轻松地使用继承来解决这个问题。只需从实体派生相机,并添加额外的缩放函数和成员即可。但这种方法感觉不太对。

我的问题:

  • 如何使用C++中的组件系统解决专业化实体的问题?

1
将相机和播放器制作成实体的好处是什么?为什么它们不应该作为自己的事物存在,因为它们是古怪的对象?我注意到你所描述的“播放器”实际上只是MVC的控制器端,不适用于其他播放器(如果有的话)。我的主要观点是:不要创建一个创新系统,只是让它成为一个束缚,阻止你在独特情况下做正确的事情。 - Mike DeSimone
@Mike:我希望我的实体系统是统一的。相机基本上只是一个具有位置的实体,我发现这样做是合乎逻辑和正确的方式。对于玩家也是一样,甚至更多,玩家具有物理实体的所有典型属性。 - Jarnstrom
你不能像为其他独特功能所做的那样,为玩家和相机创建新的组件类型吗? - Olli Etuaho
你假设所有事物都可以成为“实体”。这样做有什么好处?换句话说,“你为什么要这样做?”如果没有好处,那么你就是在浪费时间。 - Jay
1
@Jay:不,但相机和播放器对我来说绝对是实体,它们是存在于世界空间中的对象。 - Jarnstrom
4
我认为将游戏中代表玩家的实体(具有物理特性)与玩家本身(世界的控制器)分开是个好主意。同样地,你可以将摄像机作为实体(在世界上有一个位置)与对摄像机的解释(渲染缩放等内容)分开处理。 - molbdnilo
5个回答

14

您似乎对IS-A关系持怀疑态度。那为什么不将其转换为HAS-A关系呢?相机和玩家可以成为拥有实体(或实体引用)但存在于组件系统之外的对象,而不是实体本身。这样,您就可以轻松地保持组件系统的一致性和正交性。

这也很好地符合这两个示例(相机/玩家)作为“粘合”对象的含义。玩家将实体系统粘合到输入系统,并充当控制器。相机将实体系统粘合到渲染器,并充当一种观察者。


3

那么创建使行为生效的组件怎么样呢?例如,InputComponent可以处理玩家的输入。这样,您的设计保持不变,而玩家只是一个实体,允许从键盘而不是AI控制器获得输入。


1

基于组件的系统通常有一种通用方法,允许向实体发送“消息”,就像一个函数send(string message_type, void* data)。然后实体将其传递给所有组件,只有其中一些会对其做出反应。例如,您的组件Point可以对send("move", &direction)做出反应。或者您可以引入一个moveable组件以获得更多控制。同样的事情也适用于您的相机,添加一个view组件并使其处理“缩放”消息。

这种模块化设计已经允许定义不同类型的相机(例如固定相机没有moveable组件),为其他东西重用某些组件(另一种类型的实体可能使用“视图”),并且您还可以通过具有不同组件以不同方式处理每个消息来获得灵活性。

当然,有些优化技巧可能是必要的,特别是对于频繁使用的消息。


0
一种常见的解决方案是使用访问者模式。基本上,您将使用 Visitor 类“访问”实体。在实体内部,您将有:

void onVisitTime(Visitor* v)
{
   // for each myComponent...
   v->visit(myComponent);
   // end for each
}

然后,在访问者类中,您将拥有:

void visit(PointComponent* p);
void visit(CameraComponent* c);

请注意,这有点违反面向对象编程的原则(数据操作在对象外部处理,因为访问者将处理它)。而且访问者往往会变得过于复杂,所以这不是一个很好的解决方案。

我不明白访问者模式如何在这里发挥作用。它是一种动态类型分派机制。楼主正在询问如何设计他的系统。我不明白它与此有何关系。 - ltjax
它简化了实体的专业化:例如,他只需添加一个ZoomCameraComponent,而不必触及类层次结构。但我可能误解了问题。 - Raveline

0
给每个实体一些限制,以确定它可以持有哪种组件(也许还应该确定它应该持有哪些组件),并在从该实体派生时放松这些限制,这样怎么样?例如,通过添加一个虚函数来验证是否可以将某个特定组件添加到实体中。

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