Skip to content

linuxy/coyote-ecs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

coyote-ecs

A fast and simple zig native ECS.

Builds against zig 0.14.0

git clone --recursive https://github.com/linuxy/coyote-ecs.git

To build:

  • zig build

A more complete example: coyote-snake

Benchmark: coyote-bunnies

Define your components in a container

const std = @import("std");
const ecs = @import("coyote-ecs");

const World = ecs.World;
const Cast = ecs.Cast;
const Systems = ecs.Systems;

//Container name is configured in ECS constants
pub const Components = struct {
    pub const Apple = struct {
        color: u32 = 0,
        ripe: bool = false,
        harvested: bool = false,
    };

    pub const Orange = struct {
        color: u32 = 0,
        ripe: bool = false,
        harvested: bool = false,
    };

    pub const Pear = struct {
        color: u32 = 0,
        ripe: bool = false,
        harvested: bool = false,
    };
};

Create some entities and components in a world

pub fn main() !void {
    //Create a world
    var world = try World.create();
    defer world.deinit();
    
    //Create an entity
    var anOrange = try world.entities.create();
    var anApple = try world.entities.create();
    var aPear = try world.entities.create();

    std.log.info("Created an Orange ID: {}", .{anOrange.id});

    //Create a unique component
    var orangeComponent = try world.components.create(Components.Orange);
    var appleComponent = try world.components.create(Components.Apple);

    //Attach and assign a component. Do not use an anonymous struct.
    try anOrange.attach(orangeComponent, Components.Orange{.color = 0, .ripe = false, .harvested = false});
    try anApple.attach(appleComponent, Components.Apple{.color = 0, .ripe = false, .harvested = false});
    _ = try aPear.addComponent(Components.Pear{.color = 1, .ripe = false, .harvested = false});

    //Create 50k entities and attach 50k unique components
    var i: usize = 0;
    while(i < 50000) : (i += 1) {
        var anEntity = try world.entities.create();
        var anOrangeComponent = try world.components.create(Components.Orange);
        try anEntity.attach(anOrangeComponent, Components.Orange{.color = 1, .ripe = false, .harvested = false});
    }

    //Filter components by type
    var it = world.components.iteratorFilter(Components.Orange);
    i = 0;
    while(it.next()) |_| : (i += 1) {
        //...
    }

    std.log.info("Orange components: {}", .{i});

    //Filter entities by type
    var it2 = world.entities.iteratorFilter(Components.Apple);
    i = 0;
    while(it2.next()) |_| : (i += 1) {
        //...
    }

    std.log.info("Apple entities: {}", .{i});

    if(aPear.getOneComponent(Components.Pear) != null)
        std.log.info("Pear entities: >= 1", .{})
    else
        std.log.info("Pear entities: 0", .{});

    try Systems.run(Grow, .{world});
    try Systems.run(Harvest, .{world});
    try Systems.run(Raze, .{world});

    std.log.info("Entities: {}", .{world.entities.count()});
    std.log.info("Components: {}", .{world.components.count()});
}

Create some systems

pub fn Grow(world: *World) void {
    var it = world.components.iterator();
    var i: u32 = 0;
    while(it.next()) |component| : (i += 1) {
        if(component.is(Components.Orange)) {
            try component.set(Components.Orange, .{.ripe = true});
        }

        if(component.is(Components.Apple)) {
            try component.set(Components.Apple, .{.ripe = true});
        }

        if(component.is(Components.Pear)) {
            try component.set(Components.Pear, .{.ripe = true});
        }
        //Fruits fall from the tree
        component.detach();
    }
    std.log.info("Fruits grown: {}", .{i});
}

pub fn Harvest(world: *World) void {
    var it = world.components.iterator();
    var i: u32 = 0;
    while(it.next()) |component| {
        if(component.is(Components.Orange)) {
            if(Cast(Components.Orange, component).ripe == true) {
                try component.set(Components.Orange, .{.harvested = true});
                i += 1;
            }
        }
        if(component.is(Components.Apple)) {
            if(Cast(Components.Apple, component).ripe == true) {
                try component.set(Components.Apple, .{.harvested = true});
                i += 1;
            }
        }
        if(component.is(Components.Pear)) {
            if(Cast(Components.Pear, component).ripe == true) {
                try component.set(Components.Pear, .{.harvested = true});
                i += 1;
            }
        }
        component.destroy();
    }
    
    world.components.gc();
    std.log.info("Fruits harvested: {}", .{i});
}

pub fn Raze(world: *World) void {
    var it = world.entities.iterator();
    var i: u32 = 0;

    while(it.next()) |entity| {
        entity.destroy();
        i += 1;
    }

    std.log.info("Entities destroyed: {}", .{i});
}

With C bindings

#include <stddef.h>
#include <stdio.h>
#include "../include/coyote.h"

typedef struct apple {
    int color;
    int ripe;
    int harvested;
} apple;

typedef struct orange {
    int color;
    int ripe;
    int harvested;
} orange;

typedef struct pear {
    int color;
    int ripe;
    int harvested;
} pear;

static const coyote_type t_apple = COYOTE_MAKE_TYPE(0, apple);
static const coyote_type t_orange = COYOTE_MAKE_TYPE(1, orange);
static const coyote_type t_pear = COYOTE_MAKE_TYPE(2, pear);

int main(void) {
    world world = coyote_world_create();

    if(world != 0)
        printf("Created world @%d\n", world);
    else
        printf("World creation failed.\n");

    entity e_apple = coyote_entity_create(world);
    entity e_orange = coyote_entity_create(world);
    entity e_pear = coyote_entity_create(world);

    component c_apple = coyote_component_create(world, t_apple);
    component c_orange = coyote_component_create(world, t_orange);
    component c_pear = coyote_component_create(world, t_pear);
 
    printf("Created an apple component @%d\n", c_apple);
    printf("Created an orange component @%d\n", c_orange);
    printf("Created an pear component @%d\n", c_pear);

    iterator it = coyote_components_iterator_filter(world, t_orange);
    component next = coyote_components_iterator_filter_next(it);
    if(next)
        printf("Another orange component @%d\n", c_orange);
    else
        printf("NOT another orange component @%d\n", c_orange);

    if(coyote_component_is(c_orange, t_orange))
        printf("Component is an orange @%d\n", c_orange);
    else
        printf("Component is NOT an orange @%d\n", c_orange);

    coyote_entity_attach(e_apple, c_apple, t_apple);

    //Assignment must happen after attach, TODO: Change?
    apple* a1 = coyote_component_get(c_apple); a1->color = 255; a1->ripe = 0; a1->harvested = 0;
    printf("Got and assigned an apple component @%d\n", a1);

    coyote_entity_detach(e_apple, c_apple);
    coyote_component_destroy(c_apple);
    coyote_entity_destroy(e_apple);
    coyote_entity_destroy(e_pear);

    printf("Number of entities: %d == 1\n", coyote_entities_count(world));
    printf("Number of components: %d == 3\n", coyote_components_count(world));

    coyote_world_destroy(world);
    printf("World destroyed.\n");
    return 0;
}

About

A fast and simple zig native ECS.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published