diff options
author | quou <quou@disroot.org> | 2023-04-28 20:45:18 +1000 |
---|---|---|
committer | quou <quou@disroot.org> | 2023-04-28 20:45:18 +1000 |
commit | 7c9c45845cfaeae56f0112a2c4eb151571385d9f (patch) | |
tree | 770bdafbd3ab3e4ce39dd216b218d4a05844dbbf | |
parent | 18f55a34369f58c551ccdc996b25d9488e85d945 (diff) |
Add a generic ECS.
-rw-r--r-- | ecs2.cpp | 212 |
1 files changed, 212 insertions, 0 deletions
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 <utility> + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +/* 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 <typename T> +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 <typename T> + 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 <typename T> + void register_component_type() { + pools[get_component_id<T>()].init(sizeof(T)); + } + + Entity create_entity() { + auto e = entity_count++; + bitset[e] = 0; + return e; + } + + template <typename T> + T& add_component(Entity e, T&& c) { + unsigned id = get_component_id<T>(); + Component_Pool& pool = pools[id]; + T& el = pool.get<T>(e); + bitset[e] |= (1 << id); + el = std::move(c); + return el; + } + + template <typename T> + void remove_component(Entity e) { + unsigned id = get_component_id<T>(); + bitset[e] &= ~(1 << id); + } + + template <typename T> + T& get_component(Entity e) { + unsigned id = get_component_id<T>(); + Component_Pool& pool = pools[id]; + return pool.get<T>(e); + } + + template <typename Fn> + void iterate_with_mask(unsigned mask, Fn fn) { + for (int i = 0; i < entity_count; i++) { + if (bitset[i] & mask) { + fn(i); + } + } + } + + template <typename A, typename Fn> + void iterate_with(Fn fn) { + unsigned mask_a = 1 << get_component_id<A>(); + 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 <typename A, typename B, typename Fn> + void iterate_with(Fn fn) { + unsigned mask_a = 1 << get_component_id<A>(); + unsigned mask_b = 1 << get_component_id<B>(); + 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<Transform>([&](Entity e) { + Transform& t = world.get_component<Transform>(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<Transform, Enemy>([&](Entity e) { + Transform& t = world.get_component<Transform>(e); + Enemy& enemy = world.get_component<Enemy>(e); + + printf("Updating enemy for %d\n", e); + printf("The health is: %d\n", enemy.health); + }); +} + +void sprite_system(World& world) { + world.iterate_with<Transform, Sprite>([&](Entity e) { + Transform& t = world.get_component<Transform>(e); + Sprite& sprite = world.get_component<Sprite>(e); + + printf("Updating sprite for %d\n", e); + printf("The id is: %d\n", sprite.id); + }); +} + +int main() { + World world; + world.register_component_type<Transform>(); + world.register_component_type<Enemy>(); + world.register_component_type<Sprite>(); + + 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(); +} |