From 021df6d77af313f76ff2802569a591e946cf10c7 Mon Sep 17 00:00:00 2001 From: Klemen Plestenjak Date: Fri, 17 Nov 2023 15:13:30 +0100 Subject: [PATCH] Remove multiple colliders, add spatial index component --- engine/breeze/defines.h | 2 +- engine/breeze/map/map.c | 107 ++++++++++++++++++++-------------------- engine/breeze/map/map.h | 14 ++---- game/buildings.c | 49 +++++++++--------- game/components.c | 2 + game/components.h | 3 ++ game/main.c | 10 ++-- game/map_init.c | 4 ++ game/pathfinding.c | 7 ++- game/systems.h | 4 +- game/systems_entity.c | 15 ++++++ 11 files changed, 122 insertions(+), 95 deletions(-) diff --git a/engine/breeze/defines.h b/engine/breeze/defines.h index a182b46..ea365e6 100644 --- a/engine/breeze/defines.h +++ b/engine/breeze/defines.h @@ -24,7 +24,7 @@ typedef double f64; #define DEBUG_MODE #ifndef DEBUG_MODE #undef BZ_ASSERT -#define BZ_ASSERT(e) BZ_UNUSDE(x) +#define BZ_ASSERT(e) BZ_UNUSED(x) #endif #define BZ_UNUSED(x) (void)(x) diff --git a/engine/breeze/map/map.c b/engine/breeze/map/map.c index 0885b2d..3b479db 100644 --- a/engine/breeze/map/map.c +++ b/engine/breeze/map/map.c @@ -10,19 +10,25 @@ BzTileMap BZ_TILEMAP_INVALID = {.isValid = false}; void bzTileLayerSkipRender(BzTileMap *map, BzTileLayer *layer) { - + BZ_UNUSED(map); + BZ_UNUSED(layer); } void bzTileObjectGroupSkipRender(BzTileMap *map, BzTileObjectGroup *objectGroup) { - + BZ_UNUSED(map); + BZ_UNUSED(objectGroup); } BzTileLayerRenderFunc BZ_TILE_LAYER_SKIP_RENDER = bzTileLayerSkipRender; BzTileObjectGroupRenderFunc BZ_TILE_OBJECTS_SKIP_RENDER = bzTileObjectGroupSkipRender; bool bzTileLayerClear(BzTileMap *map, BzTileLayer *layer) { + BZ_UNUSED(map); + BZ_UNUSED(layer); return true; } bool bzTileObjectsClear(BzTileMap *map, BzTileObjectGroup *objectGroup) { + BZ_UNUSED(map); + BZ_UNUSED(objectGroup); return true; } @@ -106,12 +112,20 @@ static void handleTileObjectLayer(BzTileObjectGroup *layer, cute_tiled_layer_t * } static void updateColliders(BzTileMap *map, i32 startX, i32 startY, i32 endX, i32 endY) { + BZ_ASSERT(map->colliderMap); BZ_ASSERT(startX >= 0 && endX <= map->width && startY >= 0 && endY <= map->height); + for (i32 y = startY; y < endY; y++) { + for (i32 x = startX; x < endX; x++) { + map->colliderMap[y * map->width + x] = (BzTileShape){.type=BZ_TILE_SHAPE_NONE}; + } + } + // Top-most layer takes priority for (i32 i = map->layerCount - 1; i >= 0; i--) { BzTileLayer *layer = map->layers + i; + if (!layer->desc.applyColliders) continue; if (!layer->data) continue; if (layer->tilesetIdx == -1) continue; BzTileset *tileset = map->tilesets + layer->tilesetIdx; @@ -125,15 +139,9 @@ static void updateColliders(BzTileMap *map, i32 startX, i32 startY, i32 endX, i3 tilesetShape.x += layer->offsetX; tilesetShape.y += layer->offsetY; - i32 colliderIdx = y * map->width + x; - BzTileCollider *collider = map->colliderMap + colliderIdx; - - for (i32 sIdx = 0; sIdx < BZ_MAP_COLLIDER_DEPTH; sIdx++) { - BzTileShape *shape = collider->shapes + sIdx; - if (shape->type == BZ_TILE_SHAPE_NONE) { - *shape = tilesetShape; - break; - } + BzTileShape *shape = &map->colliderMap[y * map->width + x]; + if (shape->type == BZ_TILE_SHAPE_NONE) { + *shape = tilesetShape; } } } @@ -141,17 +149,12 @@ static void updateColliders(BzTileMap *map, i32 startX, i32 startY, i32 endX, i3 } static void createColliders(BzTileMap *map) { - map->collidersCount = map->width * map->height; - map->colliderMap = bzAlloc(map->collidersCount * sizeof(*map->colliderMap)); - for (i32 i = 0; i < map->collidersCount; i++) { - map->colliderMap[i] = (BzTileCollider) {{BZ_TILE_SHAPE_NONE}}; - } - + map->colliderMap = bzAlloc(map->width * map->height * sizeof(*map->colliderMap)); updateColliders(map, 0, 0, map->width, map->height); } BzTileMap bzTileMapCreate(const BzTileMapDesc *desc) { - BzTileMap map = {}; + BzTileMap map = {.backgroundColor=BLACK}; // Auto detect tileset count. for (i32 i = 0; i < BZ_MAP_MAX_TILESETS; i++) { if (!desc->tilesets[i].isValid) @@ -181,8 +184,8 @@ BzTileMap bzTileMapCreate(const BzTileMapDesc *desc) { for (i32 i = 0; i < BZ_MAP_MAX_LAYERS; i++) { const BzTileLayerDesc *layerDesc = desc->layers + i; const BzTileObjectsDesc *objectsDesc = desc->objectGroups + i; - if (layerDesc->name && strcmp(layerDesc->name, cuteLayer->name.ptr) == 0 || - objectsDesc->name && strcmp(objectsDesc->name, cuteLayer->name.ptr) == 0) { + if ((layerDesc->name && strcmp(layerDesc->name, cuteLayer->name.ptr) == 0) || + (objectsDesc->name && strcmp(objectsDesc->name, cuteLayer->name.ptr) == 0)) { slot = i; break; } else if (!layerDesc->name && !objectsDesc->name) { @@ -254,7 +257,8 @@ BzTileMap bzTileMapCreate(const BzTileMapDesc *desc) { } cute_tiled_free_map(cuteMap); - createColliders(&map); + if (desc->generateColliderMap) + createColliders(&map); map.isValid = true; return map; @@ -279,7 +283,6 @@ void bzTileMapDestroy(BzTileMap *map) { } bzFree(map->colliderMap); - map->collidersCount = 0; *map = BZ_TILEMAP_INVALID; } @@ -403,28 +406,25 @@ void bzTileMapDrawColliders(BzTileMap *map) { for (i32 y = 0; y < map->height; y++) { for (i32 x = 0; x < map->width; x++) { i32 idx = y * map->width + x; - BzTileCollider collider = map->colliderMap[idx]; - for (i32 i = 0; i < BZ_MAP_COLLIDER_DEPTH; i++) { - BzTileShape shape = collider.shapes[i]; - if (shape.type == BZ_TILE_SHAPE_NONE) + BzTileShape shape = map->colliderMap[idx]; + if (shape.type == BZ_TILE_SHAPE_NONE) + continue; + + i32 posX = x * map->tileWidth + shape.x; + i32 posY = y * map->tileHeight + shape.y; + f32 sizeX = shape.sizeX; + f32 sizeY = shape.sizeY; + + switch (shape.type) { + case BZ_TILE_SHAPE_NONE: + default: + break; + case BZ_TILE_SHAPE_RECT: + DrawRectangleLines(posX, posY, sizeX, sizeY, color); + break; + case BZ_TILE_SHAPE_ELLIPSE: + DrawEllipseLines(posX, posY, sizeX, sizeY, color); break; - - i32 posX = x * map->tileWidth + shape.x; - i32 posY = y * map->tileHeight + shape.y; - f32 sizeX = shape.sizeX; - f32 sizeY = shape.sizeY; - - switch (shape.type) { - case BZ_TILE_SHAPE_NONE: - default: - break; - case BZ_TILE_SHAPE_RECT: - DrawRectangleLines(posX, posY, sizeX, sizeY, color); - break; - case BZ_TILE_SHAPE_ELLIPSE: - DrawEllipseLines(posX, posY, sizeX, sizeY, color); - break; - } } @@ -432,17 +432,18 @@ void bzTileMapDrawColliders(BzTileMap *map) { } } -void bzTileMapUpdateCollider(BzTileMap *map, i32 x, i32 y) { +BzTileShape bzTileMapGetCollider(BzTileMap *map, i32 x, i32 y) { + BzTileShape invalid = {.type = BZ_TILE_SHAPE_NONE}; + if (map->colliderMap == 0) return invalid; i32 idx = y * map->width + x; - BZ_ASSERT(idx >= 0 && idx < map->collidersCount); - map->colliderMap[idx] = (BzTileCollider){}; - updateColliders(map, x, y, x + 1, y + 1); -} - -BzTileCollider bzTileMapGetCollider(BzTileMap *map, i32 x, i32 y) { - i32 idx = y * map->width + x; - if (idx < 0 || idx >= map->collidersCount) { - return (BzTileCollider) {{BZ_TILE_SHAPE_NONE}}; + if (idx < 0 || idx >= map->width * map->height) { + return invalid; } return map->colliderMap[idx]; } + +void bzTileMapUpdateColliders(BzTileMap *map, i32 x, i32 y, i32 sizeX, i32 sizeY) { + if (!map->colliderMap) return; + updateColliders(map, x, y, x + sizeX, y + sizeY); +} + diff --git a/engine/breeze/map/map.h b/engine/breeze/map/map.h index 3c4c5e0..634ce5b 100644 --- a/engine/breeze/map/map.h +++ b/engine/breeze/map/map.h @@ -6,7 +6,6 @@ #define BZ_MAP_MAX_LAYERS 8 #define BZ_MAP_MAX_TILESETS 8 -#define BZ_MAP_COLLIDER_DEPTH 2 typedef struct BzTileLayer BzTileLayer; typedef struct BzTileObject BzTileObject; @@ -22,6 +21,7 @@ extern BzTileObjectGroupRenderFunc BZ_TILE_OBJECTS_SKIP_RENDER; typedef struct BzTileLayerDesc { const char *name; // Matches map layer names BzTileLayerRenderFunc renderer; + bool applyColliders; } BzTileLayerDesc; typedef struct BzTileObjectsDesc { const char *name; // Matches map layer names @@ -31,6 +31,7 @@ typedef struct BzTileObjectsDesc { typedef struct BzTileMapDesc { const char *path; + bool generateColliderMap; BzTileset tilesets[BZ_MAP_MAX_TILESETS]; BzTileLayerDesc layers[BZ_MAP_MAX_LAYERS]; @@ -76,10 +77,6 @@ typedef struct BzTileObjectGroup { } BzTileObjectGroup; -typedef struct BzTileCollider { - BzTileShape shapes[BZ_MAP_COLLIDER_DEPTH]; -} BzTileCollider; - typedef struct BzTileMap { Color backgroundColor; @@ -88,8 +85,7 @@ typedef struct BzTileMap { i32 tileWidth; i32 tileHeight; - BzTileCollider *colliderMap; - i32 collidersCount; + BzTileShape *colliderMap; BzTileLayer layers[BZ_MAP_MAX_LAYERS]; i32 layerCount; @@ -128,8 +124,8 @@ BzTileObjectGroup *bzTileMapGetObjects(BzTileMap *map, i32 slotID); void bzTileMapDraw(BzTileMap *map); void bzTileMapDrawColliders(BzTileMap *map); -BzTileCollider bzTileMapGetCollider(BzTileMap *map, i32 x, i32 y); -void bzTileMapUpdateCollider(BzTileMap *map, i32 x, i32 y); +BzTileShape bzTileMapGetCollider(BzTileMap *map, i32 x, i32 y); +void bzTileMapUpdateColliders(BzTileMap *map, i32 x, i32 y, i32 sizeX, i32 sizeY); diff --git a/game/buildings.c b/game/buildings.c index 29eea25..2574404 100644 --- a/game/buildings.c +++ b/game/buildings.c @@ -37,30 +37,27 @@ bool canPlaceBuilding(BzTileMap *map, BuildingType type, BzTile tileX, BzTile ti if (tile == BUILDINGS_ROAD) return false; } - BzTileCollider collider = bzTileMapGetCollider(map, x, y); + BzTileShape shape = bzTileMapGetCollider(map, x, y); f32 posX = x * map->tileWidth; f32 posY = y * map->tileHeight; - for (int i = 0; i < BZ_MAP_COLLIDER_DEPTH; i++) { - BzTileShape shape = collider.shapes[i]; - shape.x += posX; - shape.y += posY; - switch (shape.type) { - case BZ_TILE_SHAPE_NONE: - case BZ_TILE_SHAPE_POINT: - break; - case BZ_TILE_SHAPE_RECT: { - Rectangle shapeRec = {shape.x, shape.y, shape.sizeX, shape.sizeY}; - if (CheckCollisionRecs(buildArea, shapeRec)) - return false; - break; - } - case BZ_TILE_SHAPE_ELLIPSE: { - Vector2 pos = {shape.x, shape.y}; - f32 radius = (shape.sizeX + shape.sizeY) * 0.5f; - if (CheckCollisionCircleRec(pos, radius, buildArea)) - return false; - break; - } + shape.x += posX; + shape.y += posY; + switch (shape.type) { + case BZ_TILE_SHAPE_NONE: + case BZ_TILE_SHAPE_POINT: + break; + case BZ_TILE_SHAPE_RECT: { + Rectangle shapeRec = {shape.x, shape.y, shape.sizeX, shape.sizeY}; + if (CheckCollisionRecs(buildArea, shapeRec)) + return false; + break; + } + case BZ_TILE_SHAPE_ELLIPSE: { + Vector2 pos = {shape.x, shape.y}; + f32 radius = (shape.sizeX + shape.sizeY) * 0.5f; + if (CheckCollisionCircleRec(pos, radius, buildArea)) + return false; + break; } } } @@ -82,9 +79,9 @@ ecs_entity_t placeBuilding(BzTileMap *map, BuildingType type, BzTile tileX, BzTi // Create entity ecs_entity_t e = ecs_new_id(ECS); - ecs_set(ECS, e, TilePosition, {.x=tileX, .y=tileY}); - ecs_set(ECS, e, TileSize, {.sizeX=sizeX, .sizeY=sizeY}); - ecs_set(ECS, e, Owner, {.playerID=BUILDINGS_PLAYER_RED}); + ecs_set(ECS, e, TilePosition, { .x = tileX, .y = tileY }); + ecs_set(ECS, e, TileSize, { .sizeX = sizeX, .sizeY = sizeY }); + ecs_set(ECS, e, Owner, { .playerID = BUILDINGS_PLAYER_RED }); for (i32 y = tileY; y < tileY + sizeY; y++) { for (i32 x = tileX; x < tileX + sizeX; x++) { @@ -92,7 +89,7 @@ ecs_entity_t placeBuilding(BzTileMap *map, BuildingType type, BzTile tileX, BzTi bzTileLayerSetTile(buildingLayer, layerTile, x, y, 1, 1); buildingTile++; - bzTileMapUpdateCollider(map, x, y); + bzTileMapUpdateColliders(map, x, y, 1, 1); } buildingTile += buildingTileset->width - sizeX; } diff --git a/game/components.c b/game/components.c index 1fa86f7..4cd7043 100644 --- a/game/components.c +++ b/game/components.c @@ -8,6 +8,7 @@ ECS_COMPONENT_DECLARE(Position); ECS_COMPONENT_DECLARE(Size); ECS_COMPONENT_DECLARE(TargetPosition); ECS_COMPONENT_DECLARE(MoveForce); +ECS_COMPONENT_DECLARE(SpatialGridID); ECS_COMPONENT_DECLARE(Rotation); ECS_COMPONENT_DECLARE(Health); ECS_COMPONENT_DECLARE(TextureRegion); @@ -23,6 +24,7 @@ void initComponentIDs(ecs_world_t *ecs) { ECS_COMPONENT_DEFINE(ecs, Size); ECS_COMPONENT_DEFINE(ecs, TargetPosition); ECS_COMPONENT_DEFINE(ecs, MoveForce); + ECS_COMPONENT_DEFINE(ecs, SpatialGridID); ECS_COMPONENT_DEFINE(ecs, Rotation); ECS_COMPONENT_DEFINE(ecs, Health); ECS_COMPONENT_DEFINE(ecs, TextureRegion); diff --git a/game/components.h b/game/components.h index 4804367..c71f31b 100644 --- a/game/components.h +++ b/game/components.h @@ -32,6 +32,9 @@ extern ECS_COMPONENT_DECLARE(Size); extern ECS_COMPONENT_DECLARE(TargetPosition); extern ECS_COMPONENT_DECLARE(MoveForce); +typedef BzSpatialGridID SpatialGridID; +extern ECS_COMPONENT_DECLARE(SpatialGridID); + typedef f32 Rotation; extern ECS_COMPONENT_DECLARE(Rotation); diff --git a/game/main.c b/game/main.c index 562d909..57f578a 100644 --- a/game/main.c +++ b/game/main.c @@ -83,15 +83,16 @@ bool init(void *userData) { game->map = bzTileMapCreate(&(BzTileMapDesc) { .path="assets/maps/test.tmj", + .generateColliderMap=true, .tilesets[0]=game->terrainTileset, .tilesets[1]=game->buildingsTileset, .tilesets[2]=game->entitiesTileset, - .layers[LAYER_TERRAIN]=(BzTileLayerDesc) {"Terrain"}, + .layers[LAYER_TERRAIN]=(BzTileLayerDesc) {"Terrain", .applyColliders=true}, .layers[LAYER_FOLIAGE]=(BzTileLayerDesc) {"Foliage"}, .layers[LAYER_TREES]=(BzTileLayerDesc) {"Trees"}, .layers[LAYER_TREES2]=(BzTileLayerDesc) {"TreesS"}, - .layers[LAYER_BUILDINGS]=(BzTileLayerDesc) {"Buildings"}, + .layers[LAYER_BUILDINGS]=(BzTileLayerDesc) {"Buildings", .applyColliders=true}, .layers[LAYER_BUILDING_OWNER]=(BzTileLayerDesc) {"BuildingOwnership", BZ_TILE_LAYER_SKIP_RENDER}, .objectGroups[OBJECTS_GAME]=(BzTileObjectsDesc) {"Game"}, @@ -100,8 +101,8 @@ bool init(void *userData) { game->entityGrid = bzSpatialGridCreate(&(BzSpatialGridDesc) { .maxWidth=game->map.width * game->map.tileWidth, .maxHeight=game->map.height * game->map.tileHeight, - .cellWidth=game->map.tileWidth * 2, - .cellHeight=game->map.tileHeight * 2, + .cellWidth=game->map.tileWidth * 5, + .cellHeight=game->map.tileHeight * 5, .userDataSize=sizeof(ecs_entity_t) }); @@ -113,6 +114,7 @@ bool init(void *userData) { ECS_SYSTEM(ECS, uiTask, EcsOnUpdate, 0); ECS_SYSTEM(ECS, updateAnimations, EcsOnUpdate, Animation, TextureRegion); ECS_SYSTEM(ECS, updatePos, EcsOnUpdate, Position, TargetPosition, TextureRegion); + ECS_SYSTEM(ECS, entityUpdatePhysics, EcsOnUpdate, Position, Size, SpatialGridID); ECS_SYSTEM(ECS, drawDebugPath, EcsOnUpdate, Path); ECS_SYSTEM(ECS, renderEntities, EcsOnUpdate, Position, Size, Rotation, TextureRegion); ECS_OBSERVER(ECS, targetFinish, EcsOnRemove, TargetPosition); diff --git a/game/map_init.c b/game/map_init.c index 21b7a43..150ea71 100644 --- a/game/map_init.c +++ b/game/map_init.c @@ -29,6 +29,10 @@ bool initEntityObjectsLayer(BzTileMap *map, BzTileObjectGroup *objectGroup) { game->entity = e; ecs_set(ECS, e, Position, {object.shape.x, object.shape.y}); ecs_set(ECS, e, Size, {object.shape.sizeX, object.shape.sizeY}); + BzSpatialGridID spatialID = bzSpatialGridInsert(game->entityGrid, &e, + object.shape.x, object.shape.y, + object.shape.sizeX, object.shape.sizeY); + ecs_set(ECS, e, SpatialGridID, {spatialID}); ecs_set(ECS, e, Rotation, {0.0f}); ecs_set(ECS, e, TextureRegion, {objectTileset->tiles, bzTilesetGetTileRegion(objectTileset, object.gid)}); ecs_set(ECS, e, Animation, { diff --git a/game/pathfinding.c b/game/pathfinding.c index 6bc8e8d..2681e92 100644 --- a/game/pathfinding.c +++ b/game/pathfinding.c @@ -54,7 +54,7 @@ bool findPath(const PathfindingDesc *desc) { if (y < 0 || y >= map->height || x < 0 || x >= map->width) continue; - if (bzTileMapGetCollider(map, x, y).shapes[0].type != BZ_TILE_SHAPE_NONE) + if (bzTileMapGetCollider(map, x, y).type != BZ_TILE_SHAPE_NONE) continue; Visited *curVisited = &visited[y * map->width + x]; if (curVisited->visited) @@ -91,6 +91,7 @@ bool findPath(const PathfindingDesc *desc) { pathData->next = NULL; i32 numWaypoints = 0; // Write path + // TODO: Write end pos while (pos.x != desc->start.x || pos.y != desc->start.y) { Position waypoint = { pos.x * map->tileWidth + map->tileWidth * 0.5f, @@ -113,6 +114,10 @@ bool findPath(const PathfindingDesc *desc) { pos.y -= visit.y; pathLen++; } + if (pathLen == 0) { + bzObjectPoolRelease(desc->pool, pathData); + pathData = NULL; + } out->paths = pathData; // Reverse paths diff --git a/game/systems.h b/game/systems.h index dc47754..9427e22 100644 --- a/game/systems.h +++ b/game/systems.h @@ -22,7 +22,9 @@ void entityAdded(ecs_iter_t *it); /* * 0: Game (singleton) * 1: Position - * 2: MoveForce + * 2: Size + * 3: MoveForce + * 4: SpatialGridID */ void entityUpdatePhysics(ecs_iter_t *it); diff --git a/game/systems_entity.c b/game/systems_entity.c index c37e9e7..2fbbfce 100644 --- a/game/systems_entity.c +++ b/game/systems_entity.c @@ -12,6 +12,19 @@ void entityAdded(ecs_iter_t *it) { } void entityUpdatePhysics(ecs_iter_t *it) { + Game *game = ecs_singleton_get_mut(ECS, Game); + Position *pos = ecs_field(it, Position, 1); + Size *size = ecs_field(it, Size, 2); + //MoveForce *force = ecs_field(it, MoveForce, 3); + SpatialGridID *spatialID = ecs_field(it, SpatialGridID, 3); + + for (i32 i = 0; i < it->count; i++) { + //bzSpatialGridUpdate(game->entityGrid, spatialID[i], pos[i].x, pos[i].y, size[i].x, size[i].y); + ecs_entity_t *e = bzSpatialGridGetData(game->entityGrid, spatialID[i]); + BZ_ASSERT(*e == it->entities[i]); + bzSpatialGridRemove(game->entityGrid, spatialID[i]); + spatialID[i] = bzSpatialGridInsert(game->entityGrid, &(it->entities[i]), pos[i].x, pos[i].y, size[i].x, size[i].y); + } } @@ -46,6 +59,7 @@ void renderEntities(ecs_iter_t *it) { if (t[i].flipX) src.width *= -1.0f; if (t[i].flipY) src.height *= -1.0f; DrawTexturePro(t[i].texture, src, dst, origin, r[i], WHITE); + DrawRectangleLines(dst.x - dst.width * 0.5f, dst.y, dst.width, dst.height, RED); } } @@ -74,6 +88,7 @@ void updatePos(ecs_iter_t *it) { #include void targetFinish(ecs_iter_t *it) { const Game *game = ecs_singleton_get(ECS, Game); + if (game == NULL) return; for (i32 i = 0; i < it->count; i++) { ecs_entity_t e = it->entities[i];