Skip to content

ecoricemon/my-ecs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

my-ecs

Crates.io CI Status Codecov

my-ecs is an Entity Component System library for Rust with parallel system execution, built-in async task integration, and wasm32 support.

What It Provides

  • Parallel scheduling by default. Systems can run across multiple worker threads while my-ecs enforces data access rules for components, entities, and resources.
  • Explicit data access. Systems declare what they read or write, and the scheduler uses that information to order execution safely.
  • Async integration. Systems can enqueue futures through the built-in posting/execution flow, which makes it practical to mix ECS logic with async work.
  • Native and web targets. The crate supports native workers and includes web-worker support for wasm32-unknown-unknown.

Core Concepts

  • Component: plain data attached to entities.
  • Entity: a collection of components.
  • System: logic that reads or writes ECS data.
  • Resource: unique global data stored once in the world.
  • Filter / Select: type-level queries created with the filter! macro.

Architecture

my-ecs overview

Demo

my-ecs-demo-small.mp4

my-ecs demo video

Rendering the Mandelbrot set with multiple threads using my-ecs

Try the demo here

Quick Start

You need Rust's cargo command to run these examples. If cargo is not available yet, install Rust first.

If you already have this repository checked out, run the smallest example first:

cargo run -p my-ecs --example hello_world

Expected output:

Object: (1, 2)
MovableObject: (3, 4)
Object: (1, 2)
MovableObject: (13, 14)

To try my-ecs in a new Rust project:

cargo new my-ecs-hello
cd my-ecs-hello
cargo add my-ecs

Then replace src/main.rs with this full program:

use my_ecs::prelude::*;

#[derive(Component)]
struct Position {
    x: u32,
    y: u32,
}

#[derive(Component)]
struct Movable;

#[derive(Entity)]
struct Object {
    pos: Position,
}

#[derive(Entity)]
struct MovableObject {
    pos: Position,
    _m: Movable,
}

filter!(AllPositions, Target = Position);
filter!(MovablePositions, Target = Position, All = Movable);

fn main() {
    Ecs::create(WorkerPool::with_len(2), [2])
        .register_entity_of::<Object>()
        .register_entity_of::<MovableObject>()
        .add_once_systems((
            create_objects,
            print_positions,
            move_objects,
            print_positions,
        ))
        .step();
}

fn create_objects(ew: EntWrite<(Object, MovableObject)>) {
    let (mut objects, mut movable_objects) = ew.take_recur();

    objects.add(Object {
        pos: Position { x: 1, y: 2 },
    });

    movable_objects.add(MovableObject {
        pos: Position { x: 3, y: 4 },
        _m: Movable,
    });
}

fn move_objects(mut positions: Write<MovablePositions>) {
    for Position { x, y } in positions.iter_mut().flatten() {
        *x += 10;
        *y += 10;
    }
}

fn print_positions(positions: Read<AllPositions>) {
    for container in positions.iter() {
        for Position { x, y } in container.iter() {
            println!("{}: ({x}, {y})", container.entity_name().unwrap());
        }
    }
}

Run it:

cargo run

What this program does:

  • Position is a component: plain data that can be attached to an entity.
  • Object and MovableObject are entity types: named bundles of components.
  • Movable is a marker component used to select only movable entities.
  • filter! creates reusable queries for systems.
  • Read<AllPositions> reads matching components.
  • Write<MovablePositions> mutates matching components.
  • EntWrite<(Object, MovableObject)> creates entities.

For a slower walkthrough of these Rust and ECS terms, see Getting Started guide.

Declaring Access

Parallel execution depends on explicit access declarations. Reads may run together, while writes are exclusive.

fn read_components(v: Read<A>) { /* ... */ }
fn write_components(v: Write<B>) { /* ... */ }
fn read_resource(v: ResRead<C>) { /* ... */ }
fn write_resource(v: ResWrite<D>) { /* ... */ }
fn write_entity(v: EntWrite<E>) { /* ... */ }

Because systems state their access up front, my-ecs can detect dependency conflicts and schedule safe execution automatically.

Examples In This Crate

  • examples/hello_world.rs: the smallest complete program for first-time users.
  • examples/parallel.rs: using rayon with ECS-aware parallel iteration.
  • examples/async.rs: posting futures and applying their results back into the ECS world.
  • examples/async_io.rs: separating async I/O work from compute-heavy work by worker group.
  • demo/: a browser demo that renders the Mandelbrot set with my-ecs.

Prelude

Most applications can start with:

use my_ecs::prelude::*;

The prelude re-exports the main ECS API, derive macros, helper macros, and rayon::prelude::*.

About

Parallel and Asynchronous ECS library with wasm worker support

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE.txt
MIT
LICENSE-MIT.txt

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages