forked from 0ad/0ad
Enhance the MakeGoalReachable algorithm to handle better non-point goals.
Fixes #3363, #3297. This was SVN commit r16915.
This commit is contained in:
parent
f7a7b687fc
commit
28ef270571
@ -205,17 +205,48 @@ void HierarchicalPathfinder::Chunk::RegionNavcellNearest(u16 r, int iGoal, int j
|
||||
{
|
||||
for (int i = 0; i < CHUNK_SIZE; ++i)
|
||||
{
|
||||
if (m_Regions[j][i] == r)
|
||||
{
|
||||
u32 dist2 = (i + m_ChunkI*CHUNK_SIZE - iGoal)*(i + m_ChunkI*CHUNK_SIZE - iGoal)
|
||||
+ (j + m_ChunkJ*CHUNK_SIZE - jGoal)*(j + m_ChunkJ*CHUNK_SIZE - jGoal);
|
||||
if (m_Regions[j][i] != r)
|
||||
continue;
|
||||
|
||||
if (dist2 < dist2Best)
|
||||
{
|
||||
iBest = i + m_ChunkI*CHUNK_SIZE;
|
||||
jBest = j + m_ChunkJ*CHUNK_SIZE;
|
||||
dist2Best = dist2;
|
||||
}
|
||||
u32 dist2 = (i + m_ChunkI*CHUNK_SIZE - iGoal)*(i + m_ChunkI*CHUNK_SIZE - iGoal)
|
||||
+ (j + m_ChunkJ*CHUNK_SIZE - jGoal)*(j + m_ChunkJ*CHUNK_SIZE - jGoal);
|
||||
|
||||
if (dist2 < dist2Best)
|
||||
{
|
||||
iBest = i + m_ChunkI*CHUNK_SIZE;
|
||||
jBest = j + m_ChunkJ*CHUNK_SIZE;
|
||||
dist2Best = dist2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the global navcell coords, and the squared distance to the (i0,j0)
|
||||
* navcell, of whichever navcell inside the given region and inside the given goal
|
||||
* is closest to (i0,j0)
|
||||
*/
|
||||
void HierarchicalPathfinder::Chunk::RegionNearestNavcellInGoal(u16 r, u16 i0, u16 j0, const PathGoal& goal, u16& iOut, u16& jOut, u32& dist2Best) const
|
||||
{
|
||||
iOut = 0;
|
||||
jOut = 0;
|
||||
dist2Best = std::numeric_limits<u32>::max();
|
||||
|
||||
for (int j = 0; j < CHUNK_SIZE; ++j)
|
||||
{
|
||||
for (int i = 0; i < CHUNK_SIZE; ++i)
|
||||
{
|
||||
if (m_Regions[j][i] != r || !goal.NavcellContainsGoal(i + m_ChunkI*CHUNK_SIZE, j + m_ChunkJ*CHUNK_SIZE))
|
||||
continue;
|
||||
|
||||
u32 dist2 = (i + m_ChunkI*CHUNK_SIZE - i0)*(i + m_ChunkI*CHUNK_SIZE - i0)
|
||||
+ (j + m_ChunkJ*CHUNK_SIZE - j0)*(j + m_ChunkJ*CHUNK_SIZE - j0);
|
||||
|
||||
if (dist2 < dist2Best)
|
||||
{
|
||||
iOut = i + m_ChunkI*CHUNK_SIZE;
|
||||
jOut = j + m_ChunkJ*CHUNK_SIZE;
|
||||
dist2Best = dist2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -452,7 +483,7 @@ HierarchicalPathfinder::RegionID HierarchicalPathfinder::Get(u16 i, u16 j, pass_
|
||||
return m_Chunks[passClass][cj*m_ChunksW + ci].Get(i % CHUNK_SIZE, j % CHUNK_SIZE);
|
||||
}
|
||||
|
||||
bool HierarchicalPathfinder::MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, pass_class_t passClass)
|
||||
void HierarchicalPathfinder::MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, pass_class_t passClass)
|
||||
{
|
||||
RegionID source = Get(i0, j0, passClass);
|
||||
|
||||
@ -461,6 +492,7 @@ bool HierarchicalPathfinder::MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, p
|
||||
FindReachableRegions(source, reachableRegions, passClass);
|
||||
|
||||
// Check whether any reachable region contains the goal
|
||||
std::set<RegionID> reachableContainingGoal;
|
||||
for (const RegionID& region : reachableRegions)
|
||||
{
|
||||
// Skip region if its chunk doesn't contain the goal area
|
||||
@ -472,26 +504,59 @@ bool HierarchicalPathfinder::MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, p
|
||||
continue;
|
||||
|
||||
// If the region contains the goal area, the goal is reachable
|
||||
// and we don't need to move it
|
||||
if (GetChunk(region.ci, region.cj, passClass).RegionContainsGoal(region.r, goal))
|
||||
return false;
|
||||
{
|
||||
// If it's a point, no need to move it, we're done
|
||||
if (goal.type == PathGoal::POINT)
|
||||
return;
|
||||
// Else remember this region
|
||||
reachableContainingGoal.insert(region);
|
||||
}
|
||||
}
|
||||
|
||||
// The goal area wasn't reachable,
|
||||
// so find the navcell that's nearest to the goal's center
|
||||
// If the goal area wasn't reachable,
|
||||
// find the navcell that's nearest to the goal's center
|
||||
if (reachableContainingGoal.empty())
|
||||
{
|
||||
u16 iGoal, jGoal;
|
||||
Pathfinding::NearestNavcell(goal.x, goal.z, iGoal, jGoal, m_W, m_H);
|
||||
|
||||
u16 iGoal, jGoal;
|
||||
Pathfinding::NearestNavcell(goal.x, goal.z, iGoal, jGoal, m_W, m_H);
|
||||
FindNearestNavcellInRegions(reachableRegions, iGoal, jGoal, passClass);
|
||||
|
||||
FindNearestNavcellInRegions(reachableRegions, iGoal, jGoal, passClass);
|
||||
// Construct a new point goal at the nearest reachable navcell
|
||||
PathGoal newGoal;
|
||||
newGoal.type = PathGoal::POINT;
|
||||
Pathfinding::NavcellCenter(iGoal, jGoal, newGoal.x, newGoal.z);
|
||||
goal = newGoal;
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-point goals:
|
||||
// For each region, find the closest navcell to (i0,j0) which is also in the goal,
|
||||
// and use the best of all regions
|
||||
ENSURE(goal.type != PathGoal::POINT);
|
||||
|
||||
u16 bestI, bestJ;
|
||||
u32 dist2Best = std::numeric_limits<u32>::max();
|
||||
|
||||
for (const RegionID& region : reachableContainingGoal)
|
||||
{
|
||||
u16 i, j;
|
||||
u32 dist2;
|
||||
GetChunk(region.ci, region.cj, passClass).RegionNearestNavcellInGoal(region.r, i0, j0, goal, i, j, dist2);
|
||||
|
||||
if (dist2 < dist2Best)
|
||||
{
|
||||
bestI = i;
|
||||
bestJ = j;
|
||||
dist2Best = dist2;
|
||||
}
|
||||
}
|
||||
|
||||
// Construct a new point goal at the nearest reachable navcell
|
||||
PathGoal newGoal;
|
||||
newGoal.type = PathGoal::POINT;
|
||||
Pathfinding::NavcellCenter(iGoal, jGoal, newGoal.x, newGoal.z);
|
||||
Pathfinding::NavcellCenter(bestI, bestJ, newGoal.x, newGoal.z);
|
||||
goal = newGoal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HierarchicalPathfinder::FindNearestPassableNavcell(u16& i, u16& j, pass_class_t passClass)
|
||||
|
@ -91,12 +91,14 @@ public:
|
||||
/**
|
||||
* Updates @p goal so that it's guaranteed to be reachable from the navcell
|
||||
* @p i0, @p j0 (which is assumed to be on a passable navcell).
|
||||
* If any part of the goal area is already reachable then
|
||||
* nothing is changed; otherwise the goal is replaced with a point goal
|
||||
* at the nearest reachable navcell to the original goal's center.
|
||||
* Returns true if the goal was replaced.
|
||||
*
|
||||
* If the goal is not reachable, it is replaced with a point goal nearest to
|
||||
* the goal center.
|
||||
*
|
||||
* In the case of a non-point reachable goal, it is replaced with a point goal
|
||||
* at the reachable navcell of the goal which is nearest to the starting navcell.
|
||||
*/
|
||||
bool MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, pass_class_t passClass);
|
||||
void MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, pass_class_t passClass);
|
||||
|
||||
/**
|
||||
* Updates @p i, @p j (which is assumed to be an impassable navcell)
|
||||
@ -140,6 +142,8 @@ private:
|
||||
bool RegionContainsGoal(u16 r, const PathGoal& goal) const;
|
||||
|
||||
void RegionNavcellNearest(u16 r, int iGoal, int jGoal, int& iBest, int& jBest, u32& dist2Best) const;
|
||||
|
||||
void RegionNearestNavcellInGoal(u16 r, u16 i0, u16 j0, const PathGoal& goal, u16& iOut, u16& jOut, u32& dist2Best) const;
|
||||
};
|
||||
|
||||
typedef std::map<RegionID, std::set<RegionID> > EdgesMap;
|
||||
|
@ -809,19 +809,9 @@ void LongPathfinder::ComputeJPSPath(entity_pos_t x0, entity_pos_t z0, const Path
|
||||
|
||||
state.goal = origGoal;
|
||||
|
||||
if (state.goal.type != PathGoal::POINT)
|
||||
{
|
||||
// Try to go to the nearest point on non-point goals.
|
||||
// If it is reachable, use it, else fallback to the reachable point which is closest to the center of the goal
|
||||
CFixedVector2D goalPos = state.goal.NearestPointOnGoal(CFixedVector2D(x0, z0));
|
||||
PathGoal pointGoal = { PathGoal::POINT, goalPos.X, goalPos.Y };
|
||||
if (!m_PathfinderHier.MakeGoalReachable(i0, j0, pointGoal, passClass))
|
||||
state.goal = pointGoal;
|
||||
else
|
||||
m_PathfinderHier.MakeGoalReachable(i0, j0, state.goal, passClass);
|
||||
}
|
||||
else
|
||||
m_PathfinderHier.MakeGoalReachable(i0, j0, state.goal, passClass);
|
||||
// Make the goal reachable. This includes shortening the path if the goal is in a non-passable
|
||||
// region, transforming non-point goals to reachable point goals, etc.
|
||||
m_PathfinderHier.MakeGoalReachable(i0, j0, state.goal, passClass);
|
||||
|
||||
// If we're already at the goal tile, then move directly to the exact goal coordinates
|
||||
if (state.goal.NavcellContainsGoal(i0, j0))
|
||||
|
Loading…
Reference in New Issue
Block a user