sparsey-0.1.0 has been yanked.
Sparsey
Sparsey is a sparse set-based Entity Component System with lots of features and nice syntax ~( ˘▾˘~)
Check out the Sparsey Cheat Sheet and examples to get started!
Example
use sparsey::prelude::*;
struct Position(f32);
struct Velocity(f32);
struct Immovable;
fn update_velocity(mut vel: CompMut<Velocity>, imv: Comp<Immovable>) {
for (mut vel,) in (&mut vel,).include(&imv).iter() {
vel.0 = 0.0;
}
}
fn update_position(mut pos: CompMut<Position>, vel: Comp<Velocity>) {
for (mut pos, vel) in (&mut pos, &vel).iter() {
pos.0 += vel.0;
}
}
fn main() {
let mut world = World::default();
world.register::<Position>();
world.register::<Velocity>();
world.create_entity((Position(0.0), Velocity(1.0)));
world.create_entity((Position(0.0), Velocity(2.0)));
world.create_entity((Position(0.0), Velocity(3.0), Immovable));
let mut dispatcher = Dispatcher::builder()
.add_system(update_velocity.system())
.add_system(update_position.system())
.build();
for _ in 0..3 {
dispatcher.run_seq(&mut world).unwrap();
world.increment_ticks().unwrap();
}
}
Features
Systems
Systems are functions that have Component views and Resource views as parameters.
fn movement(mut pos: CompMut<Position>, vel: Comp<Velocity>, time: Res<Time>) {
for (mut pos, vel) in (&mut pos, &vel).iter() {
pos.x += vel.x * time.delta();
pos.y += vel.y * time.delta();
}
}
Fallible systems may return a SystemResult to signal failure.
fn save_entities(a: Comp<A>, b: Comp<B>, c: Comp<C>) -> SystemResult {
for (entity, (a, b, c)) in (&a, &b, &c).iter().entities() {
try_save_entity(entity, a, b, c)?;
}
Ok(())
}
Systems are executed using a Dispatcher. Errors can be retrieved after the systems finish executing.
let mut dispatcher = Dispatcher::builder()
.add_system(movement.system())
.add_system(save_entities.system())
.build();
if let Err(run_error) = dispatcher.run_seq(&mut world) {
for error in run_error.errors() {
println!("{}", error);
}
}
Expressive Queries
Queries can be used to iterate entities and components.
fn example(a: Comp<A>, b: Comp<B>, c: Comp<C>) {
for (a, b, c) in (&a, &b, &c).iter() {}
for (entity, (a, b, c)) in (&a, &b, &c).iter().entities() {}
for (a,) in (&a,).include((&b, &c)).iter() {}
for (a,) in (&a,).include(&b).exclude(&c).iter() {}
}
Granular Change Detection
Sparsey supports change detection at a component level.
fn example(a: Comp<A>, b: Comp<B>, c: Comp<C>) {
use sparsey::filters::{added, mutated, changed};
for (a, b, c) in (added(&a), &b, &c).iter() {}
for (a, b, c) in (mutated(&a), &b, &c).iter() {}
for (a, b, c) in (changed(&a), &b, &c).iter() {}
for (a, b, c) in (!added(&a), &b, &c).iter() {}
}
Groups and Layouts
Layouts can be used to group component storages within a World. Grouped storages are much faster to iterate over and allow accessing their components and entities as ordered slices, with a small performance penalty when adding or removing components.
let layout = Layout::builder()
.add_group(<(A, B)>::group())
.add_group(<(A, B, C)>::group())
.build();
let mut world = World::default();
world.set_layout(&layout);
All iterations bellow get a significant performance boost without having to change the code at all.
fn iterators(a: Comp<A>, b: Comp<B>, c: Comp<C>) {
for (a, b) in (&a, &b).iter() {}
for (a,) in (&a,).include(&b).iter() {}
for (a, b, c) in (&a, &b, &c).iter() {}
for (a,) in (&a,).include((&b, &c)).iter() {}
for (a, b) in (&a, &b).exclude(&c).iter() {}
for (a,) in (&a,).include(&b).exclude(&c).iter() {}
}
Groups allow accessing their components and entities as ordered slices.
fn slices(a: Comp<A>, b: Comp<B>, c: Comp<C>) {
let _: &[Entity] = (&a, &b).entities().unwrap();
let _: (&[A], &[B], &[C]) = (&a, &b, &c).components().unwrap();
let _: (&[Entity], (&[A], &[B])) = (&a, &b)
.exclude(&c)
.entities_components()
.unwrap();
}
Thanks
Sparsey takes inspiration and borrows features from other free and open source ECS projects, namely Bevy, EnTT, Legion, Shipyard and Specs. Make sure you check them out!
License
Sparsey is dual-licensed under either
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above without any additional terms or conditions.