aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorquou <quou@disroot.org>2023-04-28 20:45:18 +1000
committerquou <quou@disroot.org>2023-04-28 20:45:18 +1000
commit7c9c45845cfaeae56f0112a2c4eb151571385d9f (patch)
tree770bdafbd3ab3e4ce39dd216b218d4a05844dbbf
parent18f55a34369f58c551ccdc996b25d9488e85d945 (diff)
Add a generic ECS.
-rw-r--r--ecs2.cpp212
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();
+}