aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ecs.c153
1 files changed, 153 insertions, 0 deletions
diff --git a/ecs.c b/ecs.c
new file mode 100644
index 0000000..494a79f
--- /dev/null
+++ b/ecs.c
@@ -0,0 +1,153 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Very simple Entity Component System, created to help a
+ * Discord user understand the concept.
+ *
+ * I made it to be very easy to read and understand and
+ * as simple as possible while still having enough features
+ * to be usable for simple games.
+ *
+ * Areas for improvement:
+ * - Using an intermediate array (or "sparse set") instead
+ * of a bitset to avoid gaps in the component arrays.
+ * - Destroying entities - this can be done by simply
+ * removing all of the components in the current system,
+ * but you may want to have a way to re use-entity IDs.
+ * Hint: This will probably involve sacrificing some
+ * of the bits in the entity ID to store a "version"
+ * to check how many times the ID has been recycled.
+ * - Anything else you think you need :)
+ */
+
+#define max_entities 256
+#define max_component_types 8
+
+/* Entities are just IDs. */
+typedef int Entity;
+
+typedef enum {
+ ctype_null = 1 << 0,
+ ctype_transform = 1 << 1,
+ ctype_enemy = 1 << 2,
+ ctype_sprite = 1 << 3
+} CType;
+
+typedef struct {
+ int x, y;
+ int scale_x, scale_y;
+} Transform;
+
+typedef struct {
+ int health;
+ int damage;
+} Enemy;
+
+typedef struct {
+ int id;
+ /* Whatever else a sprite needs, you get the idea. */
+} Sprite;
+
+typedef struct {
+ /* Here are the component pools. */
+ Transform transforms[max_entities];
+ Enemy enemies[max_entities];
+ Sprite sprites[max_entities];
+
+ /* If you have more than 8 different types of component, change this
+ * to an unsigned short or an int to fit the extra bits in the CType
+ * enum. */
+ unsigned char bitset[max_entities];
+
+ int entity_count;
+} World;
+
+void enemy_system(World* world) {
+ int i;
+ unsigned char bits;
+ Transform* transform;
+ Enemy* enemy;
+
+ for (i = 0; i < world->entity_count; i++) {
+ bits = world->bitset[i];
+ /* Iterate all entities with both a transform and an enemy component. */
+ if ((bits & ctype_enemy) && (bits & ctype_transform)) {
+ transform = &world->transforms[i];
+ enemy = &world->enemies[i];
+
+ /* Do enemy stuff here, pathfinding, shooting etc. */
+ printf("Updating enemy for %d!\n", i);
+ }
+ }
+};
+
+void transform_system(World* world) {
+ int i;
+ unsigned char bits;
+ Transform* transform;
+
+ for (i = 0; i < world->entity_count; i++) {
+ bits = world->bitset[i];
+ /* Iterate all entities with a transform component */
+ if (bits & ctype_transform) {
+ transform = &world->transforms[i];
+ transform->x += 10;
+
+ /* Do transform stuff here, for example updating matrices */
+ printf("Updating transform for %d!\n", i);
+ }
+ }
+}
+
+void sprite_system(World* world) {
+ /* etc. */
+}
+
+void init_world(World* world) {
+ world->entity_count = 0;
+}
+
+Entity create_entity(World* world) {
+ /* Might want to verify that we don't overflow max_entities. */
+ Entity e;
+
+ e = world->entity_count++;
+
+ /* Make sure each entity starts with no components. */
+ world->bitset[e] = ctype_null;
+
+ return e;
+}
+
+void entity_add_components(World* world, Entity entity, unsigned int bits) {
+ /* Might want to verify that this entity is valid, etc. */
+ world->bitset[entity] |= bits;
+}
+
+void entity_remove_components(World* world, Entity entity, unsigned int bits) {
+ world->bitset[entity] &= ~bits;
+}
+
+int main() {
+ World* world;
+ Entity a, b;
+
+ world = (World*)malloc(sizeof *world);
+
+ init_world(world);
+
+ a = create_entity(world);
+ entity_add_components(world, a, ctype_transform | ctype_enemy);
+
+ b = create_entity(world);
+ entity_add_components(world, b, ctype_transform);
+
+ while (1) {
+ /* The game loop. */
+ enemy_system(world);
+ transform_system(world);
+ sprite_system(world);
+ }
+
+ free(world);
+}