组件之间如何进行有效的通信

How could components communicate effectively with each other?

本文关键字:通信 有效 何进行 之间 组件      更新时间:2023-10-16

我正在设计一个基于组件的系统,一切都很好,但缺少一个关键功能,即能够从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类移到物理组件类中,我如何让这些组件相互通信,而不必使用上面给出的代码行

我的解决方案是创建一个注册每个组件的组件管理器,任何需要另一个组件的组件都必须与该组件管理器协商,而不必直接这样做。这是一个可行的解决方案吗?那么我该如何处理这样一个组件管理器的逻辑呢?

好吧,我想你应该从设计一个消息系统开始

似乎您希望对代码进行大量解耦,并尽可能多地创建组件。我想,这很好,但在没有耦合的情况下拥有依赖关系的答案可能是在消息传递的行中。

假设你需要一个成就系统。这是一个完美的例子,说明了一个系统需要把手伸进尽可能多的角落和缝隙,以便在设计成果时实现最大的灵活性。但是,你怎么能同时把手伸进物理、人工智能和输入系统,而不写意大利面条代码呢答案是将侦听器放在事件队列中,然后根据消息的内容按特定条件运行它们。

因此,对于每个组件,您可能希望继承一个通用的消息发送/接收组件,可能带有泛型,以便发送数据消息。例如,假设你在FPS游戏中发射激光。激光很可能会发出声音,而激光很可能需要动画。你可能想向音响系统发送一条消息来播放某种声音,然后向物理系统或类似系统发送一个消息来模拟激光的效果。

如果您感兴趣,我有一个非常非常粗糙的库,用于基于队列、侦听器和通用消息对事件系统进行建模:https://github.com/VermillionAzure/Flexiglass您可能会从其他新手代码中获得一些见解。

我真的建议看看Boost.Signals2或Boost.Asio。信号/网络知识通常有助于设计通信系统,即使系统只是在游戏组件之间。

我最近在SFML中研究一个实体组件系统,遇到了同样的问题。

我最终没有创建某种允许组件相互通信的消息传递系统,而是将"系统"对象添加到组合中。这是实现组件系统时经常使用的另一种流行方法,也是迄今为止我使用过的最灵活的方法。

  • 实体不过是组件的集合
  • 组件是POD结构,没有方法(例如,PositionComponent只具有X和Y值)
  • 系统更新具有所需组件的任何实体

例如,我的"MovementSystem"遍历我的每个实体,并检查它们是否有VelocityComponent和InputComponent。如果是,则会根据当前按下的键更改当前实体的速度。

这消除了组件之间通信的问题,因为现在您只需要访问/修改存储在当前实体组件中的数据。

有几种不同的方法可以确定当前实体是否具有所需的组件——我使用的是位掩码。如果你决定也这样做,我强烈建议你看看这篇文章,对方法进行更彻底的解释:https://gamedev.stackexchange.com/questions/31473/role-of-systems-in-entity-systems-architecture