zig-ecs

repository·master·Indexed 19 days ago

https://github.com/prime31/zig-ecs

A high-performance Entity Component System for Zig, ported from the C++ library Entt. It utilizes Zig's generics and compile-time metaprogramming to provide entity querying and iteration via Views, Groups, and OwningGroups.

Tokens
1K
Snippets
2
Records
3
Agent score
15%

What's inside zig-ecs

  1. How Views and Groups work in Zig ECS

    master

    Zig ECS provides two primary ways to query and iterate over entities: Views and Groups.

    • Views: These store no data within the ECS itself. They iterate over entities on the fly without a cache. They are the recommended starting point for most queries due to their simplicity and low overhead. You can always upgrade to a Group if performance becomes a bottleneck.
    • Groups: These maintain a cached list of all entities that match a specific query. This cache speeds up iteration because the ECS already knows exactly which entities match, but it incurs a cost in memory and CPU cycles to keep the cache synchronized as entities are added or removed.
    • OwningGroups: A specialized type of Group where the component storage containers for the specified components are constantly reordered to ensure there are no gaps in the data. This allows for extremely fast, direct iteration. While memory usage is low, it is more CPU-intensive when there is high 'churn' (frequent adding or removing of the owned components).
  2. Iterate over entities using a Group with `each`

    master

    A Group provides faster iteration than a View by caching matching entities. You can use the .each() method to pass a function (or anonymous function) that operates on the components. The function signature for each uses a struct where each field is a pointer to a component type.

    // Setup components and registry
    pub const Velocity = struct { x: f32, y: f32 };
    pub const Position = struct { x: f32, y: f32 };
    var reg = ecs.Registry.init(std.testing.allocator);
    
    // Create entity and add components
    const entity = reg.create();
    reg.add(entity, Position{ .x = 0, .y = 0 });
    reg.add(entity, Velocity{ .x = 5, .y = 7 });
    
    // Create an owning Group and iterate with `each`
    var group = reg.group(.{ Velocity, Position }, .{}, .{});
    group.each(each);
    
    fn each(e: struct { vel: *Velocity, pos: *Position }) void {
        e.pos.*.x += e.vel.x;
        e.pos.*.y += e.vel.y;
    }
  3. Iterate over entities using a View

    master

    A View is used to find entities that possess a specific set of components. You create a view from a Registry, then use an entityIterator() to loop through the matching entities. Inside the loop, you can retrieve components using getConst (for a read-only copy) or get (for mutable access).

    // Setup components and registry
    pub const Velocity = struct { x: f32, y: f32 };
    pub const Position = struct { x: f32, y: f32 };
    var reg = ecs.Registry.init(std.testing.allocator);
    
    // Create entity and add components
    const entity = reg.create();
    reg.add(entity, Position{ .x = 0, .y = 0 });
    reg.add(entity, Velocity{ .x = 5, .y = 7 });
    
    // Create and iterate a View
    var view = reg.view(.{ Velocity, Position }, .{});
    var iter = view.entityIterator();
    while (iter.next()) |entity| {
        const pos = view.getConst(Position, entity); // readonly copy
        var vel = view.get(Velocity, entity);        // mutable
    }