From 7c9c45845cfaeae56f0112a2c4eb151571385d9f Mon Sep 17 00:00:00 2001 From: quou Date: Fri, 28 Apr 2023 20:45:18 +1000 Subject: Add a generic ECS. --- ecs2.cpp | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 ecs2.cpp diff --git a/ecs2.cpp b/ecs2.cpp new file mode 100644 index 0000000..dfc992b --- /dev/null +++ b/ecs2.cpp @@ -0,0 +1,212 @@ +#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 */ + +#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(); +} -- cgit v1.2.3-54-g00ecf