#ecs #entities #magma-ecs

magma_ecs

Entity-Component-System for the Magma3D game engine

16 releases

Uses new Rust 2024

new 0.3.0-beta Apr 30, 2025
0.2.0 Mar 3, 2025
0.2.0-beta.2 Nov 2, 2024
0.1.0-alpha.5 Jul 9, 2024
0.1.0-alpha.1 Nov 30, 2023

#150 in Game dev

Download history 7/week @ 2025-02-03 29/week @ 2025-02-10 14/week @ 2025-02-17 3/week @ 2025-02-24 430/week @ 2025-03-03 18/week @ 2025-03-10 10/week @ 2025-03-17 249/week @ 2025-04-07 23/week @ 2025-04-14 10/week @ 2025-04-21 125/week @ 2025-04-28

407 downloads per month
Used in 6 crates (2 directly)

MIT license

51KB
988 lines

Magma-ECS

Magma-ECS is the Entity-component-system developed for the Magma3D engine. It aims to be simple to use and lightweight. It also provides an easy way for parallel execution of systems.

Even though this is intended for the magma_api it is easily possible to use on it's own.

Features

  • Efficient component storage using roaring bitmaps.
  • Multithreading using parking_lot's RwLock and rayon's parallel iterators.
  • Easy and ergonimic querying.
  • Creating system dispatchers that allow for parallel execution of systems, with the possibility to depend on other systems.
  • Support for global resources.

Usage

Add the crate to your Cargo.toml:

[dependencies]
magma_ecs = "0.3.0-alpha"

Entity-Component-System

Entity: An entity is just an index into the component storage. Component: A component holds some type of data. Entities can have components assigned to them. System: A system is a piece of code (usually a function), that reads and modifies the data.

Another way to think about this would be Identifier-Data-Logic.

Example

use magma_ecs::World;

fn main() -> Result<(), Box<dyn Error>> {
    // Create a world.
    let mut world = World::new();

    // Register components in the world.
    world.register_component::<Position>();
    world.register_component::<Health>();
    world.register_component::<Speed>();

    // Add a global resource.
    world.add_resource(GlobalDirection(1.0, 0.0, 0.0))?;

    // create a couple of entities
    for z in 0..10 {
        world.create_entity((Position(0.0, 0.0, z as f32), Health(20), Speed(1.0)))?;
    }

    // Create a system dispatcher.
    let dispatcher = Systems::new()
        .with(update_position, "update_position", &[])
        .build_dispatcher();

    // Run the dispatcher.
    loop {
        dispatcher.dispatch(&world);
    }
}

// Components can be any type that is `Sync + Send`,
// or just any if you disabled the multithreading feature.
struct Position(f32, f32, f32);
struct Health(u32);
struct Speed(f32);

// The same applies to resources.
struct GlobalDirection(f32, f32, f32);

// Systems are ordinary functions, that take &World as a parameter.
fn update_position(world: &World) {
    let global_dir = world.get_resource::<GlobalDirection>().unwrap();

    world.query::<(Position, Speed)>().unwrap().iter().for_each(|entity| {
        let global_dir = world.get_resource::<GlobalDirection>().unwrap();
        let speed = entity.get_component::<Speed>().unwrap();
        let position = entity.get_component_mut::<Position>().unwrap();

        *position.0 += *global_dir.0 * *speed;
        *position.1 += *global_dir.1 * *speed;
        *position.2 += *global_dir.2 * *speed;
    });
}

Cargo Features

  • multithreading: Multithreading is enabled by default.
    • It also reexports rayon for your convinience.

Disclaimer

This is still in developement and not production ready.

Dependencies

~1–8MB
~40K SLoC