#include #include #include #include /* So it's like the other ECS example with a bunch of academic masturbation * See https://git.quou.xyz/samples/tree/ecs.c * * Areas for improvement: * - Should really respect alignment. * - Entity destruction */ #define max_entities 256 /* If you want more than 32 component types then you'll have to * use an unsigned long instead of an int for the component type * IDs. */ #define max_component_types 32 typedef int Entity; unsigned get_new_component_id(); /* DO NOT make either of these functions static. That will break stuff. */ template unsigned get_component_id() { static unsigned id = get_new_component_id(); return id; } unsigned get_new_component_id() { static unsigned id = 0; return id++; } struct Component_Pool { void* data; int element_size; template T& get(int idx) { assert(sizeof(T) == element_size); return ((T*)data)[idx]; } Component_Pool(): data(0) {} void init(int es) { element_size = es; data = malloc(element_size * max_entities); } void deinit() { free(data); } bool isinit() { return data != nullptr; } }; struct World { Component_Pool pools[max_component_types]; int entity_count; int bitset[max_entities]; World(): entity_count(0) {} void deinit() { for (int i = 0; i < max_component_types; i++) { if (pools[i].isinit()) { pools[i].deinit(); } } } template void register_component_type() { pools[get_component_id()].init(sizeof(T)); } Entity create_entity() { auto e = entity_count++; bitset[e] = 0; return e; } template T& add_component(Entity e, T&& c) { unsigned id = get_component_id(); Component_Pool& pool = pools[id]; T& el = pool.get(e); bitset[e] |= (1 << id); el = std::move(c); return el; } template void remove_component(Entity e) { unsigned id = get_component_id(); bitset[e] &= ~(1 << id); } template T& get_component(Entity e) { unsigned id = get_component_id(); Component_Pool& pool = pools[id]; return pool.get(e); } template void iterate_with_mask(unsigned mask, Fn fn) { for (int i = 0; i < entity_count; i++) { if (bitset[i] & mask) { fn(i); } } } template void iterate_with(Fn fn) { unsigned mask_a = 1 << get_component_id(); unsigned bits; for (int i = 0; i < entity_count; i++) { bits = bitset[i]; if (bits & mask_a) { fn(i); } } } /* You need to make an overload for every number of components that you want to * iterate at once. Just making two for example. * * There's probably a modern C++ way to not have to do this, but I don't know it. * Please tell me if you know. */ template void iterate_with(Fn fn) { unsigned mask_a = 1 << get_component_id(); unsigned mask_b = 1 << get_component_id(); unsigned bits; for (int i = 0; i < entity_count; i++) { bits = bitset[i]; if ((bits & mask_a) && (bits & mask_b)) { fn(i); } } } }; /* Example */ struct Transform { int x, y; int scale_x, scale_y; }; struct Enemy { int health, damage; }; struct Sprite { int id; }; void transform_system(World& world) { world.iterate_with([&](Entity e) { Transform& t = world.get_component(e); printf("Updating transform for %d\n", e); printf("The position is: %d, %d\n", t.x, t.y); }); } void enemy_system(World& world) { world.iterate_with([&](Entity e) { Transform& t = world.get_component(e); Enemy& enemy = world.get_component(e); printf("Updating enemy for %d\n", e); printf("The health is: %d\n", enemy.health); }); } void sprite_system(World& world) { world.iterate_with([&](Entity e) { Transform& t = world.get_component(e); Sprite& sprite = world.get_component(e); printf("Updating sprite for %d\n", e); printf("The id is: %d\n", sprite.id); }); } int main() { World world; world.register_component_type(); world.register_component_type(); world.register_component_type(); Entity a = world.create_entity(); world.add_component(a, Transform { 25, 25, 100, 100 }); world.add_component(a, Enemy { 10, 5 }); world.add_component(a, Sprite { 0 }); Entity b = world.create_entity(); world.add_component(b, Transform { 1, 3, 50, 4 }); world.add_component(b, Enemy { 5, 20 }); /* The game loop. */ while (true) { enemy_system(world); transform_system(world); sprite_system(world); } world.deinit(); }