tiny_ecs 0.9.0

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, and in some use cases you will need to use downcast_ref or downcast_mut.

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

Demonstrating use

use tiny_ecs::{Entities, PartMap};
use std::any::Any;
use std::collections::HashMap;
use std::cell::{Ref, RefMut};

struct Vector1 { x: i32 }

struct Vector2 { x: i32, y: i32 }

struct Vector3 { x: i32, y: i32, z: i32 }

let mut entities = Entities::new(Some(10));
let entity_1 = entities.get_free_slot().unwrap();

assert!(entities
.add_part(entity_1,
Vector1 { x: 42 }).is_ok());
assert!(entities
.add_part(entity_1,
Vector3 { x: 3, y: 10, z: -12 }).is_ok());

let entity_2 = entities.get_free_slot().unwrap();
assert!(entities
.add_part(entity_2,
Vector2 { x: 66, y: 6 }).is_ok());
assert!(entities
.add_part(entity_2,
Vector1 { x: 6 }).is_ok());

if entities.entity_contains::<Vector3>(entity_1) {
let mut partmap = entities.get_pmap_mut::<Vector3>().unwrap().unwrap();
let mut part: &Vector3 = partmap.get_part_mut(entity_1).unwrap();
assert_eq!(part.z, -12);
}

if entities.entity_contains::<Vector1>(entity_1) {
assert!(entities.rm_part::<Vector1>(entity_1));
}
assert_eq!(entities.entity_contains::<Vector1>(entity_1), false);

fn some_system(mut partmap: RefMut<PartMap>) {
for (k, v) in partmap.iter_mut() {
let part = v.downcast_mut::<Vector1>().unwrap();
part.x += 1;
assert!(part.x > *k as i32);
}
}
some_system(entities.get_pmap_mut::<Vector1>().unwrap().unwrap());

fn second_system(active: &[u32], mut v1_map: RefMut<PartMap>) {
for id in active {
if let Ok(part) = v1_map.get_part_mut::<Vector1>(*id) {
part.x = 42;
}
}
}
second_system(&[0, 1, 2], entities.get_pmap_mut::<Vector1>().unwrap().unwrap());

// Or a system can use a list of entities you keep track of
fn other_system(active_ents: &[u32], entities: &mut Entities) {
let mut partmap = entities.get_pmap_mut::<Vector1>()
.expect("No part found for entity")
.expect("Mutable borrow failed");
for id in active_ents {
if entities.entity_contains::<Vector1>(*id) {
let part: &mut Vector1 = partmap.get_part_mut(*id).unwrap();
part.x = 42;
assert_ne!(part.x, 43);
assert_eq!(part.x, 42);
}
}
}
other_system(&[0, 1, 2], &mut entities);