我正在设计一个基于组件的系统,一切运行良好,但缺少一个关键特性,那就是能够从Object类中获取一种类型的组件,其中可以添加此类/删除组件。在Object类中,存在一个组件向量:
vector<Component*> pComponents;
在Component类中,Component必须有一个名称。因此,像Drawable这样的组件将被调用:
pPlayer->addComponent(new Drawable("Drawable"));
这就是玩家可以绘制的所有内容。现在这就是问题所在:当谈到添加依赖于其他组件的组件时,是否存在组件如何相互通信的解决方案?
目前,在我的Game类中(不是Object类型,虽然我可能从Object派生它虽然不确定这是否是一个好的设计决定)但我在更新函数中有这个代码:
void Game::update()
{
pPlayer->update(0);
pSpriteLoader->getSprite()->move(pPlayer->getVelocity());
}
我之所以使用SFML2,是因为它易于用于2D图形。播放器更新功能调用组件的相应更新功能。精灵加载器也是一个组件,它负责通过可从文件或内存中读取的纹理/图像来处理精灵的加载。如果我要省略这行代码,那么精灵将无法在屏幕上移动。正如你所看到的,pPlayer有一个getVelocity()函数是奇怪的,因为我还没有将所有的物理内容移动到它自己的组件中。令人害怕的是,一旦我将物理内容从Player类移到物理组件类中,如何让这些组件相互通信而不必诉诸上面描述的代码行? / strong>
我的解决方案是创建一个组件管理器来注册每个组件,并且需要另一个组件的任何组件都必须咨询该组件管理器,而不必直接这样做。这是否是一个可行的解决方案,那么我如何继续使用这样一个组件管理器的逻辑呢?
答案 0 :(得分:2)
好吧,我想你会从设计一个消息传递系统开始。
您希望尽可能多地解耦代码并创建组件。我认为这很好,但是没有耦合的依赖关系的答案可能是消息传递的一部分。
假设你需要一个成就系统。这是一个系统的完美示例,需要尽可能多地将手伸入尽可能多的角落和缝隙中,以便在设计成就时获得最大的灵活性。但是你怎么能同时把你的手伸进物理,AI和输入系统,而不是写意大利面条代码? 答案是将侦听器置于事件队列,然后根据消息内容以某些条件运行它们。
因此,对于每个组件,您可能希望继承可能带有泛型的公共消息发送/接收组件,以便发送数据消息。例如,假设您在FPS游戏中拍摄激光。激光很可能发出声音,激光很可能需要动画。您可能希望向声音系统发送消息以播放特定声音,然后向物理系统发送消息或类似物以模拟激光的效果。
如果你有兴趣,我有一个非常非常粗糙的库,可以在这里基于队列,听众和通用消息对事件系统进行建模:https://github.com/VermillionAzure/Flexiglass你可能会从其他新手代码中获得一些见解。
我真的建议你看一下Boost.Signals2或Boost.Asio。在设计通信系统时,信号/网络知识通常很有帮助,即使系统只是在游戏组件之间。
答案 1 :(得分:0)
我最近一直在使用SFML中的实体组件系统,并遇到了同样的问题。
我没有创建某种允许组件相互通信的消息传递系统,而是最终将“系统”对象添加到混合中。这是另一种在实现组件系统时经常使用的流行方法,它是迄今为止我使用过的最灵活的方法。
例如,我的'MovementSystem'遍历我的每个实体并检查它们是否具有VelocityComponent和InputComponent。如果是,则根据当前按下的键改变当前实体的速度。
这消除了组件之间相互通信的问题,因为现在您需要做的就是访问/修改当前实体组件中存储的数据。
有几种不同的方法可以确定当前实体是否具有所需的组件 - 我正在使用位掩码。如果您决定这样做,我强烈建议您查看这篇文章,以便对该方法进行更全面的解释:https://gamedev.stackexchange.com/questions/31473/role-of-systems-in-entity-systems-architecture