#include #include /* 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); }