Crate shipyard[][src]

Expand description

Shipyard is an Entity Component System focused on usability and speed.

Getting started

The user guide is a great place to learn all about Shipyard!
Here’s two examples to get an idea of what it provides:

use shipyard::prelude::*;

struct Health(f32);
struct Position { x: f32, y: f32 }

#[system(InAcid)]
fn run(pos: &Position, mut health: &mut Health) {
    (&pos, &mut health).iter()
        .filter(|(pos, _)| is_in_acid(pos))
        .for_each(|(pos, mut health)| {
            health.0 -= 1.0;
        });
}

fn is_in_acid(pos: &Position) -> bool {
    // it's wet season
    true
}

let world = World::new();

{
    let (mut entities, mut positions, mut healths) =
        world.borrow::<(EntitiesMut, &mut Position, &mut Health)>();
    
    entities.add_entity(
        (&mut positions, &mut healths),
        (Position { x: 0.0, y: 0.0 },
        Health(1000.0))
    );
}

world.run_system::<InAcid>();

Let’s make some pigs!

use shipyard::prelude::*;

struct Health(f32);
struct Fat(f32);

#[system(Reproduction)]
fn run(mut fat: &mut Fat, mut health: &mut Health, mut entities: &mut Entities) {
    let count = (&health, &fat).iter().filter(|(health, fat)| health.0 > 40.0 && fat.0 > 20.0).count();
    (0..count).for_each(|_| {
        entities.add_entity((&mut health, &mut fat), (Health(100.0), Fat(0.0)));
    });
}

#[system(Meal)]
fn run(mut fat: &mut Fat) {
    (&mut fat).iter().into_chunk(8).ok().unwrap().for_each(|slice| {
        for fat in slice {
            fat.0 += 3.0;
        }
    });
}

#[system(Age)]
fn run(mut health: &mut Health, thread_pool: ThreadPool) {
    use rayon::prelude::ParallelIterator;

    thread_pool.install(|| {
        (&mut health).par_iter().for_each(|health| {
            health.0 -= 4.0;
        });
    });
}

let world = World::new();

world.run::<(EntitiesMut, &mut Health, &mut Fat), _, _>(|(mut entities, mut health, mut fat)| {
    (0..100).for_each(|_| {
        entities.add_entity(
            (&mut health, &mut fat),
            (Health(100.0), Fat(0.0))
        );
    })
});

world.add_workload::<(Meal, Age), _>("Life");
world.add_workload::<Reproduction, _>("Reproduction");

for day in 0..100 {
    if day % 6 == 0 {
        world.run_workload("Reproduction");
    }
    world.run_default();
}

world.run::<&Health, _, _>(|health| {
    // we've got some new pigs
    assert_eq!(health.len(), 900);
});

Features

  • parallel (default) — adds parallel iterators and dispatch
  • proc (default) — adds system proc macro
  • serde — adds (de)serialization support with serde
  • non_send — add methods and types required to work with !Send components
  • non_sync — add methods and types required to work with !Sync components
  • std (default) — let shipyard use the standard library

Unsafe

This crate uses unsafe both because sometimes there’s no way around it, and for performance gain.
Releases should have all invocation of unsafe explained.
If you find places where a safe alternative is possible without repercussion (small ones are sometimes acceptable) feel free to open an issue or a PR.

Modules

Contains all error types.

Re-export types that aren’t needed in most use cases.

Structs

Contains all components present in the World.

Entities holds the EntityIds to all entities: living, removed and dead.

Type used to borrow Entities mutably.

Type used to borrow the rayon::ThreadPool inside World.

Type used to access the value of a unique storage.

Holds all components and keeps track of entities and what they own.