1个不稳定版本
0.1.0 | 2020年1月26日 |
---|
#762 in 并发
157 星标 & 8 关注者
97KB
2.5K SLoC
一个不带组件锁的多线程实体组件系统
特性
- 快速,旨在具有低开销
- 线程化,尽可能自动地线程化系统
- 简单,在API使用方面没有复杂之处
工作原理
系统通过使用参数声明它们希望访问的组件。一个 Read<T>
参数声明系统只希望以只读方式访问组件,而 Write<T>
声明系统可以修改组件(添加/删除/编辑)。
内部调度器将使用固定数量的线程执行所有系统,只允许可以在任何给定时间安全运行的系统运行。该系统的规则很简单:只要没有写入者,组件可以同时拥有任意数量的读者。只有一个系统可以写入组件,并且在系统持有对组件的写入访问权时,不允许读者。
使用方法
首先,所有用作组件的结构体都必须实现 Component
特性。这可以通过 component!
宏来完成。 (有关不同类型,请参阅 component!
的文档)。然后必须将组件注册到 Container
中。
struct Position {
x: i32,
y: i32,
}
component!(Position => Vec);
let mut c = Container::new();
c.register_component::<Position>();
现在可以使用容器创建实体,添加组件并访问它们。
let entity = c.new_entity();
c.add_component(entity, Position { x: 5, y: 10});
// Mutable access to the component
{
let pos = c.get_component_mut::<Position>(entity).unwrap();
pos.x += 4;
}
// Immutable access to the component
assert_eq!(c.get_component::<Position>(entity), Some(&Position { x: 9, y: 10}));
实体通常通过系统进行处理。系统仅仅是函数。您可以通过Systems
类型注册要运行的一组函数。当运行Systems
时,系统将根据其参数以及当前正在运行的其他系统自动决定何时运行。系统的运行顺序没有定义。
函数至少需要一个参数,即一个EntityManager
引用。这提供了一个接口来创建和遍历系统中的所有实体。其他参数必须是Read<T>
或Write<T>
引用,其中T是组件类型。Read
提供对组件的不可变访问,而Write
提供可变访问(包括添加/删除组件)。Read
和Write
类型都提供了一个mask
方法,可以与EntityManager
的iter_mask
方法一起使用来遍历实体的子集。掩码可以像以下这样链接,以遍历多个类型的交集:pos.mask().and(vel)
。
let mut sys = Systems::new();
closure_system!(fn example(em: EntityManager, mut pos: Write<Position>) {
let mask = pos.mask();
for e in em.iter_mask(&mask) {
let pos = pos.get_component_mut(e).unwrap();
pos.y -= 3;
}
});
sys.add(example);
sys.run(&mut c);
问题/问题
- 在系统中移除实体实际上不会在所有系统执行完毕之前将其移除。
依赖关系
~1.5MB
~25K SLoC