Crate tiny_ecs

Source
Expand description

§Tiny ECS

The intention of this crate is that a basic ECS is provided, where you will be required to exercise a little additional control.

Where most other ECS crates provide a mechanism for inserting “systems” in to the ECS to run against entities, this one leaves it out - you can think of it as a “system for entity/components”. You will need to create external systems; these can be a function, a loop, or anything else.

Internally this ECS is the use of bitmasks. Each entity ID is in practice an internal index number in to an array which contains bitmasks. The bitmasks themselves keep track of what components the entity has.

Note: borrows of Persist are checked at runtime, but there are also some unchecked borrows provided.

§Examples

§Demonstrating use

use tiny_ecs::Entities;
use tiny_ecs::ECSError;

// These are the "components" we will use
struct Vector1 {
    x: i32,
}
struct Vector2 {
    x: i32,
    y: i32,
}
struct Vector3 {
    x: i32,
    y: i32,
    z: i32,
}
// Initialize the Entity collection
let mut entities = Entities::new(Some(3), Some(3));

// Creating an entity uses the builder pattern.
// Be sure to return the ID if you need to track it
let entity_1 = entities.new_entity()
                       .with(Vector1 { x: 42 })?
                       .with(Vector3 { x: 3, y: 10, z: -12 })?
                       .finalise()?;
// The entity ID will increment up from 0
assert_eq!(entity_1, 0);

// Do note however, that you can keep adding parts to this entity
// via the builder pattern until you choose to create another entity

// To add another entity you need to create one
entities.new_entity()
        .with(Vector2 { x: 66, y: 6 })?
        .with(Vector1 { x: 6 })?
        .finalise()?;

§Access an entities part of type <T>

struct Vector3 {x: i32, y: i32, z: i32 }
// To get access to a part belonging to an entity you need
// first to get the component map created for the part type
// You need to 'anchor' this with a let or the ref is
// dropped before you can use it
let mut components = entities
    .borrow_mut::<Vector3>()?;
// You can then use the part by getting a reference
let mut part = components.get_mut(entity_1).unwrap();
assert_eq!(part.z, -12);

§Check if Entity contains a part type + remove part

struct Vector1 {x: i32 }
let entity_1 = entities.new_entity()
                       .with(Vector1 { x: 3 })?
                       .finalise()?;
// You can check if an entity contains a part with the type signature
if entities.entity_contains::<Vector1>(entity_1) {
    assert!(entities.rm_component_from::<Vector1>(entity_1).is_ok());
}
assert_eq!(entities.entity_contains::<Vector1>(entity_1), false);

§A system that uses an get_mut()

struct Vector1 {x: i32 }

// Make a system of some form that takes a `Persist<T>` arg
fn some_system(mut components: &mut Persist<Vector1>) {
    // You can then iterate over the components directly
    for (k, v) in components.iter_mut().enumerate() {
        v.x += 1;
        assert!(v.x > k as i32);
    }
}
some_system(&mut components);

§Get components for an entity ID list

struct Vector1 {x: i32 }

// A system that fetches the components for only the entities you are require
fn second_system(active: &[usize], mut v1_map: &mut Persist<Vector1>) {
    for id in active {
        if let Some(part) = v1_map.get_mut(*id) {
            part.x = 42;
        }
    }
}
second_system(&[0, 1, 2], &mut components);

§A more complex system using Persists directly


// Or a system handles the `Entities` container directly
fn other_system(active_ents: &[usize], entities: &mut Entities) -> Result<(), ECSError> {
    // You can mutably borrow multiple component maps at once
    let mut v1_components = entities
        .borrow_mut::<Vector1>()?;

    let mut v2_components = entities
        .borrow_mut::<Vector2>()?;

    // But not have a mutable borrow and immutable borrow to the same map
    // Fails at runtime!
    // let v2_components = entities.borrow::<Vector2>().unwrap();
    for id in active_ents {
        if entities.entity_contains::<Vector1>(*id) &&
           entities.entity_contains::<Vector2>(*id) {
            let v1_part = v1_components.get_mut(*id).unwrap();
            let v2_part = v2_components.get_mut(*id).unwrap();
            v1_part.x = 42;
            assert_ne!(v1_part.x, 43);
            assert_eq!(v1_part.x, 42);
        }
    }
    Ok(())
}
other_system(&[0, 1, 2], &mut entities);

Structs§

  • This is the root of the ECS implementation
  • Immutable reference container for Persist returned by Entities::borrow()
  • Mutable reference container for Persist returned by Entities::borrow_mut()
  • Iteration will not always be in order

Enums§