freecs 0.1.1

A high-performance, archetype-based Entity Component System (ECS) written in Rust.
Documentation

freecs

A high-performance, archetype-based Entity Component System (ECS) written in Rust.


## Features

- ๐Ÿš€ **Archetype-based Storage**: Optimal cache coherency through grouped component storage
- ๐Ÿ”„ **Built-in Parallel Processing**: Leverages rayon for efficient multi-threading
- ๐Ÿ“ฆ **Zero-overhead Component Access**: Direct slice indexing for component data
- ๐Ÿ›ก๏ธ **Type-safe Component Management**: Compile-time component type checking
- ๐Ÿ”ง **Dynamic Components**: Add or remove components at runtime
- ๐Ÿงน **Table Defragmentation**: Maintains optimal memory layout
- ๐Ÿ“ **Batch Operations**: Efficient bulk entity operations
- ๐Ÿ’พ **Serialization Support**: Built-in serde support for all core types

## Quick Start

Add this to your `Cargo.toml`:

```toml
[dependencies]
freecs = "0.1.1"

And in main.rs:

use freecs::{world, has_components};

// Define your components
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
struct Position { x: f32, y: f32 }

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

// Create a world with your components
world! {
  World {
      positions: Position => POSITION,
      velocities: Velocity => VELOCITY,
  }
}

pub fn main() {
    // Create a new world
    let mut world = World::default();

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

    // Read a component
    let position = get_component::<Position>(&world, *entity, POSITION);
    println!("Position: {:?}", position); // Prints "Some(Position { x: 0.0, y: 0.0 })"

    // Write a component
    if let Some(_position) = get_component_mut::<Position>(&mut world, *entity, POSITION) {
        // _position could be mutated here
    }

// Assign a component
set_component(&mut world, *entity, VELOCITY, Velocity { x: 1.0 y: 2.0 } );

// You can write plain functions
fn run_systems(world: &mut World, dt: f32) {
    use rayon::prelude::*;
    world.tables.par_iter_mut().for_each(|table| {
        if has_components!(table, POSITION | VELOCITY) {
            update_positions_system(&mut table.positions, &table.velocities, dt);
        }
    });
}

#[inline]
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;
      });
}

Running the Demo

The project includes a demo:

cargo run -r --example demo

Controls:

  • Use arrow keys to rotate the camera
  • Press Escape to exit

Data Oriented vs Object Oriented

When processing entities, the cache-friendly layout makes a significant difference:

// Given 10,000 entities with Position and Velocity:

// Traditional OOP: Cache misses, unpredictable memory access
for entity in entities {
  entity.position.x += entity.velocity.x;  // ๐Ÿ’ฅ Cache miss probable
  entity.position.y += entity.velocity.y;  // ๐Ÿ’ฅ Cache miss probable
}

// freecs: Streaming through contiguous memory
for table in &mut world.tables {
  if has_components!(table, POSITION | VELOCITY) {
      // โœจ CPU prefetcher can predict this pattern
      // โœจ Full cache lines are utilized
      // โœจ Minimal memory stalls
      table.positions.iter_mut()
          .zip(table.velocities.iter())
          .for_each(|(pos, vel)| {
              pos.x += vel.x;
              pos.y += vel.y;
          });
  }
}

Benefits of Macro-based Generation

  • Inlining: The entire ECS can be inlined into your crate by inlining the impl_world! macro
  • Zero Overhead: No virtual dispatch or runtime type checking. Only static dispatch is used.
  • Compile-time Optimization: The compiler sees your exact component types and can optimize accordingly
  • No Runtime Dependencies: The core ECS can work without external crates
  • Specialized Code: Generated code is specific to your component types

License

This project is licensed under the MIT License - see the LICENSE file for details.