5 releases (3 breaking)

0.12.0 Jan 27, 2024
0.11.1 Sep 27, 2023
0.11.0 Jul 16, 2023
0.10.0 Nov 22, 2022
0.5.0 Nov 9, 2021

#100 in Game dev

Download history 3/week @ 2024-01-24 2/week @ 2024-02-14 8/week @ 2024-02-21 13/week @ 2024-02-28 1/week @ 2024-03-06 2/week @ 2024-03-13 56/week @ 2024-03-20 5/week @ 2024-03-27 7/week @ 2024-04-03

68 downloads per month

MIT/Apache

145KB
4K SLoC

Sparsey

Crates.io Documentation

Sparsey is a sparse set-based Entity Component System (ECS).

Design Goals

  • Be flexible: Any Send + Sync + 'static type can be used as a component.
  • Be concise: The most commonly used functionalities should require the least amount of typing.
  • Make use of sparse sets: Provide features exclusive to sparse set-based ECS.

Example

use sparsey::prelude::*;

struct Position(f32);
struct Velocity(f32);

fn main() {
    let mut entities = EntityStorage::default();
    entities.register::<Position>();
    entities.register::<Velocity>();

    entities.create((Position(0), Velocity(1)));
    entities.create((Position(0), Velocity(2)));
    entities.create((Position(0), Velocity(3)));

    entities.run(|mut positions: CompMut<Position>, velocities: Comp<Velocity>| {
        (&mut positions, &velocities).for_each(|(position, velocity)| {
            position.0 += velocity.0;
        }); 
    });
}

Features

Easy to Use Systems

Systems are plain functions.

struct HealMultiplier(f32);

fn update_positions(mut positions: CompMut<Position>, velocities: Comp<Velocity>) {
    (&mut positions, &velocities).for_each(|(position, velocity)| {
        position.0 += velocity.0;
    });
}

fn update_hps(mut hps: CompMut<Hp>, heals: Comp<Heal>, heal_multipler: Res<HealMultiplier>) {
    (&mut hps, &heals).for_each(|(hp, heal)| {
        hp.0 += heal.0 * heal_multiplier.0;
    });
}

let mut world = World::default();
world.entities.register::<Position>();
world.entities.register::<Velocity>();
world.resources.insert(HealMultiplier(1.2));

world.run(update_positions);
world.run(update_hps);

Expressive Queries

Get, include and exclude components using Sparsey's query API.

fn queries(a: Comp<A>, b: Comp<B>, c: Comp<C>, d: Comp<D>, e: Comp<E>) {
    // Iter components A and B from entities with A and B.
    (&a, &b).for_each(|(a, b)| {
        // ... 
    });

    // Iter components A from entities with A and B.
    (&a).include(&b).for_each(|a| {
        // ...
    });

    // Iter components A from entities with A and without B.
    (&a).exclude(&b).for_each(|a| {
        // ...
    });

    // Iter components A from entities with A and B, without C.
    (&b).include(&b).exclude(&c).for_each(|a| {
        // ...
    });
}

Great Performance with Grouped Storages

Sparsey allows the user to "group" component storages to greatly optimize iteration performance. Groups are created by setting a GroupLayout.

let layout = GroupLayout::builder()
    .add_group::<(A, B)>()
    .add_group::<(A, B, C, D>)>()
    .build();

let entities = EntityStorage::new(&layout);

After the layout is set, iterators over the grouped storages become "dense", greatly improving their performance. Additionally, grouped storages allow access to their components and entities as slices.

fn group_slices(a: Comp<A>, b: Comp<B>) {
    if let Some(entities) = (&a, &b).group_entities() {
        // ...
    }

    if let Some((a_slice, b_slice)) = (&a, &b).group_components() {
        // ...
    }

    if let Some((entities, (a_slice, b_slice))) = (&a, &b).group_data() {
        // ...
    }
}

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.

Dependencies

~29KB