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)
|
for (int i = 0; i < CHUNK_SIZE; ++i)
|
||||||
{
|
{
|
||||||
if (m_Regions[j][i] == r)
|
if (m_Regions[j][i] != r)
|
||||||
{
|
continue;
|
||||||
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)
|
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);
|
||||||
iBest = i + m_ChunkI*CHUNK_SIZE;
|
|
||||||
jBest = j + m_ChunkJ*CHUNK_SIZE;
|
if (dist2 < dist2Best)
|
||||||
dist2Best = dist2;
|
{
|
||||||
}
|
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);
|
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);
|
RegionID source = Get(i0, j0, passClass);
|
||||||
|
|
||||||
@ -461,6 +492,7 @@ bool HierarchicalPathfinder::MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, p
|
|||||||
FindReachableRegions(source, reachableRegions, passClass);
|
FindReachableRegions(source, reachableRegions, passClass);
|
||||||
|
|
||||||
// Check whether any reachable region contains the goal
|
// Check whether any reachable region contains the goal
|
||||||
|
std::set<RegionID> reachableContainingGoal;
|
||||||
for (const RegionID& region : reachableRegions)
|
for (const RegionID& region : reachableRegions)
|
||||||
{
|
{
|
||||||
// Skip region if its chunk doesn't contain the goal area
|
// 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;
|
continue;
|
||||||
|
|
||||||
// If the region contains the goal area, the goal is reachable
|
// 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))
|
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,
|
// If the goal area wasn't reachable,
|
||||||
// so find the navcell that's nearest to the goal's center
|
// 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;
|
FindNearestNavcellInRegions(reachableRegions, iGoal, jGoal, passClass);
|
||||||
Pathfinding::NearestNavcell(goal.x, goal.z, iGoal, jGoal, m_W, m_H);
|
|
||||||
|
|
||||||
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;
|
PathGoal newGoal;
|
||||||
newGoal.type = PathGoal::POINT;
|
newGoal.type = PathGoal::POINT;
|
||||||
Pathfinding::NavcellCenter(iGoal, jGoal, newGoal.x, newGoal.z);
|
Pathfinding::NavcellCenter(bestI, bestJ, newGoal.x, newGoal.z);
|
||||||
goal = newGoal;
|
goal = newGoal;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HierarchicalPathfinder::FindNearestPassableNavcell(u16& i, u16& j, pass_class_t passClass)
|
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
|
* 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).
|
* @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
|
* If the goal is not reachable, it is replaced with a point goal nearest to
|
||||||
* at the nearest reachable navcell to the original goal's center.
|
* the goal center.
|
||||||
* Returns true if the goal was replaced.
|
*
|
||||||
|
* 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)
|
* 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;
|
bool RegionContainsGoal(u16 r, const PathGoal& goal) const;
|
||||||
|
|
||||||
void RegionNavcellNearest(u16 r, int iGoal, int jGoal, int& iBest, int& jBest, u32& dist2Best) 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;
|
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;
|
state.goal = origGoal;
|
||||||
|
|
||||||
if (state.goal.type != PathGoal::POINT)
|
// 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.
|
||||||
// Try to go to the nearest point on non-point goals.
|
m_PathfinderHier.MakeGoalReachable(i0, j0, state.goal, passClass);
|
||||||
// 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);
|
|
||||||
|
|
||||||
// If we're already at the goal tile, then move directly to the exact goal coordinates
|
// If we're already at the goal tile, then move directly to the exact goal coordinates
|
||||||
if (state.goal.NavcellContainsGoal(i0, j0))
|
if (state.goal.NavcellContainsGoal(i0, j0))
|
||||||
|
Loading…
Reference in New Issue
Block a user