在我用C ++编写的游戏引擎中,我已经离开了传统的分层实体系统,并构建了一个基于组件的系统。它大致以这种方式工作:
实体只是组件的容器。一些示例组件是:Point,Sprite,Physics,Emitter。
每个实体最多可以容纳每种类型的一个组件。有些组件依赖于另一个组件,比如Physics和Sprite依赖于Point,因为它们需要一个位置和角度。
所以一切都适用于组件系统,但现在我无法实现更专业的实体,例如:
现在,我可以通过继承轻松解决这个问题。只需从实体派生相机并添加其他缩放功能和成员。但这只是感觉不对。
我的问题:
答案 0 :(得分:14)
你似乎怀疑这里的IS-A关系。那么为什么不把它变成一个HAS-A关系呢?相机和播放器可以是具有实体(或对实体的引用)但存在于组件系统之外的对象,而不是实体。这样,您可以轻松保持组件系统的一致性和正交性。
这也非常适合这两个例子(相机/播放器)作为“粘合”对象的含义。播放器将实体系统粘合到输入系统并充当控制器。相机将实体系统粘合到渲染器上,并充当一种观察者。
答案 1 :(得分:3)
如何创建支持该行为的组件呢?例如,InputComponent可以处理来自播放器的输入。然后你的设计保持不变,玩家只是一个允许键盘输入的实体,而不是AI控制器的输入。
答案 2 :(得分:1)
基于组件的系统通常有一种允许向实体发送“消息”的通用方法,如函数send(string message_type, void* data)
。然后,实体将其传递给所有组件,只有其中一些组件会对其做出反应。例如,您的组件Point
可能会对send("move", &direction)
做出反应。或者您可以引入moveable
组件以获得更多控制权。您的相机也一样,添加一个组件view
并使其处理“缩放”消息。
这种模块化设计已经允许定义不同类型的摄像机(比如没有moveable
组件的固定摄像机),为其他东西重用一些组件(另一种类型的实体可能使用“视图”)而你通过让不同的组件以不同的方式处理每个消息,也可以获得灵活性。
当然,可能需要一些优化技巧,特别是对于经常使用的消息。
答案 3 :(得分:0)
如何给每个实体一些限制它可能拥有的组件类型(也可能是它应该拥有的组件),并在从该实体派生时放松这些限制。例如,通过添加一个虚函数来验证是否可以将某个组件添加到实体中。
答案 4 :(得分:0)
常见的解决方案是使用访客模式。基本上,您将通过Visitor类“访问”您的实体。在你的实体内,你有:
void onVisitTime(Visitor* v)
{
// for each myComponent...
v->visit(myComponent);
// end for each
}
然后,你会在访客班中找到:
void visit(PointComponent* p);
void visit(CameraComponent* c);
请注意,它有点违反OOP(在对象外部处理数据操作,因为访问者将处理它)。访问者往往变得过于复杂,所以这是一个不那么好的解决方案。