From f431be1b29188e01bfe4c897e7f90d1d89bb2dbd Mon Sep 17 00:00:00 2001 From: doyle Date: Sat, 23 Sep 2023 15:52:26 +1000 Subject: [PATCH] fp: Precalculate the collision grid before the A* process --- feely_pona_game.cpp | 40 ++++++++++++++++++---------------------- feely_pona_game.h | 1 + 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp index 2924250..8cbe0c7 100644 --- a/feely_pona_game.cpp +++ b/feely_pona_game.cpp @@ -468,6 +468,8 @@ static Dqn_Slice FP_Game_AStarPathFind(FP_Game *game, FP_GameEntityHandle entity, Dqn_V2I dest_tile) { + Dqn_DSMap astar_info = Dqn_DSMap_Init(128); + DQN_DEFER { Dqn_DSMap_Deinit(&astar_info); }; // NOTE: Enumerate the entities that are collidable ============================================ Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); @@ -480,16 +482,25 @@ static Dqn_Slice FP_Game_AStarPathFind(FP_Game *game, if ((walk_entity->flags & FP_GameEntityFlag_NonTraversable) == 0) continue; - Dqn_List_Add(&colliders, walk_entity); + // NOTE: Mark tiles that the entity is on as non-traversable + Dqn_Rect bounding_box = FP_Game_CalcEntityWorldBoundingBox(game, walk_entity->handle); + Dqn_RectMinMax min_max = Dqn_Rect_MinMax(bounding_box); + Dqn_V2I min_tile = FP_Game_WorldPosToTilePos(game, min_max.min); + Dqn_V2I max_tile = FP_Game_WorldPosToTilePos(game, min_max.max); + + for (int32_t y = min_tile.y; y < max_tile.y; y++) { + for (int32_t x = min_tile.x; x < max_tile.x; x++) { + uint64_t tile_u64 = (DQN_CAST(uint64_t)y << 32) | (DQN_CAST(uint64_t)x << 0); + FP_GameAStarNode *node = Dqn_DSMap_MakeKeyU64(&astar_info, tile_u64).value; + node->non_traversable = true; + } + } } // NOTE: Setup A* state ======================================================================== Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity); Dqn_V2I src_tile = FP_Game_WorldPosToTilePos(game, entity_world_pos); - Dqn_DSMap astar_info = Dqn_DSMap_Init(128); - DQN_DEFER { Dqn_DSMap_Deinit(&astar_info); }; - Dqn_FArray frontier = {}; Dqn_FArray_Add(&frontier, src_tile); @@ -535,26 +546,11 @@ static Dqn_Slice FP_Game_AStarPathFind(FP_Game *game, uint64_t next_tile_u64 = (DQN_CAST(uint64_t)next_tile.y << 32) | (DQN_CAST(uint64_t)next_tile.x << 0); Dqn_DSMapResult next_cost_result = Dqn_DSMap_MakeKeyU64(&astar_info, next_tile_u64); - // NOTE: If we have already visited this node before, we only keep the cost if it's cheaper - if (next_cost_result.found && new_cost >= next_cost_result.value->cost) + if (next_cost_result.value->non_traversable) continue; - // NOTE: Check if the neighbour is a non-traversable tile, if so we skip it - bool tile_is_non_traversable = false; - for (Dqn_ListIterator it = {}; Dqn_List_Iterate(&colliders, &it, 0); ) { - FP_GameEntity const *collider = *it.data; - Dqn_Rect bounding_box = FP_Game_CalcEntityWorldBoundingBox(game, collider->handle); - Dqn_RectMinMax min_max = Dqn_Rect_MinMax(bounding_box); - Dqn_V2I min_tile = FP_Game_WorldPosToTilePos(game, min_max.min); - Dqn_V2I max_tile = FP_Game_WorldPosToTilePos(game, min_max.max); - - if ((next_tile.x >= min_tile.x && next_tile.x <= max_tile.x) && - (next_tile.y >= min_tile.y && next_tile.y <= max_tile.y)) { - tile_is_non_traversable = true; - } - } - - if (tile_is_non_traversable) + // NOTE: If we have already visited this node before, we only keep the cost if it's cheaper + if (next_cost_result.found && new_cost >= next_cost_result.value->cost) continue; // NOTE: Update the node cost value and the heuristic (estimated cost to the end) diff --git a/feely_pona_game.h b/feely_pona_game.h index 029ee0d..8292e4f 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -159,4 +159,5 @@ struct FP_GameAStarNode Dqn_usize cost; Dqn_usize heuristic; Dqn_V2I came_from; + bool non_traversable; };