[−][src]Crate tiny_ecs
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.
In all cases you will need to use downcast_ref
or downcast_mut
to
get the correct types to work with.
Both downcast_ref
or downcast_mut
return a reference to the type, in
this case it will be PartMap<T>
where T is your stored type. The
downcast is required because the PartMap
is stored as an Any
trait
object to enable storage of multiple PartMap<T>
.
The basis of 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.
For the most part, bitmasks are handled for you, and some helper methods
are available to hide their use, but there are also methods to get the
bitmask for any ID if you are inclined to do some manual management.
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 partmaps allocs (they will be equal in size).
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)); // To create an entity you only need to add the first part using // a free slot let entity_1 = entities.get_free_slot().unwrap(); entities.add_part(entity_1, Vector1 { x: 42 }); // And you can add more parts to it // The entity is only considered newly created if no parts existed before entities.add_part(entity_1, Vector3 { x: 3, y: 10, z: -12 }); // To add another entity you need another free slot let entity_2 = entities.get_free_slot().unwrap(); entities.add_part(entity_2, Vector2 { x: 66, y: 6 }); entities.add_part(entity_2, Vector1 { x: 6 });
Access an entities part of type <T>
// To get access to a part belonging to an entity you need // first to get the partmap 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 partmap = entities.get_map_mut::<Vector3>() .expect("No part found for entity"); // To access the partmap proper you need to downcast let mut partmap = partmap.downcast_mut::<PartMap<Vector3>>().unwrap(); // You can then use the part by getting a reference let mut part = partmap.get_part_mut(entity_1).unwrap(); assert_eq!(part.z, -12);
Check if Entity
contains a part type + remove part
// 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)); } assert_eq!(entities.entity_contains::<Vector1>(entity_1), false);
A system that uses an iter_mut()
use std::cell::RefMut; // Make a system of some form that takes a `PartMap<T>` arg fn some_system(mut partmap: &mut PartMap<Vector1>) { // You can then iterate over the parts directly for (k, v) in partmap.iter_mut() { v.x += 1; assert!(v.x > *k as i32); } } some_system(&mut partmap);
Get parts for an entity ID list
use std::cell::{Ref, RefMut}; // A system that fetches the parts for only the entities you are require fn second_system(active: &[u32], mut v1_map: &mut PartMap<Vector1>) { for id in active { if let Ok(part) = v1_map.get_part_mut(*id) { part.x = 42; } } } second_system(&[0, 1, 2], &mut partmap);
A more complex system using PartMaps directly
// Or a system handles the `Entities` container directly fn other_system(active_ents: &[u32], entities: &mut Entities) { // You can mutably borrow multiple part types at once let mut v1_partmap = entities.get_map_mut::<Vector1>() .expect("No part found for entity"); let mut v1_partmap = v1_partmap .downcast_mut::<PartMap<Vector1>>().unwrap(); let mut v2_partmap = entities.get_map_mut::<Vector2>() .expect("No part found for entity"); let mut v2_partmap = v2_partmap .downcast_mut::<PartMap<Vector2>>().unwrap(); // But not have a mutable borrow and immutable borrow to the same // Fails at runtime! // let v2_partmap = entities.get_map_ref::<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_partmap.get_part_mut(*id).unwrap(); let v2_part = v2_partmap.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);
Structs
Entities | This is the root of the ECS implementation |
PartMap |
|
Constants
EMPTY | Bitmask used to fill the initial mask list, and replace deleted entities |