cheecs 0.1.0

A small, sparse-set-based Entity Component System with zero-boilerplate components.
Documentation
  • Coverage
  • 84%
    21 out of 25 items documented5 out of 15 items with examples
  • Size
  • Source code size: 40.6 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 412.9 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 1s Average build duration of successful builds.
  • all releases: 1s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • CHEAPFISS/cheecs
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • CHEAPFISS

cheecs

Crates.io Docs.rs License

A small, dependency-free Entity Component System for Rust, built on sparse-set storage.

cheecs favors simplicity over raw throughput: no archetypes, no scheduler, no macros required on your component types. Just entities, sparse sets, and queries.

Features

  • Zero boilerplate components — any 'static + Send + Sync type is automatically a Component. No #[derive(...)] needed.
  • Sparse-set storage — O(1) insert / remove / lookup per component type, contiguous dense arrays for cache-friendly iteration.
  • Generational entities — Entity { index, generation } so stale handles to despawned entities are safely rejected instead of aliasing new ones.
  • Tuple queries — query up to 16 component types at once as a plain tuple, e.g. world.query::<(&Position, &Velocity)>().
  • No unsafe code in the public API surface.

Installation

[dependencies]
cheecs = "0.1"

Quick example

use cheecs::prelude::*;

struct Position { x: f32, y: f32 }
struct Velocity { dx: f32, dy: f32 }

fn main() {
    let mut world = World::default();

    let player = world.spawn();
    world.add_component(player, Position { x: 0.0, y: 0.0 }).unwrap();
    world.add_component(player, Velocity { dx: 1.0, dy: 0.5 }).unwrap();

    // A second entity with only a Position — it will be skipped by the query below.
    let decoration = world.spawn();
    world.add_component(decoration, Position { x: 5.0, y: 5.0 }).unwrap();

    for (pos, vel) in world.query::<(&Position, &Velocity)>() {
        println!("moving by ({}, {})", vel.dx, vel.dy);
        let _ = pos; // read-only access for now
    }

    world.kill(&decoration).unwrap();
    assert!(!world.is_alive(&decoration));
}

Why sparse sets (and not archetypes)?

Archetype-based ECS (like bevy_ecs or hecs) group entities by their exact component combination, which gives extremely fast iteration but makes adding/removing components an O(archetype size) move. cheecs instead keeps one sparse set per component type:

  • Adding/removing a component only touches that one sparse set — no data is moved between archetypes.
  • Iteration over a query starts from the smallest matching sparse set and filters down, which keeps things fast for common cases without needing to precompute archetype graphs.
  • The trade-off: iterating a query with many component types has more indirection than a columnar archetype scan. For most gamejam/hobby-project workloads this is a good trade.

Core API

Method What it does
World::spawn() Creates a new Entity
World::kill(&entity) Removes an entity and all of its components
World::is_alive(&entity) Checks the entity's generation is still current
World::add_component(entity, component) Attaches (or overwrites) a component
World::remove_component::<T>(entity) Removes a component of type T
World::query::<Q>() Iterates entities matching a tuple of &Component types

Status

cheecs is a young, hobby-scale project — the API may still shift before 1.0. Feedback and issues are welcome.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.


Documentation and README drafted with AI assistance, reviewed and edited by the author.