问题
我正在为我正在尝试构建的游戏引擎构建一个实体组件系统,但我不太确定如何使用严格类型的语言(在本例中为 Rust)进行操作。
我希望组件类型是任意结构,可以包含有关实体的任何类型的状态,但不知道行为。这样,例如,实体可以包含Position
、Hitbox
和Velocity
组件,但物理子系统可以单独更改或交换,而无需修改有关这些组件的任何内容。
我还想让从模块外部添加新的组件类型成为可能。这将允许新游戏模组将其自己的自定义组件添加到现有实体,而无需修改游戏的核心代码。
我对 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 提供的更高性能的解决方案,我想尽量减少指针间接、哈希表查找、内存分配和优化数据局部性,但自然优化仅次于功能。我的示例代码忽略了这些优化。
我一直在尝试的
我考虑过创建一个 的 hashmap ComponentStorage<T>
,其中ComponentStorage
的 trait 允许我抽象用于存储组件的底层数据结构。一个State
结构将包含一个HashMap<ComponentStorage<std::any::TypeId, T>>
. 可以通过 TypeId 哈希查找特定存储,然后使用 ComponentStorage 特征,我可以Option<T>
通过实体 ID 从该存储中检索 a,然后访问T
包含的任何属性。
但是,这不起作用,因为 T 中的每个项目的类型都不同HashMap
,并且我无法通过创建单个特征来为类型参数的每个变体实现来擦除类型参数(如在类似问题中所建议的那样:Rust 中的通用结构向量),因为我需要访问处理实体的系统中的具体类型 T。
我可能会通过使用来存储组件来实现类似于我的示例 JavaScript 的东西Any
,但我的理解是,Any
在任意自定义结构上使用意味着没有相邻存储,并且指针间接丰富。我并不是要过早地进行优化,但我很犹豫是否沿着这条路线进行原型设计,因为如果Any
以后不完全重写它,似乎无法克服这些限制。
由于显然必须发生指针间接以便不将类型硬编码到该系统中,我希望组件的集合,而不是组件本身,是多态的,同时能够访问包含在给定集合的具体类型。
您可以在这里为我提供任何帮助,我将永远感激不尽。谢谢!