siecs 0.1.2

Rust bindings for SIECS
Documentation

SIECS Rust bindings

Rust bindings for SIECS, a C entity component system.

The crate exposes safe wrappers for the common ECS workflow and keeps a raw module available when a feature is not wrapped yet. The API is experimental and may change before the first stable release.

Install

From crates.io:

[dependencies]
siecs = "0.1.1"

From this repository:

[dependencies]
siecs = { path = "bindings/siecs_rust" }

Quick Start

use siecs::prelude::*;

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

#[derive(Component)]
struct Velocity {
    x: f32,
    y: f32,
}

fn move_system(mut query: Query<(&mut Position, &Velocity)>) {
    for (position, velocity) in &mut query {
        position.x += velocity.x;
        position.y += velocity.y;
    }
}

fn main() {
    let mut world = World::new();
    let entity = world.entity();

    world.set(entity, Position { x: 0.0, y: 0.0 });
    world.set(entity, Velocity { x: 1.0, y: 2.0 });

    world.system(Phase::OnUpdate, move_system);
    world.progress();
}

Components And Entities

Derive Component for Rust data types and store them on entities:

use siecs::prelude::*;

#[derive(Component)]
struct Health {
    value: i32,
}

let mut world = World::new();
let entity = world.entity();

world.set(entity, Health { value: 100 });
assert_eq!(world.get::<Health>(entity).unwrap().value, 100);
world.remove::<Health>(entity);
assert!(!world.has::<Health>(entity));

Queries

Queries are typed over the data they return. Use With<T> and Without<T> for filter terms that should not be returned as fields:

use siecs::prelude::*;

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

#[derive(Component)]
struct Velocity {
    x: f32,
    y: f32,
}

#[derive(Component)]
struct Player;

let mut world = World::new();
let mut query = world.query_filtered::<(&mut Position, &Velocity), With<Player>>();
for (position, velocity) in &mut query {
    position.x += velocity.x;
    position.y += velocity.y;
}

Systems

Systems run when the world progresses. System functions can request queries, resources, and commands:

use siecs::prelude::*;

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

#[derive(Component)]
struct Velocity {
    x: f32,
    y: f32,
}

#[derive(Resource)]
struct DeltaTime(f32);

fn move_system(mut query: Query<(&mut Position, &Velocity)>, time: Res<DeltaTime>) {
    for (position, velocity) in &mut query {
        position.x += velocity.x * time.0;
        position.y += velocity.y * time.0;
    }
}

let mut world = World::new();
world.set_resource(DeltaTime(0.016));
world.system(Phase::OnUpdate, move_system);
world.progress();

Observers And Events

Observers react to lifecycle events or custom events:

use siecs::prelude::*;

#[derive(Component)]
struct Health {
    value: i32,
}

#[derive(Event)]
struct Damage {
    amount: i32,
}

fn on_damage(damage: &Damage, health: &mut Health) {
    health.value -= damage.amount;
}

let mut world = World::new();
world.observe::<Damage>().each(on_damage);

let entity = world.entity();
world.set(entity, Health { value: 10 });
world.trigger(entity, Damage { amount: 3 });
assert_eq!(world.get::<Health>(entity).unwrap().value, 7);

Raw FFI

Use siecs::raw only when the safe wrapper does not expose a feature yet. Raw functions follow the C API and keep the same pointer and same-world safety requirements.