Nomad游戏引擎:第4.1部分-通用组件管理器

最后一些实现细节!

这篇文章是系列文章的一部分,我在其中记录我从头开始构建ECS游戏引擎的经验。 请查看 该项目 主页,以 获取更多帖子,信息和源代码。

这篇博客文章将从概念和编程的角度开始着手解决“组件管理器”。 这将是一个由三部分组成的博客文章,因为涉及的内容很多,因为当前这是游戏引擎中技术上较为复杂的部分之一。 在第一篇文章中,我们将介绍基本组件管理器的实现,将一系列通用组件紧密封装在其内存中。 我将尽力在每个设计决策后解释我的想法,但是,如果其中任何一个没有意义,请随时向我发送消息或对这篇文章发表评论,我很乐于澄清。

什么是组件管理器?

虽然我以前的文章涵盖了对组件管理器的需求,但以下是组件管理器的快速入门:
组件可以附加到实体以赋予其某些属性。 与其将实体的所有组件都保存在一起,不如将实体的所有组件都保存在同一位置上,可以提高缓存效率。 这样,当我们在类型的所有组件上运行更新方法时,它们都将保存在连续的内存中。

“通用”部分是什么意思,为什么我们需要它?

这个概念很简单。 每个组件管理器中都包含一个数据数组,其中包含一堆结构(组件)。 既然所有组件管理器显然都将执行相同的操作,因此自然而然地我们应该尝试编写一组可以正确处理所有组件的代码。 通过创建此通用组件管理器,我们还可以做一些非常整洁的事情,例如创建一个通用的“将组件添加到实体”方法,该方法将能够确定需要调用哪个组件管理器。 所有这些优点都是通过C ++模板的功能来使用的。

实作

组件管理器需要做什么?

1.添加组件
2.从特定实体访问组件
3.从实体中删除组件
4.遍历所有项目

让我们再思考一下提供此功能需要什么样的数据结构。 我们需要选择存储的两个要求是#2和#4。 在给定实体ID的情况下访问组件会发出尖叫声哈希图,在该图中,我们使实体ID指向O(1)访问的实际数据。 #4为我们提供了以相对不透明的方式连续存储数据的额外要求。

我们的数据结构将是顶部的哈希图(上图中的entityMap),并具有可通过(componentData)进行迭代的基础数据数组。 现在我们已经弄清楚了,让我们看一下新的“通用组件管理器”的轮廓可能是什么样的:

为此,我们将数组包装在一个结构中,以使其更易于阅读:

那么我们该如何解决呢? 好了,只要我们删除一个项目,我们就简单地获取列表中的最后一个项目并将其移动以填充空间。 这样,我们可以确保始终拥有紧密打包的数据,但要付出一些代价,即要复制组件并更新其在哈希图中的索引。

如下所示:

这是实现它的代码:

注意,实际上我们不需要在componentData [componentData.size – 1]处“清除”现在复制的组件,因为它将被下一个要添加的组件覆盖。

还要注意,我们已经有了此方法getEntityByComponentInstance 。 这是此设计的必要条件—实际上,我们需要一个双向映射,该映射允许我们按实体查询componentinstance和按componentinstance查询实体。 在此处可以找到示例实现。

遍历所有项目

我们的最终要求实际上是我最近添加的要求。 在大多数情况下,组件通过系统与其他组件进行交互,因此,我们不能仅遍历willy nilly类型的每个组件。 例如,仅当玩家站在带有PoisonCloud组件的实体中时,HealthComponent才可能减少–我们不能只运行每个HealthComponent并降低它,而又不确保拥有该HealthComponent的实体也站在一个具有PoisonCloud组件的实体。

因此,大多数用例实际上并不需要按顺序遍历所有项目。 大多数用例将仅使用组件访问方法并每天调用它。 但是,有些组件具有相对独立的功能。 例如,Motion组件需要在每个刻度上更新其位置和速度,如下所示:

  //每次打勾 
component.position + = component.velocity;
component.velocity + = component.acceleration;

在这种情况下,我们的功能非常完备-Motion组件只需访问自身即可正确更新。 这将发生在每个Motion组件的每个滴答声中,因此这将运行很多代码。 对于此类用途,我们提供了一个简单的lambda函数,该函数允许在类型的每个组件上按顺序运行方法。 看起来是这样的:

因此,要完成我们上面写的内容,我们只需要调用:

  componentManager.iterateAll([](positionComponent c){ 
c。位置+ = c。速度;
c。速度+ = c。加速度
}

因此,现在我们有了一个功能齐全的组件管理器。 还不错吧? 不幸的是,这仅仅是开始。 如果您认为这是过度优化的,请等到看到接下来的两篇文章…