From 9c745d2857986d3b9136f19bb025aa0b21794aa5 Mon Sep 17 00:00:00 2001 From: Klemen Plestenjak Date: Sun, 7 Jan 2024 13:49:13 +0100 Subject: [PATCH] Partial entity inspector --- engine/breeze/util/array.c | 13 ++++ engine/breeze/util/array.h | 2 + game/components.c | 123 ++++++++++++++++++++++++++++++++++--- game/components.h | 53 ++++++++++++---- game/game_state.h | 11 ++-- game/main.c | 96 +++++++++++++++++++++-------- game/systems/s_entity.c | 2 +- game/systems/s_input.c | 18 ++++++ game/systems/systems.c | 14 ++++- 9 files changed, 276 insertions(+), 56 deletions(-) diff --git a/engine/breeze/util/array.c b/engine/breeze/util/array.c index cd8319b..021c3d3 100644 --- a/engine/breeze/util/array.c +++ b/engine/breeze/util/array.c @@ -66,6 +66,19 @@ void _bzArrayDelN(void *arr, i32 idx, i32 n) { head->size -= n; } +void _bzArrayDelSwap(void *arr, i32 idx) { + BzArrayHead *head = ARRAY_HEAD(arr); + BZ_ASSERT(idx >= 0 && idx < head->size); + + i32 lastIdx = head->size - 1; + if (idx != lastIdx) { + bzMemMove((u8 *) arr + idx * head->stride, + (u8 *) arr + (lastIdx) * head->stride, + head->stride); + } + + head->size--; +} i32 _bzArrayPush(void *arr) { BzArrayHead *head = ARRAY_HEAD(arr); diff --git a/engine/breeze/util/array.h b/engine/breeze/util/array.h index 8694d67..2516286 100644 --- a/engine/breeze/util/array.h +++ b/engine/breeze/util/array.h @@ -15,6 +15,7 @@ void *_bzArrayEnsureCapacity(void **arr, i32 capacity); i32 _bzArrayGet(void *arr, i32 idx); void _bzArrayDelN(void *arr, i32 idx, i32 n); +void _bzArrayDelSwap(void *arr, i32 idx); i32 _bzArrayPush(void *arr); i32 _bzArrayIns(void *arr, i32 idx); @@ -37,6 +38,7 @@ i32 _bzArrayPop(void *arr); #define bzArrayGet(arr, idx) (arr)[_bzArrayGet(arr, idx)] #define bzArrayDel(arr, idx) _bzArrayDelN(arr, idx, 1) #define bzArrayDelN(arr, idx, n) _bzArrayDelN(arr, idx, n) +#define bzArrayDelSwap(arr, idx) _bzArrayDelSwap(arr, idx) #define bzArrayPush(arr, ...) \ do { \ diff --git a/game/components.c b/game/components.c index ba842aa..5ee5256 100644 --- a/game/components.c +++ b/game/components.c @@ -94,10 +94,8 @@ void igTagCheckbox(const char *label, ecs_world_t *ecs, else ecs_remove_id(ecs, entity, tag); } -void igResource(const char *label, ecs_world_t *ecs, +void igResource(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { - if (!ecs_has_id(ecs, entity, comp)) return; - igSeparatorText(label); Resource *res = ecs_get_mut_id(ecs, entity, comp); const char *resStrings[RES_COUNT]; for (i32 i = 0; i < RES_COUNT; i++) { @@ -108,13 +106,118 @@ void igResource(const char *label, ecs_world_t *ecs, res->type = curType; igInputInt("Amount", &res->amount, 1, 10, 0); } -void igVec2(const char *label, ecs_world_t *ecs, - ecs_entity_t entity, ecs_entity_t comp) { - if (!ecs_has_id(ecs, entity, comp)) return; - igSeparatorText(label); - Vector2 *vec = ecs_get_mut_id(ecs, entity, comp); - igPushID_Str(label); +void igTilePosition(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + TilePosition *tilePos = ecs_get_mut_id(ecs, entity, comp); + igInputInt("X", &tilePos->x, 1, 4, 0); + igInputInt("Y", &tilePos->y, 1, 4, 0); +} +void igTileSize(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + TileSize *tileSize = ecs_get_mut_id(ecs, entity, comp); + igInputInt("X", &tileSize->sizeX, 1, 1, 0); + igInputInt("Y", &tileSize->sizeY, 1, 1, 0); +} +void igOwner(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + Owner *owner = ecs_get_mut_id(ecs, entity, comp); + igInputInt("PlayerID", &owner->playerID, 0, 0, 0); +} + +void igSpatialGridID(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + SpatialGridID *id = ecs_get_mut_id(ecs, entity, comp); + igText("SpatialID", *id); +} +void igVec2(Vector2 *vec) { igInputFloat("X", &vec->x, 1.0f, 10.0f, "%.2f", 0); igInputFloat("Y", &vec->y, 1.0f, 10.0f, "%.2f", 0); - igPopID(); +} +void igVec2Comp(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + Vector2 *vec = ecs_get_mut_id(ecs, entity, comp); + igVec2(vec); +} +void igFloat(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + f32 *f = ecs_get_mut_id(ecs, entity, comp); + igInputFloat("", f, 0.1f, 1.0f, "%.2f", 0); +} + +void igPath(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + Path path = *(Path *) ecs_get_mut_id(ecs, entity, comp); + i32 idx = 0; + while (path.paths) { + for (int32_t i = path.curWaypoint; i < path.paths->numWaypoints; i++) { + igPushID_Int(idx); + igText("Waypoint %d:", idx); + igInputFloat("X", &path.paths->waypoints[i].x, 1, 16, "%.2f", 0); + igInputFloat("Y", &path.paths->waypoints[i].y, 1, 16, "%.2f", 0); + igPopID(); + idx++; + } + path.paths = path.paths->next; + path.curWaypoint = 0; + } +} + +void igRect(Rectangle *rect) { + igInputFloat("X", &rect->x, 1, 16, "%.2f", 0); + igInputFloat("Y", &rect->y, 1, 16, "%.2f", 0); + igInputFloat("Width", &rect->width, 1, 16, "%.2f", 0); + igInputFloat("Height", &rect->height, 1, 16, "%.2f", 0); +} +void igTextureRegion(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + TextureRegion *tex = ecs_get_mut_id(ecs, entity, comp); + igText("Texture: %d", tex->texture.id); + igRect(&tex->rec); + bool flipX = tex->flipX; + bool flipY = tex->flipY; + igCheckbox("flipX", &flipX); + igCheckbox("flipY", &flipY); + tex->flipX = flipX; + tex->flipY = flipY; +} + +void igAnimation(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + //Animation *anim = ecs_get_mut_id(ecs, entity, comp); + + +} +void igEasing(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + Easing *easing = ecs_get_mut_id(ecs, entity, comp); + igText("Component: %d, offset: %d", easing->compID, easing->offset); + igText("x = %.2f + %.2f * (%.2f + (%.2f * %.2f) + %.2f) + %.2f", + easing->start, easing->target, easing->easeStart, easing->easeTarget, + easing->x, easing->easeOffset, easing->offset); +} + +void igArms(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + +} +void igArm(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + +} + +void igUnitAction(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + +} +void igUnitAI(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + +} +void igWorker(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + +} +void igUnit(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp) { + } diff --git a/game/components.h b/game/components.h index 89159a3..acf51f9 100644 --- a/game/components.h +++ b/game/components.h @@ -46,7 +46,7 @@ typedef struct TileSize { extern ECS_COMPONENT_DECLARE(TileSize); typedef struct Owner { - BuildingType playerID; + int32_t playerID; } Owner; extern ECS_COMPONENT_DECLARE(Owner); @@ -133,12 +133,6 @@ typedef struct Easing { } Easing; extern ECS_COMPONENT_DECLARE(Easing); -typedef struct EntityArms { - ecs_entity_t left; - ecs_entity_t right; -} EntityArms; -//extern ECS_COMPONENT_DECLARE(EntityArms); - /********************************************************** * Event components *********************************************************/ @@ -153,8 +147,6 @@ typedef struct EntityArms { * Gameplay components *********************************************************/ -typedef Vector2 ItemOffset; - typedef struct WeaponMelee { ecs_entity_t weapon; f32 reach; @@ -219,8 +211,47 @@ void initComponentIDs(ecs_world_t *ecs); void igTagCheckbox(const char *label, ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t tag); -void igResource(const char *label, ecs_world_t *ecs, + +typedef void(*ImGuiCompFn)(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp); +void igResource(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp); -void igVec2(const char *label, ecs_world_t *ecs, +void igTilePosition(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); +void igTileSize(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); +void igOwner(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); + +void igSpatialGridID(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); +void igVec2Comp(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); +void igFloat(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp); + +void igPath(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); + +void igTextureRegion(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); + +void igAnimation(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); +void igEasing(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); + +void igArms(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); +void igArm(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); + +void igUnitAction(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); +void igUnitAI(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); +void igWorker(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); +void igUnit(ecs_world_t *ecs, + ecs_entity_t entity, ecs_entity_t comp); + #endif //PIXELDEFENSE_COMPONENTS_H diff --git a/game/game_state.h b/game/game_state.h index d91d95d..401c8b4 100644 --- a/game/game_state.h +++ b/game/game_state.h @@ -42,11 +42,12 @@ typedef struct Game { BzObjectPool *actions; } pools; struct { - bool path; - bool entityColliders; - bool mapColliders; - bool spatialGrid; - } debugDraw; + bool drawPath; + bool drawEntityColliders; + bool drawMapColliders; + bool drawSpatialGrid; + ecs_entity_t *inspecting; + } debug; f32 elapsed; ecs_query_t *drawQuery; diff --git a/game/main.c b/game/main.c index 035eb46..81110b1 100644 --- a/game/main.c +++ b/game/main.c @@ -233,9 +233,10 @@ bool init(void *userData) { loadMap(game, "assets/maps/main_menu_01.tmj"); - game->debugDraw.mapColliders = true; - game->debugDraw.spatialGrid = true; - game->debugDraw.path = true; + game->debug.drawMapColliders = true; + game->debug.drawSpatialGrid = true; + game->debug.drawPath = true; + game->debug.inspecting = bzArrayCreate(ecs_entity_t, 10); return true; } @@ -263,6 +264,8 @@ void deinit(void *userData) { input = &inputCopy; sounds = &soundsCopy; + bzArrayDestroy(game->debug.inspecting); + bzTilesetDestroy(&game->tileset); bzStackAllocDestroy(&game->stackAlloc); @@ -396,11 +399,11 @@ static void renderGame(Game *game, float dt) { } ecs_progress(ECS, dt); - ecs_enable(ECS, renderDebugPathSystem, game->debugDraw.path); - ecs_enable(ECS, renderCollidersSystem, game->debugDraw.entityColliders); - if (game->debugDraw.mapColliders) + ecs_enable(ECS, renderDebugPathSystem, game->debug.drawPath); + ecs_enable(ECS, renderCollidersSystem, game->debug.drawEntityColliders); + if (game->debug.drawMapColliders) bzTileMapDrawCollisions(&game->map); - if (game->debugDraw.spatialGrid) + if (game->debug.drawSpatialGrid) bzSpatialGridDrawDebugGrid(game->entityGrid); drawPlayerInputUI(); @@ -612,13 +615,32 @@ void render(float dt, void *userData) { } -static void igEntity(ecs_entity_t entity) { +void igInspectComp(const char *label, ecs_entity_t entity, ecs_entity_t comp, ImGuiCompFn fn) { + igPushID_Int(comp); + igSeparatorText(label); + bool isAttached = ecs_has_id(ECS, entity, comp); + igCheckbox("Attached", &isAttached); + + if (isAttached) + fn(ECS, entity, comp); + + if (isAttached != ecs_has_id(ECS, entity, comp)) { + if (!isAttached) { + ecs_remove_id(ECS, entity, comp); + } else { + ecs_set_id(ECS, entity, comp, 0, NULL); + } + } + + igPopID(); +} +void igInspectWindow(ecs_entity_t entity, bool *open) { + igSetNextWindowSize((ImVec2) {300, 440}, ImGuiCond_FirstUseEver); char buf[64]; snprintf(buf, sizeof(buf), "Entity: %ld", entity); - if (igTreeNode_Str(buf)) { - igSeparatorText("Tags"); - { - igTagCheckbox("GameEntity", ECS, entity, GameEntity); + if (igBegin(buf, open, 0)) { + if (igCollapsingHeader_TreeNodeFlags("Tags", 0)) { + //igTagCheckbox("GameEntity", ECS, entity, GameEntity); igTagCheckbox("Selectable", ECS, entity, Selectable); igTagCheckbox("Selected", ECS, entity, Selected); igTagCheckbox("Storage", ECS, entity, Storage); @@ -626,17 +648,30 @@ static void igEntity(ecs_entity_t entity) { igTagCheckbox("Workable", ECS, entity, Workable); igTagCheckbox("Attackable", ECS, entity, Attackable); } - igResource("Resource", ECS, entity, ecs_id(Resource)); - igVec2("Position", ECS, entity, ecs_id(Position)); - igVec2("Size", ECS, entity, ecs_id(Size)); - igVec2("Velocity", ECS, entity, ecs_id(Velocity)); - igVec2("Steering", ECS, entity, ecs_id(Steering)); - igVec2("TargetPosition", ECS, entity, ecs_id(TargetPosition)); - - igTreePop(); + igInspectComp("Resource", entity, ecs_id(Resource), igResource); + igInspectComp("TilePosition", entity, ecs_id(TilePosition), igTilePosition); + igInspectComp("TileSize", entity, ecs_id(TileSize), igTileSize); + igInspectComp("Owner", entity, ecs_id(Owner), igOwner); + igInspectComp("SpatialGridID", entity, ecs_id(SpatialGridID), igSpatialGridID); + igInspectComp("Position", entity, ecs_id(Position), igVec2Comp); + igInspectComp("Size", entity, ecs_id(Size), igVec2Comp); + igInspectComp("Velocity", entity, ecs_id(Velocity), igVec2Comp); + igInspectComp("TargetPosition", entity, ecs_id(TargetPosition), igVec2Comp); + igInspectComp("Steering", entity, ecs_id(Steering), igVec2Comp); + igInspectComp("Rotation", entity, ecs_id(Rotation), igFloat); + igInspectComp("Path", entity, ecs_id(Path), igPath); + igInspectComp("TextureRegion", entity, ecs_id(TextureRegion), igTextureRegion); + igInspectComp("Animation", entity, ecs_id(Animation), igAnimation); + igInspectComp("Easing", entity, ecs_id(Easing), igEasing); + igInspectComp("Arms", entity, ecs_id(Arms), igArms); + igInspectComp("Arm", entity, ecs_id(Arm), igArm); + igInspectComp("UnitAction", entity, ecs_id(UnitAction), igUnitAction); + igInspectComp("UnitAI", entity, ecs_id(UnitAI), igUnitAI); + igInspectComp("Worker", entity, ecs_id(Worker), igWorker); + igInspectComp("Unit", entity, ecs_id(Unit), igUnit); } + igEnd(); } - void imguiRender(float dt, void *userData) { BZ_UNUSED(userData); Game *game = ecs_singleton_get_mut(ECS, Game); @@ -676,7 +711,7 @@ void imguiRender(float dt, void *userData) { while (ecs_iter_next(&it)) { for (i32 i = 0; i < it.count; i++) { ecs_entity_t entity = it.entities[i]; - igEntity(entity); + igText("Entity: %ld", entity); } } ecs_defer_end(ECS); @@ -705,10 +740,10 @@ void imguiRender(float dt, void *userData) { } if (igCollapsingHeader_TreeNodeFlags("DebugDraw", 0)) { - igCheckbox("map colliders", &game->debugDraw.mapColliders); - igCheckbox("entity colliders", &game->debugDraw.entityColliders); - igCheckbox("spatial grid", &game->debugDraw.spatialGrid); - igCheckbox("path", &game->debugDraw.path); + igCheckbox("map colliders", &game->debug.drawMapColliders); + igCheckbox("entity colliders", &game->debug.drawEntityColliders); + igCheckbox("spatial grid", &game->debug.drawSpatialGrid); + igCheckbox("path", &game->debug.drawPath); } if (igCollapsingHeader_TreeNodeFlags("Entities", 0)) { @@ -718,5 +753,14 @@ void imguiRender(float dt, void *userData) { bzGameExit(); } igEnd(); + + i32 inspectLen = bzArraySize(game->debug.inspecting); + for (i32 i = inspectLen - 1; i >= 0; i--) { + bool open = true; + igInspectWindow(game->debug.inspecting[i], &open); + if (!open) { + bzArrayDelSwap(game->debug.inspecting, i); + } + } } diff --git a/game/systems/s_entity.c b/game/systems/s_entity.c index b6ad905..8aba581 100644 --- a/game/systems/s_entity.c +++ b/game/systems/s_entity.c @@ -189,7 +189,7 @@ void renderOrientationDirection(ecs_iter_t *it) { Vector2 v = {6.0f, 0.0f}; v = Vector2Rotate(v, orientation[i]); v = Vector2Add(v, pos[i]); - DrawLine(pos->x, pos->y, v.x, v.y, RED); + DrawLine(pos[i].x, pos[i].y, v.x, v.y, RED); } } diff --git a/game/systems/s_input.c b/game/systems/s_input.c index d84c7ce..df60fcb 100644 --- a/game/systems/s_input.c +++ b/game/systems/s_input.c @@ -64,6 +64,24 @@ void inputPrimaryAction(Game *game, InputState *input) { input->state = INPUT_SELECTED_BUILDING; } selectedCount = ecs_query_entity_count(input->queries.selected); + if (IsKeyDown(KEY_LEFT_ALT) && IsKeyDown(KEY_LEFT_SHIFT)) { + ecs_iter_t it = ecs_query_iter(ECS, input->queries.selected); + while (ecs_iter_next(&it)) { + for (i32 i = 0; i < it.count; i++) { + ecs_entity_t entity = it.entities[i]; + bool alreadyInspecting = false; + for (i32 j = 0; j < bzArraySize(game->debug.inspecting); j++) { + if (game->debug.inspecting[j] == entity) { + alreadyInspecting = true; + break; + } + } + if (!alreadyInspecting) + bzArrayPush(game->debug.inspecting, entity); + } + } + } + } if (selectedCount == 0) diff --git a/game/systems/systems.c b/game/systems/systems.c index 14e5969..7da1cd8 100644 --- a/game/systems/systems.c +++ b/game/systems/systems.c @@ -29,10 +29,17 @@ ECS_MOVE(Path, dst, src, { }) ECS_DTOR(Arms, arms, { - if (arms->primary) + if (arms->primary) { ecs_delete(ECS, arms->primary); - if (arms->secondary) + arms->primary = 0; + } + if (arms->secondary) { ecs_delete(ECS, arms->secondary); + arms->secondary = 0; + } +}) +ECS_MOVE(Arms, dst, src, { + *dst = *src; }); void setupSystems() { @@ -45,7 +52,8 @@ void setupSystems() { .move_dtor = ecs_move(Path) }); ecs_set_hooks(ECS, Arms, { - .dtor = ecs_dtor(Arms) + .dtor = ecs_dtor(Arms), + .move_dtor = ecs_move(Arms) }); ECS_OBSERVER(ECS, entityPathRemove, EcsOnRemove, Path);