archetype_ecs 1.1.1

Archetype ECS - High-performance Entity Component System with parallel execution
Documentation

Archetype ECS

A high-performance Entity Component System (ECS) library for Rust.

License

Installation

[dependencies]

archetype_ecs = "1.1.1"

Quick Start

use archetype_ecs::prelude::*;

// Components are plain structs
struct Position { x: f32, y: f32 }
struct Velocity { dx: f32, dy: f32 }

fn main() -> Result<()> {
    let mut world = World::new();
    
    // Spawn entities
    for i in 0..1000 {
        world.spawn((
            Position { x: i as f32, y: 0.0 },
            Velocity { dx: 1.0, dy: 0.0 },
        ))?;
    }
    
    // Query and update components
    for (pos, vel) in world.query_mut::<(&mut Position, &Velocity)>() {
        pos.x += vel.dx;
        pos.y += vel.dy;
    }
    
    Ok(())
}

Querying Entities

Basic Queries

// Mutable query - use .iter() or IntoIterator
let mut query = world.query_mut::<(&mut Position, &Velocity)>();
for (pos, vel) in query.iter() {
    pos.x += vel.dx;
}

// Or directly (IntoIterator)
for (pos, vel) in world.query_mut::<(&mut Position, &Velocity)>() {
    pos.x += vel.dx;
}

Getting Entity IDs

Use the Entity marker to get entity IDs during iteration:

use archetype_ecs::prelude::*;

let mut to_delete = Vec::new();

for (entity, health) in world.query_mut::<(Entity, &Health)>() {
    if health.current <= 0.0 {
        to_delete.push(entity);  // Track entity for deletion
    }
}

// Delete entities after iteration
for entity in to_delete {
    world.despawn(entity)?;
}

Mixed Mutability

Read some components while writing others:

// Read Position, write Velocity
for (pos, vel) in world.query_mut::<(&Position, &mut Velocity)>() {
    vel.dx = -pos.x * 0.1;  // Use pos to calculate new velocity
}

Direct Component Access

Access components on a specific entity:

// Immutable access
if let Some(pos) = world.get_component::<Position>(entity) {
    println!("Position: ({}, {})", pos.x, pos.y);
}

// Mutable access
if let Some(pos) = world.get_component_mut::<Position>(entity) {
    pos.x += 10.0;
}

Resources (Global State)

Resources are typed singletons for global state:

struct GameTime { delta: f32, elapsed: f32 }

// Insert resource
world.insert_resource(GameTime { delta: 0.016, elapsed: 0.0 });

// Read resource
if let Some(time) = world.resource::<GameTime>() {
    println!("Elapsed: {}", time.elapsed);
}

// Mutate resource
if let Some(time) = world.resource_mut::<GameTime>() {
    time.elapsed += time.delta;
}

Query Filters

Filter entities by component presence:

use archetype_ecs::query::{With, Without, Changed};

// Only entities WITH a Visible component
let query = Query::<&Position, With<Visible>>::new(&world);
for pos in query.iter() { /* ... */ }

// Only entities WITHOUT a Dead component
let query = Query::<&Position, Without<Dead>>::new(&world);

// Only entities where Position changed this frame
let query = Query::<&Position, Changed<Position>>::new(&world);

Systems & Scheduling

use archetype_ecs::{System, SystemAccess};
use std::any::TypeId;

struct PhysicsSystem;

impl System for PhysicsSystem {
    fn name(&self) -> &'static str { "PhysicsSystem" }
    
    fn access(&self) -> SystemAccess {
        let mut access = SystemAccess::empty();
        access.writes.push(TypeId::of::<Position>());
        access.reads.push(TypeId::of::<Velocity>());
        access
    }
    
    fn run(&mut self, world: &mut World) -> Result<()> {
        for (pos, vel) in world.query_mut::<(&mut Position, &Velocity)>() {
            pos.x += vel.dx;
            pos.y += vel.dy;
        }
        Ok(())
    }
}

Parallel Execution

use archetype_ecs::parallel::ParallelExecutor;

let systems: Vec<Box<dyn System>> = vec![
    Box::new(PhysicsSystem),
    Box::new(RenderSystem),
];

let mut executor = ParallelExecutor::new(systems);
executor.execute_parallel(&mut world)?;

SIMD & Chunk Processing

Process entities in parallel with SIMD-friendly access:

let mut query = world.query_mut::<&mut Position>();
query.par_for_each_chunk(|mut chunk| {
    if let Some(positions) = chunk.get_slice_mut::<Position>() {
        for pos in positions.iter_mut() {
            pos.x += 1.0;
            pos.y += 1.0;
        }
    }
});

Batch Operations

// Spawn many entities efficiently
let entities = world.spawn_batch((0..1000).map(|i| {
    (Position { x: i as f32, y: 0.0 }, Velocity { dx: 1.0, dy: 0.0 })
}))?;

Performance

Operation Time Scale
Query Iteration 11.1 µs 10,000 entities
Entity Spawn 42.3 µs 1,000 entities
Parallel Execution 3.1 ms Multi-core

License

Copyright 2024 Saptak Santra. Licensed under Apache-2.0.