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 behaviour 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);