【问题标题】:Dynamically-typed entity component system in RustRust 中的动态类型实体组件系统
【发布时间】:2020-05-24 18:00:13
【问题描述】:

问题

我正在为我正在尝试构建的游戏引擎构建实体组件系统,但我不太确定如何使用严格类型的语言(在本例中为 Rust)。

我希望组件类型是任意结构,可以包含有关实体的任何类型的状态,但不知道行为。这样,例如,实体可以包含 PositionHitboxVelocity 组件,但物理子系统可以单独更改或换出,而无需修改有关这些组件的任何内容。

我还想让从模块外部添加新的组件类型成为可能。这将允许新游戏模组将自己的自定义组件添加到现有实体,而无需修改游戏的核心代码。

我是 Rust 的新手,并且在 C++ 方面做了有限的工作,所以我可能完全采用了错误的方法,如果是这样,我将不胜感激有关解决此问题的更好方法的建议。

在没有严格类型系统(而且我更熟悉)的语言中,比如 JavaScript,我可以拥有一个包含任意类型组件集合的实体数组,然后进行运行时类型检查以获取数据:

class Position {
  constructor(x = 0, y = 0, z = 0) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
}

class Velocity {
  constructor(x = 0, y = 0, z = 0) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
}

const world = [
  [
    Position(0, 0, 0),
    Velocity(0.25, 0.1, 1.2)
  ]
]

const physicsSystem = (world = []) => world.map((entity = []) => {
  const velocity = entity.find((component) => component instanceof Velocity)
  return velocity != null ? entity.map((component) => component instanceof Position
    ? Position(component.x + velocity.x, component.y + velocity.y, component.z + velocity.z)
    : component
  ) : component
})

window.setInterval(() => world = physicsSystem(world), 100)

在上面的示例中,实体可以包含任何类型的组件,处理它们的系统可以检索它们所依赖的特定组件,直接访问它们的具体属性,然后修改组件。外部代码还可以向其中一个实体添加一个完全未知的组件,并且不需要更改物理组件来适应它。这与我希望在我的 rust ECS 中具有的行为相同。

作为旁注,由于游戏需要比我的示例 javascript 提供的更高性能的解决方案,我想尽可能减少指针间接、哈希表查找、内存分配和优化数据局部性,但自然优化仅次于功能。我的示例代码忽略了这些优化。

我一直在尝试什么

我考虑过创建ComponentStorage<T> 的哈希图,其中ComponentStorage 是一个特征,它允许我抽象用于存储组件的底层数据结构。 State 结构将包含 HashMap<ComponentStorage<std::any::TypeId, T>>。可以通过 TypeId 哈希查找特定的存储,然后使用 ComponentStorage trait,我可以通过实体 ID 从该存储中检索 Option<T>,然后访问 T 包含的任何属性。

但是,这不起作用,因为类型 T 对于HashMap 中的每个项目都会不同,并且我无法通过创建单个特征来为类型参数的每个变体实现来删除类型参数(如在这个类似的问题中建议:Vector of Generic Structs in Rust) 因为我需要访问处理实体的系统中的具体类型 T。

我可以通过使用Any 来存储组件来实现类似于我的示例JavaScript 的东西,但我的理解是,在任意自定义结构上使用Any 意味着没有相邻存储,并且指针间接大量。我并不是要过早地进行优化,但我很犹豫是否要沿着这条路线进行原型设计,因为看起来Any 的这些限制似乎无法在以后完全重写的情况下克服。

由于指针间接必须显然是为了不将类型硬编码到这个系统中,所以我希望组件的集合,而不是组件本身,是多态的,而在同时能够通过具体类型访问给定集合中包含的项目。

您可以在这里为我提供任何帮助,我将永远感激不尽。谢谢!

【问题讨论】:

  • 你听说过Specs吗?
  • @FrancisGagné 我听说过 Amethyst,但我从未研究过他们是如何解决这个问题的。我可能最终只是使用他们的库而不是重新发明轮子,但为了学习,我仍然很好奇他们如何设法保持不同组件存储的动态集合。我将不得不对此进行一些研究。感谢您的链接!

标签: rust game-development type-systems entity-component-system


【解决方案1】:

只有一种方法可以在同一个容器中拥有不同类型的对象:大量指针间接寻址。

实际上,JavaScript 引擎在幕后为您做同样的事情。

【讨论】:

  • 我会澄清一下我的问题。是的,我确实知道我的代码中需要指针间接somewhere,但是由于游戏每次滴答都会迭代数千到数百万个组件,因此最好保留所有组件一个类型一起放在一个集合中,这样它们就可以占用相邻的内存,然后我只需要跟随一个指向集合的指针,然后从那里索引到每个组件。如果集合具有相同的类型但拥有不同的组件类型,这意味着每个组件访问都将遵循一个指针。
猜你喜欢
  • 2018-08-13
  • 2019-05-03
  • 2017-01-04
  • 2018-01-05
  • 2016-09-01
  • 2016-01-23
  • 2014-07-06
  • 2019-08-06
  • 2016-11-05
相关资源
最近更新 更多