From 9717b88e22e8649d8b2b28ed4cc1f5d61766be29 Mon Sep 17 00:00:00 2001 From: Carlos Sanchez Date: Sun, 18 Aug 2024 00:30:29 -0400 Subject: [PATCH] Playing around with ecs --- ecs.h | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ecs_comps.h | 61 +++++++++++++++++++++++++++ maze.c | 92 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 ecs.h create mode 100644 ecs_comps.h diff --git a/ecs.h b/ecs.h new file mode 100644 index 0000000..01cfe0e --- /dev/null +++ b/ecs.h @@ -0,0 +1,116 @@ +// A very simple library for constructing the basis of EXTREMELY simple ecs +// systems. Made mostly for fun + +#ifndef __HALOO3D_ECS_H +#define __HALOO3D_ECS_H + +#include +#include +#include + +#ifndef HECS_MAXENTITIES +#define HECS_MAXENTITIES 1024 +#endif + +typedef uint64_t hecs_cidt; +typedef int hecs_eidt; + +#define HECS_MAXCTYPES 63 // One minus the thing + +typedef struct { + hecs_cidt e_components[HECS_MAXENTITIES]; + void *components[HECS_MAXCTYPES]; + int numcomponents; + hecs_eidt entitytop; +} haloo_ecs; + +static void haloo_ecs_init(haloo_ecs *ecs) { + memset(ecs, 0, sizeof(haloo_ecs)); +} + +// Add an entity with given components to ecs system. +static hecs_eidt haloo_ecs_newentity(haloo_ecs *ecs, hecs_cidt basecomponents) { + for (int i = 0; i < HECS_MAXENTITIES; i++) { + hecs_eidt id = ecs->entitytop++; + if (ecs->e_components[id]) { + // Utilize the very last bit to indicate that the entity is active + ecs->e_components[id] = (1ULL << HECS_MAXCTYPES) | basecomponents; + return id; + } + } + return -1; +} + +static int haloo_ecs_match(haloo_ecs *ecs, hecs_eidt eid, hecs_cidt comps) { + hecs_cidt realcomps = (1ULL << HECS_MAXCTYPES) | comps; + return (ecs->e_components[eid] & realcomps) == realcomps; +} + +static void haloo_ecs_removeentity(haloo_ecs *ecs, int eid) { + if (eid >= 0 && eid < HECS_MAXENTITIES) + ecs->e_components[eid] = 0; +} + +// Add a new component to ecs. Create the type or storage with +// HECS_NEWCOMPONENT(type, name) +static int haloo_ecs_newcomponent(haloo_ecs *ecs, void *component) { + int id = ecs->numcomponents++; + ecs->components[id] = component; + return id; +} + +// Retrieve the component list for the given component id +// static void *haloo_ecs_getcomponents(haloo_ecs *ecs, int cid) { +// // Let the user fail in spectacular ways if cid is bad +// return ecs->components[cid]; +// } + +// static void * haloo_ecs_entitycomponent(haloo_ecs *ecs, int eid, int cid) { +// // Let the user fail in spectacular ways if cid is bad +// return haloo_ecs_getcomponents(ecs, cid) +// } + +// Convert component id into flag for use with system identification +#define HECS_CIDF(id) (1ULL << id) +// Create the storage for a new component within the entity system. Will +// create a variable within the given scope with the given name. DOES +// NOT MALLOC +#define HECS_NEWCOMPONENT(type, name) type name[HECS_MAXENTITIES]; +// Shortcut to both create the storage and add it to the given ecs. +// Automatically stores the name as __type. Stores the id for the component in +// type_id. For convenience, another variable of type_f is created for the flag +// version of the component id +#define HECS_ADDNEWCOMPONENT(type, ecs) \ + HECS_NEWCOMPONENT(type, __##type); \ + hecs_cidt type##_id = haloo_ecs_newcomponent(ecs, __##type); \ + hecs_cidt type##_f = HECS_CIDF(type##_id); +// Set component values on a given entity, also sets the flag indicating this +// component is now registered with the given entity. You should follow this +// statement immediately with the values for your component. +// Example: HECS_SETCOMPONENT(mytype, &ecs, playerid) { .x = 1, .y = 2 } +#define HECS_SETCOMPONENT(type, ecs, eid) \ + (ecs)->e_components[eid] |= type##_f; \ + __##type[eid] = (type) +//__ecs_cpos[playerid] = (ecs_cpos){.x = 1, .y = 1}; +// Retrieve a component for a single entity from the entity system, +// casting it to the given type +#define HECS_ENTITYCOMPONENT(type, eid, cid, ecs) \ + (((type)ecs->components[cid]) + eid) +// ecs.compenents[ecs.numcomponents++] = __ecs__type; + +// Run a function against ecs using the given limiting components. Note that +// components MUST be passed in the EXACT order the function expects them! +#define HECS_RUNSYS(ecs, eid, func, ...) \ + { \ + hecs_cidt _hecstemp[] = {__VA_ARGS__}; \ + hecs_cidt _hecsflags = 0; \ + for (int _hecsi = 0; _hecsi < sizeof(_hecstemp) / sizeof(hecs_cidt); \ + _hecsi++) { \ + _hecsflags |= _hecstemp[_hecsi]; \ + } \ + if (haloo_ecs_match(ecs, eid, _hecsflags)) { \ + func(ecs, eid, __VA_ARGS__); \ + } \ + } + +#endif diff --git a/ecs_comps.h b/ecs_comps.h new file mode 100644 index 0000000..442ed2f --- /dev/null +++ b/ecs_comps.h @@ -0,0 +1,61 @@ +// A bunch of component definitions that might not +// be useful in general, but who knows? + +#ifndef __HALOO3D_ECSCOMP_H +#define __HALOO3D_ECSCOMP_H + +#include "ecs.h" +#include "haloo3d/haloo3d.h" + +// A component that simply points back to an object instance. If combined +// with movement, will pull pos from objin at start of frame, then write +// pos back to obj at end of frame. +typedef haloo3d_obj_instance *ecs_objin; + +// Like objin, this component simply points to a camera. It will set the +// position AND rotation at the start of the frame, and pull the pos/ +// rotation back into the camera at the end. +typedef haloo3d_camera *ecs_camera; + +typedef struct { + struct vec3 pos; + struct vec3 dst; + int timer; +} ecs_moveto; + +typedef struct { + struct vec2 rot; + struct vec2 dstrot; + int timer; +} ecs_rotateto; + +// Move object position into moveto +static void sys_ecs_objin_moveto(haloo_ecs *ecs, hecs_eidt id, hecs_cidt oiid, + hecs_cidt mtid) { + ecs_moveto *mt = HECS_ENTITYCOMPONENT(ecs_moveto *, id, mtid, ecs); + ecs_objin *oi = HECS_ENTITYCOMPONENT(ecs_objin *, id, oiid, ecs); + mt->pos = (*oi)->pos; +} + +static void sys_ecs_moveto(haloo_ecs *ecs, hecs_eidt id, hecs_cidt mtid) { + ecs_moveto *mt = HECS_ENTITYCOMPONENT(ecs_moveto *, id, mtid, ecs); + if (mt->timer <= 0) { + mt->pos = mt->dst; + return; + } + mfloat_t xdiff = mt->dst.x - mt->pos.x; + mfloat_t ydiff = mt->dst.y - mt->pos.y; + mt->pos.x = xdiff / mt->timer; + mt->pos.y = ydiff / mt->timer; + mt->timer--; +} + +// Move movement pos back into object +static void sys_ecs_moveto_objin(haloo_ecs *ecs, hecs_eidt id, hecs_cidt mtid, + hecs_cidt oiid) { + ecs_moveto *mt = HECS_ENTITYCOMPONENT(ecs_moveto *, id, mtid, ecs); + ecs_objin *oi = HECS_ENTITYCOMPONENT(ecs_objin *, id, oiid, ecs); + (*oi)->pos = mt->pos; +} + +#endif diff --git a/maze.c b/maze.c index f948080..0c9c1b2 100644 --- a/maze.c +++ b/maze.c @@ -9,6 +9,8 @@ #include "unigi/unigi.headers/src/main.h" #include "unigi/unigi.platform.sdl1/src/main.c" +#include "ecs.h" +#include "ecs_comps.h" #include "keys.h" #include @@ -16,10 +18,10 @@ // INteresting flags for performance comparisons #define FASTFILL -#define WIDTH 320 -#define HEIGHT 200 +#define WIDTH 640 +#define HEIGHT 400 #define ASPECT ((float)WIDTH / HEIGHT) -#define SCREENSCALE 4 +#define SCREENSCALE 1 #define SWIDTH (WIDTH * SCREENSCALE) #define SHEIGHT (HEIGHT * SCREENSCALE) #define NEARCLIP 0.01 @@ -38,6 +40,55 @@ #define STACKPUSH(s, t, v) s[t++] = v; +// everything in the maze is controlled by the CPU. As such, movement +// is as simple as "go here by this time". No need to complicate the +// components? +// Allows movement in 3 dimensions +// typedef struct { +// struct vec3 pos; +// struct vec3 vel; +// } ecs_movement; + +// Allows rotational movement only with yaw and pitch (in that order) +// typedef struct { +// struct vec2 rot; +// struct vec2 rotvel; +// } ecs_rotmovement; + +// A general position within the maze, and a pointer to the maze itself. +// Can be used to traverse the maze +typedef struct { + uint8_t *maze; + int size; + struct vec2i pos; // tile position within maze + struct vec2i dir; // facing dir +} ecs_maze; + +// A component which can track progress towards some destination. Each +// frame it is expected that the timer will decrease, and that on 0, +// the destination should be reached +// typedef struct { +// int timer; +// struct vec3 dst; +// } ecs_destination; + +// // A component that simply points back to an object instance. If combined +// // with movement, will pull pos from objin at start of frame, then write +// // pos back to obj at end of frame. +// typedef haloo3d_obj_instance *ecs_objin; +// +// // Like objin, this component simply points to a camera. It will set the +// // position AND rotation at the start of the frame, and pull the pos/ +// // rotation back into the camera at the end. +// typedef haloo3d_camera *ecs_camera; + +// typedef struct vec2i ecs_cpos; +// typedef struct vec2i ecs_npos; +// typedef struct vec2i ecs_cdir; +// typedef struct vec2i ecs_ndir; +// typedef haloo3d_obj_instance *ecs_objin; +// typedef haloo3d_camera *ecs_cam; + int maze_visited(uint8_t *maze, int x, int y, int size) { return (maze[x + y * size] & MAZEVISIT) > 0; } @@ -273,6 +324,25 @@ int main() { // int argc, char **argv) { haloo3d_debugconsole_set(&dc, "obj/ceil/pos_y.f", &ceili->pos.y); + // Set up the various systems + haloo_ecs ecs; + haloo_ecs_init(&ecs); + + HECS_ADDNEWCOMPONENT(ecs_moveto, &ecs); + HECS_ADDNEWCOMPONENT(ecs_rotateto, &ecs); + HECS_ADDNEWCOMPONENT(ecs_maze, &ecs); + HECS_ADDNEWCOMPONENT(ecs_objin, &ecs); + HECS_ADDNEWCOMPONENT(ecs_camera, &ecs); + + hecs_eidt playerid = haloo_ecs_newentity(&ecs, 0); + + HECS_SETCOMPONENT(ecs_moveto, &ecs, playerid){ + .pos = render.camera.pos, .dst = render.camera.pos, .timer = 0}; + HECS_SETCOMPONENT(ecs_camera, &ecs, playerid) & render.camera; + // HECS_SETCOMPONENT(ecs_cpos, &ecs, playerid){.x = 1, .y = 1}; + + // ecs_ndir_f); + // ----------------------------------- // Actual rendering // ----------------------------------- @@ -305,6 +375,22 @@ int main() { // int argc, char **argv) { } } + // --------------------------- + // Game logic? + // --------------------------- + + for (int i = 0; i < HECS_MAXENTITIES; i++) { + HECS_RUNSYS(&ecs, i, sys_ecs_objin_moveto, ecs_objin_f, ecs_moveto_f); + HECS_RUNSYS(&ecs, i, sys_ecs_moveto, ecs_moveto_f); + HECS_RUNSYS(&ecs, i, sys_ecs_moveto_objin, ecs_moveto_f, ecs_objin_f); + // if (haloo_ecs_match(&ecs, i, ecs_moveto_f | ecs_objin_f)) { + // sys_ecs_objin_moveto(&ecs, i, ecs_objin_f, ecs_moveto_f); + // } + // if (haloo_ecs_match(&ecs, i, ecs_moveto_f)) { + // sys_ecs_moveto(&ecs, i, ecs_moveto_f); + // } + } + totaldrawn = 0; haloo3d_obj_instance *object = NULL;