我正在为我要构建的游戏引擎构建实体组件系统,但我不确定如何使用严格的类型化语言(在本例中为Rust)进行开发。
我希望组件类型为任意结构,可以包含有关实体的任何类型的状态,但不了解其行为。这样,例如,一个实体可以包含Position
,Hitbox
和Velocity
组件,但是可以单独更改或交换物理子系统,而不必修改与这些组件有关的任何内容。
我还希望能够从模块外部添加新的组件类型。这将允许新的游戏mod可以将其自定义组件添加到现有实体,而无需修改游戏的核心代码。
我对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)
在以上示例中,实体可以包含任何类型的组件,并且处理它们的系统可以检索它们所依赖的特定组件,直接访问其具体属性,然后修改这些组件。外部代码还可以向其中一个实体添加一个完全未知的组件,并且无需更改物理组件即可适应它。这是我希望在生锈的ECS中具有的相同行为。
作为旁注,由于游戏需要比我的示例javascript提供的性能更高的解决方案,因此,我希望最大程度地减少指针间接访问,哈希表查找,内存分配并尽可能优化数据位置,但是优化自然是功能的第二位。我的示例代码忽略了这些优化。
我已经考虑过创建ComponentStorage<T>
的哈希表,其中ComponentStorage
是允许我抽象用于存储组件的基础数据结构的特征。 State
结构将包含HashMap<ComponentStorage<std::any::TypeId, T>>
。可以通过TypeId哈希查找特定的存储,然后使用ComponentStorage特征,我可以通过实体ID从该存储中检索一个Option<T>
,然后访问T
包含的任何属性。
但是,这不起作用,因为HashMap
中每个项目的类型T都会不同,并且我无法通过为每个类型参数的变体创建一个单一的特征来实现来擦除类型参数(如建议的那样在这个类似的问题中:Vector of Generic Structs in Rust),因为我需要在处理实体的系统中访问具体类型T。
我可以通过使用Any
存储组件来实现类似于示例JavaScript的功能,但是我的理解是,在任意自定义结构上使用Any
意味着没有相邻的存储,并且指针间接指向大量。我并不是要过早地进行优化,但是我犹豫要走这条路线进行原型设计,因为似乎Any
的这些局限性似乎无法克服,而不必以后再完全重写。
您可以在这里为我提供的任何帮助,我将永远感激。谢谢!
问题是我正在为要构建的游戏引擎构建实体组件系统,但我不确定如何使用严格的类型化语言(在本例中为Rust)进行操作。我想要...
只有一种方法可以在同一个容器中包含不同类型的对象:指针间接控制。