Playing around with ecs

This commit is contained in:
Carlos Sanchez 2024-08-18 00:30:29 -04:00
parent e996e77c38
commit 9717b88e22
3 changed files with 266 additions and 3 deletions

116
ecs.h Normal file
View File

@ -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 <math.h>
#include <stdint.h>
#include <string.h>
#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

61
ecs_comps.h Normal file
View File

@ -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

92
maze.c
View File

@ -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 <stdlib.h>
@ -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;