2 个版本
0.0.2 | 2021年5月21日 |
---|---|
0.0.1 | 2020年1月5日 |
#839 在 游戏开发
68KB
1.5K SLoC
狂喜 - 静态类型 ECS
lib.rs
:
用于实现实体-组件-系统(ECS)模式的库。
API 基本基于 specs
,但更侧重于在编译时静态验证库的使用(而不是像 specs 那样动态验证)。这牺牲了一些灵活性,但几乎所有的逻辑错误都能在编译时被发现。
它(目前)也没有像 specs
那样优化,因为它是为 rogue-like 设计的。
使用方法
实现 ECS 需要以下步骤
- 使用
define_world!
宏定义你需要存储的组件和资源。这将生成一个名为World
的结构体,以及库与它交互所需的特质实现 - 实现一个或多个
System
- 使用
run_system
traits/trait.WorldInterface.html#method.run_system 方法在 World 上运行你的System
特性
这个库以某种高级方式使用了 Rust 的类型系统。在 traits
模块中,你可以找到 Nest
和 Flatten
特质,它们允许将平面元组(如 (A, B, C)
)转换为嵌套表示 (A, (B, (C, ())))
并再次转换回来。这些特质为长度最多为 32 的元组实现了,这对于大多数用例应该足够了。
在API边界将扁平元组转换为嵌套元组,使我们能够递归地实现某些特性,而不是需要为每个特性编写宏以在扁平元组类型上实现它们。因此,您会在代码库的各个地方看到具有 Nest
/Flatten
特性边界的类型参数。由于无法告诉编译器 Nest
和 Flatten
是相反的操作,偶尔您会看到指定嵌套表示也是可展平的边界。
此外,我们还有一些 类型级元编程 特性,提供了一定程度的编译时不变性检查。
通常,客户端代码不需要太担心这些,但它确实有使编译器错误信息不那么有帮助的不幸副作用。
示例
#[derive(Debug, PartialEq)]
pub struct Data {
x: u32,
}
// `Default` impl that isn't the additive identity.
impl Default for Data {
fn default() -> Data {
Data { x: 128 }
}
}
#[derive(Debug, Default, PartialEq)]
pub struct MoreData {
y: u32,
}
define_world!(
#[derive(Default)]
pub world {
components {
test1: BasicVecStorage<Data>,
test2: BasicVecStorage<MoreData>,
}
resources {}
}
);
let mut w = World::default();
w.new_entity().with(Data { x: 1 }).build();
w.new_entity().with(Data { x: 1 }).build();
let md = w
.new_entity()
.with(Data { x: 2 })
.with(MoreData { y: 42 })
.build();
w.new_entity().with(Data { x: 3 }).build();
w.new_entity().with(Data { x: 5 }).build();
w.new_entity().with(Data { x: 8 }).build();
/// `TestSystem` adds up the values in every `Data` component (storing the result in `total`),
/// and multiplies every `MoreData` by the `Data` in the same component.
#[derive(Default)]
struct TestSystem {
total: u32,
}
impl<'a> System<'a> for TestSystem {
type Dependencies = (
ReadComponent<'a, Data>,
WriteComponent<'a, MoreData>,
);
fn run(&'a mut self, (data, mut more_data): Self::Dependencies) {
self.total = 0;
(&data,).for_each(|_, (d,)| {
self.total += d.x;
});
(&data, &mut more_data).for_each(|_, (d, md)| {
md.y *= d.x;
});
}
}
let mut system = TestSystem::default();
w.run_system(&mut system);
assert_eq!(system.total, 20);
assert_eq!(
<World as GetComponent<'_, MoreData>>::get(&w).get(md),
Some(&MoreData { y: 84 })
);
通过 ReadComponent
访问的组件不能可变迭代
#[derive(Debug, PartialEq)]
pub struct Data {
x: u32,
}
define_world!(
pub world {
components {
test1: BasicVecStorage<Data>,
}
resources {}
}
);
#[derive(Default)]
struct TestSystem {}
impl<'a> System<'a> for TestSystem {
type Dependencies = (
ReadComponent<'a, Data>,
);
fn run(&'a mut self, (data,): Self::Dependencies) {
(&mut data,).for_each(|(d,)| {
// do something
});
}
}