Crate freecs

Source
Expand description

freecs is an abstraction-free ECS library for Rust, designed for high performance and simplicity.

It provides an archetypal table-based storage system for components, allowing for fast queries, fast system iteration, and parallel processing. Entities with the same components are stored together in contiguous memory, optimizing for cache coherency and SIMD operations.

A macro is used to define the world and its components, generating the entire entity component system at compile time. The generated code contains only plain data structures and free functions that transform them.

The core implementation is ~500 loc, is fully statically dispatched and does not use object orientation, generics, or traits.

§Creating a World

use freecs::{ecs, Entity};

// First, define components.
// They must implement: `Default`

#[derive(Default, Clone, Debug)]
struct Position { x: f32, y: f32 }

#[derive(Default, Clone, Debug)]
struct Velocity { x: f32, y: f32 }

#[derive(Default, Clone, Debug)]
struct Health { value: f32 }

// Then, create a world with the `ecs!` macro.
// Resources are stored independently of component data.
// The `World` and `Resources` type names can be customized.
ecs! {
  World {
    position: Position => POSITION,
    velocity: Velocity => VELOCITY,
    health: Health => HEALTH,
  }
  Resources {
    delta_time: f32
  }
}

§Entity and Component Access

let mut world = World::default();

// Spawn entities with components by mask
let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];

// Lookup and modify a component using generated methods
if let Some(pos) = world.get_position_mut(entity) {
    pos.x += 1.0;
}

// Read components
if let Some(pos) = world.get_position(entity) {
    println!("Position: ({}, {})", pos.x, pos.y);
}

// Set components (adds if not present)
world.set_position(entity, Position { x: 10.0, y: 20.0 });
world.set_velocity(entity, Velocity { x: 1.0, y: 0.0 });

// Add new components to an entity by mask
world.add_components(entity, HEALTH | VELOCITY);

// Or use the generated add methods
world.add_health(entity);

// Remove components from an entity by mask
world.remove_components(entity, VELOCITY | POSITION);

// Or use the generated remove methods
world.remove_velocity(entity);

// Check if entity has components
if world.entity_has_position(entity) {
    println!("Entity has position component");
}

// Query all entities
let entities = world.get_all_entities();
println!("All entities: {entities:?}");

// Query entities, iterating over all entities matching the component mask
let entities = world.query_entities(POSITION | VELOCITY);

// Query for the first entity matching the component mask, returning early when found
let player = world.query_first_entity(POSITION | VELOCITY);

§Systems

A system is any function that takes a mutable reference to a world, querying the world for entities to process and operating on their components.

fn example_system(world: &mut World) {
  for entity in world.query_entities(POSITION | VELOCITY) {
      // Use the generated methods for type-safe access
      if let Some(position) = world.get_position_mut(entity) {
          if let Some(velocity) = world.get_velocity(entity) {
              position.x += velocity.x;
              position.y += velocity.y;
          }
      }
  }
}

§Entity Builder

let mut world = World::default();
let entities = EntityBuilder::new()
    .with_position(Position { x: 1.0, y: 2.0 })
    .with_velocity(Velocity { x: 0.0, y: 1.0 })
    .spawn(&mut world, 2);
     
// Access the spawned entities
let first_pos = world.get_position(entities[0]).unwrap();
assert_eq!(first_pos.x, 1.0);

Re-exports§

pub use paste;

Macros§

ecs
table_has_components

Structs§

Entity