7个版本
0.2.5 | 2019年1月6日 |
---|---|
0.2.4 | 2018年10月30日 |
0.1.0 | 2018年10月19日 |
#125 in #linear
每月下载 29次
415KB
919 行
Pyro
线性实体组件系统
基准测试
lib.rs
:
什么是实体组件系统?
实体组件系统(ECS)与关系数据库(如SQL)非常相似。`World` 是数据存储,其中游戏对象(也称为 `Entity`)生存。`Entity` 包含数据或 `Component`。ECS 可以高效地查询这些组件。
给我所有具有位置和速度组件的实体,然后根据速度更新位置。
type PosVelQuery = (Write<Pos>, Read<Vel>);
// ^^^^^ ^^^^
// Mutable Immutable
world.matcher::<All<PosVelQuery>>().for_each(|(pos, vel)|{
pos += vel;
})
内部
概述
- 迭代始终是 线性的。
- 不同的组件组合存在于单独的存储中
- 移除实体不会创建空洞。
- 所有操作都设计为批量使用。
- 运行时强制执行借用规则。见
RuntimeBorrow
Entity
使用包装的代际索引。见Entity::version
// A Storage that contains `Pos`, `Vel`, `Health`.
(
[Pos1, Pos2, Pos3, .., PosN],
[Vel1, Vel2, Vel3, .., VelN],
[Health1, Health2, Health3, .., HealthN],
)
// A Storage that contains `Pos`, `Vel`.
(
[Pos1, Pos2, Pos3, .., PosM]
[Vel1, Vel2, Vel3, .., VelM]
)
迭代完全线性,除了跳转到不同的存储。
上述查询的迭代模式将是
positions: [Pos1, Pos2, Pos3, .., PosN], [Pos1, Pos2, Pos3, .., PosM]
velocities: [Vel1, Vel2, Vel3, .., VelN], [Vel1, Vel2, Vel3, .., VelM]
^
Jump occurs here
跳跃类似于两个迭代器的链。我们查看所有匹配特定查询的存储。如果查询是 `Write
每个组件组合都将存在于单独的存储中。这保证了迭代始终是线性的。
基准测试
入门
extern crate pyro;
use pyro::{ World, Entity, Read, Write, All, SoaStorage };
struct Position;
struct Velocity;
// By default creates a world backed by a [`SoaStorage`]
let mut world: World<SoaStorage> = World::new();
let add_pos_vel = (0..99).map(|_| (Position{}, Velocity{}));
// ^^^^^^^^^^^^^^^^^^^^^^^^
// A tuple of (Position, Velocity),
// Note: Order does *not* matter
// Appends 99 entities with a Position and Velocity component.
world.append_components(add_pos_vel);
// Appends a single entity
world.append_components(Some((Position{}, Velocity{})));
// Requests a mutable borrow to Position, and an immutable borrow to Velocity.
// Common queries can be reused with a typedef like this but it is not necessary.
type PosVelQuery = (Write<Position>, Read<Velocity>);
// Retrieves all entities that have a Position and Velocity component as an iterator.
world.matcher::<All<PosVelQuery>>().for_each(|(pos, vel)|{
// ...
});
// The same query as above but also retrieves the entities and collects the entities into a
// `Vec<Entity>`.
let entities: Vec<Entity> =
world.matcher_with_entities::<All<PosVelQuery>>()
.filter_map(|(entity, (pos, vel))|{
Some(entity)
}).collect();
// Removes all the entities
world.remove_entities(entities);
let count = world.matcher::<All<PosVelQuery>>().count();
assert_eq!(count, 0);
依赖
~3MB
~56K SLoC