fp: Add debug visualisation for A*

This commit is contained in:
doyle 2023-09-23 17:19:36 +10:00
parent 38410073cf
commit 9106230079
4 changed files with 32 additions and 6 deletions

View File

@ -605,9 +605,9 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
// NOTE: Stalk entity ======================================================================
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
{
Dqn_Profiler_ZoneScopeWithIndex("FP_Update: Path finding", FP_ProfileZone_FPUpdate_PathFinding);
FP_GameEntity *stalk_entity = FP_Game_GetEntity(game, entity->stalk_entity);
if (stalk_entity->handle.id) {
Dqn_Profiler_ZoneScopeWithIndex("FP_Update: Path finding", FP_ProfileZone_FPUpdate_PathFinding);
Dqn_V2 stalk_world_pos = FP_Game_CalcEntityWorldPos(game, stalk_entity->handle);
Dqn_V2I stalk_tile = Dqn_V2I_InitNx2(stalk_world_pos.x / game->tile_size, stalk_world_pos.y / game->tile_size);
if (entity->stalk_entity_last_known_tile != stalk_tile) {

View File

@ -8,6 +8,10 @@ enum FP_ProfileZone
FP_ProfileZone_FPUpdate = TELY_ProfileZone_Count,
FP_ProfileZone_FPUpdate_EntityLoop,
FP_ProfileZone_FPUpdate_PathFinding,
FP_ProfileZone_FPUpdate_AStar,
FP_ProfileZone_FPUpdate_AStarEnumerateCollidables,
FP_ProfileZone_FPUpdate_AStarExpand,
FP_ProfileZone_FPUpdate_AStarExploreNeighbours,
FP_ProfileZone_FPUpdate_Attacks,
FP_ProfileZone_FPRender,
};

View File

@ -468,10 +468,20 @@ static Dqn_Slice<Dqn_V2I> FP_Game_AStarPathFind(FP_Game *game,
FP_GameEntityHandle entity,
Dqn_V2I dest_tile)
{
Dqn_Profiler_ZoneScopeWithIndex("FP_Update: A*", FP_ProfileZone_FPUpdate_AStar);
Dqn_usize tile_count_x = DQN_CAST(Dqn_usize)(platform->core.window_size.w / game->tile_size);
Dqn_usize tile_count_y = DQN_CAST(Dqn_usize)(platform->core.window_size.h / game->tile_size);
Dqn_Slice<Dqn_V2I> result = {};
if (dest_tile.x < 0 || dest_tile.x > tile_count_x ||
dest_tile.y < 0 || dest_tile.y > tile_count_y)
return result;
Dqn_DSMap<FP_GameAStarNode> astar_info = Dqn_DSMap_Init<FP_GameAStarNode>(128);
DQN_DEFER { Dqn_DSMap_Deinit(&astar_info); };
// NOTE: Enumerate the entities that are collidable ============================================
auto zone_enum_collidables = Dqn_Profiler_BeginZoneWithIndex(DQN_STRING8("FP_Update: A* enumerate collidables"), FP_ProfileZone_FPUpdate_AStarEnumerateCollidables);
for (FP_GameEntityIterator it = {}; FP_Game_DFSPreOrderWalkEntityTree(game, &it, game->root_entity); ) {
FP_GameEntity const *walk_entity = it.entity;
if (entity == walk_entity->handle)
@ -491,9 +501,11 @@ static Dqn_Slice<Dqn_V2I> FP_Game_AStarPathFind(FP_Game *game,
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;
node->tile = Dqn_V2I_InitNx2(x, y);
}
}
}
Dqn_Profiler_EndZone(zone_enum_collidables);
// NOTE: Setup A* state ========================================================================
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity);
@ -502,18 +514,17 @@ static Dqn_Slice<Dqn_V2I> FP_Game_AStarPathFind(FP_Game *game,
Dqn_FArray<Dqn_V2I, 128> frontier = {};
Dqn_FArray_Add(&frontier, src_tile);
Dqn_usize tile_count_x = DQN_CAST(Dqn_usize)(platform->core.window_size.w / game->tile_size);
Dqn_usize tile_count_y = DQN_CAST(Dqn_usize)(platform->core.window_size.h / game->tile_size);
// NOTE: Initialise the starting cost
uint64_t src_tile_u64 = (DQN_CAST(uint64_t)src_tile.y << 32) | (DQN_CAST(uint64_t)src_tile.x << 0);
Dqn_DSMap_MakeKeyU64(&astar_info, src_tile_u64);
Dqn_DSMap_MakeKeyU64(&astar_info, src_tile_u64).value->tile = src_tile;
// NOTE: Do the A* process =====================================================================
Dqn_usize last_successful_manhattan_dist = UINT64_MAX;
Dqn_V2I last_successful_tile = src_tile;
auto zone_astar_expand = Dqn_Profiler_BeginZoneWithIndex(DQN_STRING8("FP_Update: A* expand"), FP_ProfileZone_FPUpdate_AStarExpand);
while (frontier.size) {
Dqn_Profiler_ZoneScopeWithIndex("FP_Update: A* neighbours", FP_ProfileZone_FPUpdate_AStarExploreNeighbours);
Dqn_V2I curr_tile = Dqn_FArray_PopFront(&frontier, 1);
if (curr_tile == dest_tile)
break;
@ -543,6 +554,7 @@ static Dqn_Slice<Dqn_V2I> FP_Game_AStarPathFind(FP_Game *game,
Dqn_usize new_cost = curr_cost + 1;
uint64_t next_tile_u64 = (DQN_CAST(uint64_t)next_tile.y << 32) | (DQN_CAST(uint64_t)next_tile.x << 0);
Dqn_DSMapResult<FP_GameAStarNode> next_cost_result = Dqn_DSMap_MakeKeyU64(&astar_info, next_tile_u64);
next_cost_result.value->tile = next_tile;
if (next_cost_result.value->non_traversable)
continue;
@ -585,6 +597,15 @@ static Dqn_Slice<Dqn_V2I> FP_Game_AStarPathFind(FP_Game *game,
Dqn_FArray_Add(&frontier, next_tile);
}
}
Dqn_Profiler_EndZone(zone_astar_expand);
TELY_Renderer *renderer = &platform->renderer;
for (uint32_t old_index = 1 /*Sentinel*/; old_index < astar_info.occupied; old_index++) {
Dqn_DSMapSlot<FP_GameAStarNode> const *slot = astar_info.slots + old_index;
FP_GameAStarNode const *node = &slot->value;
Dqn_V2 pos = FP_Game_TilePosToWorldPos(game, node->tile) + (game->tile_size * .5f);
TELY_Render_CircleColourV4(renderer, pos, 4.f, TELY_RenderShapeMode_Fill, TELY_COLOUR_BLUE_CADET_V4);
}
Dqn_usize slice_size = 0;
for (Dqn_V2I it = last_successful_tile; it != src_tile; slice_size++) {
@ -592,7 +613,7 @@ static Dqn_Slice<Dqn_V2I> FP_Game_AStarPathFind(FP_Game *game,
it = Dqn_DSMap_FindKeyU64(&astar_info, key_u64).value->came_from;
}
Dqn_Slice<Dqn_V2I> result = Dqn_Slice_Alloc<Dqn_V2I>(arena, slice_size, Dqn_ZeroMem_No);
result = Dqn_Slice_Alloc<Dqn_V2I>(arena, slice_size, Dqn_ZeroMem_No);
slice_size = 0;
for (Dqn_V2I it = last_successful_tile; it != src_tile; ) {
result.data[slice_size++] = it;

View File

@ -167,6 +167,7 @@ struct FP_GameAStarNode
{
Dqn_usize cost;
Dqn_usize heuristic;
Dqn_V2I tile;
Dqn_V2I came_from;
bool non_traversable;
};