freECS
freecs is a zero-abstraction table-based ECS library for Rust, in about ~600 lines
A macro is used to define the world and its components, and generates
the entity component system as part of your source code at compile time. The generated code
contains only plain data structures (no methods) and free functions that transform them, achieving static dispatch.
The internal implementation is ~600 loc (aside from tests, comments, and example code),
and does not use object orientation, generics, traits, or dynamic dispatch.
Quick Start
Add this to your Cargo.toml:
[dependencies]
freecs = "0.4.2"
rayon = "^1.10.0"
And in main.rs:
use freecs::{ecs, table_has_components};
use rayon::prelude::*;
ecs! {
World {
position: Position => POSITION,
velocity: Velocity => VELOCITY,
health: Health => HEALTH,
}
Resources {
delta_time: f32
}
}
pub fn main() {
let mut world = World::default();
let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
println!(
"Spawned {} with position and velocity",
world.get_all_entities().len()
);
let position = world.get_component::<Position>(entity, POSITION);
println!("Position: {:?}", position);
let position = world.get_position(entity);
println!("Position: {:?}", position);
if let Some(position) = world.get_position_mut(entity) {
position.x += 1.0;
}
println!(
"Component mask before adding health component: {:b}",
world.component_mask(entity).unwrap()
);
world.add_components(entity, HEALTH);
println!(
"Component mask after adding health component: {:b}",
world.component_mask(entity).unwrap()
);
let entities = world.get_all_entities();
println!("All entities: {entities:?}");
let players = world.query_entities(POSITION | VELOCITY | HEALTH);
println!("Player entities: {players:?}");
let first_player_entity = world.query_first_entity(POSITION | VELOCITY | HEALTH);
println!("First player entity : {first_player_entity:?}");
world.remove_components(entity, HEALTH);
systems::run_systems(&mut world);
world.despawn_entities(&[entity]);
}
use components::*;
mod components {
#[derive(Default, Debug, Clone)]
pub struct Position {
pub x: f32,
pub y: f32,
}
#[derive(Default, Debug, Clone)]
pub struct Velocity {
pub x: f32,
pub y: f32,
}
#[derive(Default, Debug, Clone)]
pub struct Health {
pub value: f32,
}
}
mod systems {
use super::*;
pub fn run_systems(world: &mut World) {
let delta_time = world.resources.delta_time;
world.tables.par_iter_mut().for_each(|table| {
if table_has_components!(table, POSITION | VELOCITY | HEALTH) {
update_positions_system(&mut table.position, &table.velocity, delta_time);
}
if table_has_components!(table, HEALTH) {
health_system(&mut table.health);
}
});
}
#[inline]
pub fn update_positions_system(positions: &mut [Position], velocities: &[Velocity], dt: f32) {
positions
.par_iter_mut()
.zip(velocities.par_iter())
.for_each(|(pos, vel)| {
pos.x += vel.x * dt;
pos.y += vel.y * dt;
});
}
#[inline]
pub fn health_system(health: &mut [Health]) {
health.par_iter_mut().for_each(|health| {
health.value *= 0.98;
});
}
}
Examples
Run the examples with:
cargo run -r --example cubes
License
This project is licensed under the MIT License - see the LICENSE file for details.