tiny_ecs 0.17.5

A tiny ECS that tries to avoid unnecessary copy/clones
Documentation

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. This is somewhat due to some limitations, and also due to trying to maintain as little overhead as possible - this means no unneccesary copies/clones.

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.

Attention: borrows of ComponentMap are checked at runtime.

Attention: this crate will panic in many cases if used incorrectly. This is intentional to prevent many kinds of bugs and to encourage you to define beviour well.

Examples

Init with a capacity

This is good to do if you know the size required as it will prevent many reallocs/moves as data is added. This affects both the entity and component map allocs (they will be equal in length).

use tiny_ecs::Entities;

let mut entities = Entities::new(Some(1000), Some(24));

Demonstrating use

use tiny_ecs::Entities;

// 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_part(Vector1 { x: 42 })
.with_part(Vector3 { x: 3, y: 10, z: -12 })
.finalise();
assert!(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_part(Vector2 { x: 66, y: 6 })
.with_part(Vector1 { x: 6 });

The builder pattern is powerful

let ent = world .entities
.new_entity()
.with_part(ParticleID { id })
.with_part(Sprite::new(sprite_id, rec))
.with_part(InputTypes::Player(binds))
.with_part(Player::new(5.0, 15.0))
.finalise();

Access an entities part of type <T>

# use tiny_ecs::{Entities, ComponentMap};
# struct Vector3 {x: i32, y: i32, z: i32 }
# let mut entities = Entities::new(Some(3), Some(3));
# let entity_1 = entities.new_entity()
#                         .with_part(Vector3 { x: 3, y: 10, z: -12 })
#                         .finalise();
// 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>()
.unwrap();
// You can then use the part by getting a reference
let mut part = components.get_part_mut(entity_1).unwrap();
assert_eq!(part.z, -12);

Check if Entity contains a part type + remove part

# use tiny_ecs::Entities;
# struct Vector1 {x: i32 }
# let mut entities = Entities::new(Some(3), Some(3));
let entity_1 = entities.new_entity()
.with_part(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_part::<Vector1>(entity_1).is_ok());
}
assert_eq!(entities.entity_contains::<Vector1>(entity_1), false);

A system that uses an get_mut()

# use tiny_ecs::{Entities, ComponentMap};
# struct Vector1 {x: i32 }
# let mut entities = Entities::new(Some(3), Some(3));
# entities.new_entity().with_part(Vector1 { x: 3 });

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

Get components for an entity ID list

# use tiny_ecs::{Entities, ComponentMap};
# struct Vector1 {x: i32 }
# let mut entities = Entities::new(Some(3), Some(3));
# entities.new_entity().with_part(Vector1 { x: 3 });

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

A more complex system using ComponentMaps directly

# use tiny_ecs::{Entities, ComponentMap};
# struct Vector1 {x: i32 }
# struct Vector2 {x: i32, y: i32 }
# let mut entities = Entities::new(Some(3), Some(3));
# entities.new_entity().with_part(Vector1 { x: 3 })
# .with_part(Vector2 { x: 3, y: 3 });

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

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

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