1
0
forked from 0ad/0ad

Min/max range support in pathfinder

This was SVN commit r7311.
This commit is contained in:
Ykkrosh 2010-02-06 20:58:48 +00:00
parent b21e798243
commit b25076ed4d
8 changed files with 96 additions and 38 deletions

View File

@ -13,7 +13,8 @@ function startMainMenu()
guiUnHide ("pg"); guiUnHide ("pg");
// Play main 0 A.D. theme when the main menu starts. // Play main 0 A.D. theme when the main menu starts.
curr_music = newRandomSound("music", "menu"); var curr_music = newRandomSound("music", "menu");
if (curr_music)
curr_music.loop(); curr_music.loop();
// Set starting volume (I'm using a value of zero here for no sound; feel free to comment out these two lines to use defaults). // Set starting volume (I'm using a value of zero here for no sound; feel free to comment out these two lines to use defaults).

View File

@ -55,7 +55,7 @@ UnitAI.prototype.Walk = function(x, z)
if (!motion) if (!motion)
return; return;
motion.MoveToPoint(x, z); motion.MoveToPoint(x, z, 0, 0);
this.state = STATE_WALKING; this.state = STATE_WALKING;
}; };
@ -96,7 +96,7 @@ UnitAI.prototype.MoveToTarget = function(target)
var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
var pos = cmpPositionTarget.GetPosition(); var pos = cmpPositionTarget.GetPosition();
cmpMotion.MoveToPoint(pos.x, pos.z); cmpMotion.MoveToPoint(pos.x, pos.z, 0, 8);
}; };
UnitAI.prototype.AttackTimeout = function(data) UnitAI.prototype.AttackTimeout = function(data)

View File

@ -32,7 +32,7 @@
// Set the maximum number of function arguments that can be handled // Set the maximum number of function arguments that can be handled
// (This should be as small as possible (for compiler efficiency), // (This should be as small as possible (for compiler efficiency),
// but as large as necessary for all wrapped functions) // but as large as necessary for all wrapped functions)
#define SCRIPT_INTERFACE_MAX_ARGS 4 #define SCRIPT_INTERFACE_MAX_ARGS 5
struct ScriptInterface_impl; struct ScriptInterface_impl;
class ScriptClass; class ScriptClass;

View File

@ -227,15 +227,15 @@ public:
virtual bool CanMoveStraight(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, u32& cost); virtual bool CanMoveStraight(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, u32& cost);
virtual void ComputePath(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, Path& ret); virtual void ComputePath(entity_pos_t x0, entity_pos_t z0, const Goal& goal, Path& ret);
virtual void SetDebugPath(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) virtual void SetDebugPath(entity_pos_t x0, entity_pos_t z0, const Goal& goal)
{ {
delete m_DebugGrid; delete m_DebugGrid;
m_DebugGrid = NULL; m_DebugGrid = NULL;
delete m_DebugPath; delete m_DebugPath;
m_DebugPath = new Path(); m_DebugPath = new Path();
ComputePath(x0, z0, x1, z1, *m_DebugPath); ComputePath(x0, z0, goal, *m_DebugPath);
} }
/** /**
@ -522,14 +522,24 @@ public:
// Calculate heuristic cost from tile i,j to destination // Calculate heuristic cost from tile i,j to destination
// (This ought to be an underestimate for correctness) // (This ought to be an underestimate for correctness)
static u32 CalculateHeuristic(u16 i, u16 j, u16 iTarget, u16 jTarget) static u32 CalculateHeuristic(u16 i, u16 j, u16 iGoal, u16 jGoal, u16 rGoal, bool aimingInwards)
{ {
#ifdef USE_DIAGONAL_MOVEMENT #ifdef USE_DIAGONAL_MOVEMENT
return hypot(i-iTarget, j-jTarget)*g_CostPerTile; CFixedVector2D pos (CFixed_23_8::FromInt(i), CFixed_23_8::FromInt(j));
// XXX: shouldn't use floats here CFixedVector2D goal (CFixed_23_8::FromInt(iGoal), CFixed_23_8::FromInt(jGoal));
// Also, the heuristic should match the costs better CFixed_23_8 dist = (pos - goal).Length();
// TODO: the heuristic could match the costs better - it's not really Euclidean movement
CFixed_23_8 rdist = dist - CFixed_23_8::FromInt(rGoal);
if (!aimingInwards)
rdist = -rdist;
if (rdist < CFixed_23_8::FromInt(0))
return 0;
return (rdist * g_CostPerTile).ToInt_RoundToZero();
#else #else
return (abs((int)i - (int)iTarget) + abs((int)j - (int)jTarget)) * g_CostPerTile; return (abs((int)i - (int)iGoal) + abs((int)j - (int)jGoal)) * g_CostPerTile;
#endif #endif
} }
@ -568,10 +578,12 @@ struct PathfinderState
{ {
u32 steps; // number of algorithm iterations u32 steps; // number of algorithm iterations
u16 iTarget, jTarget; // goal tile u16 iGoal, jGoal; // goal tile
u16 rGoal; // radius of goal (around tile center)
bool aimingInwards; // whether we're moving towards the goal or away
PriorityQueue open; PriorityQueue open;
PriorityQueue closed; // (there's no explicit closed list; it's encoded in PathfindTile::status)
Grid<PathfindTile>* tiles; Grid<PathfindTile>* tiles;
Grid<u8>* terrain; Grid<u8>* terrain;
@ -587,7 +599,7 @@ static void ProcessNeighbour(u16 pi, u16 pj, u16 i, u16 j, u32 pg, PathfinderSta
if (state.terrain->get(i, j)) if (state.terrain->get(i, j))
return; return;
u32 h = CalculateHeuristic(i, j, state.iTarget, state.jTarget); u32 h = CalculateHeuristic(i, j, state.iGoal, state.jGoal, state.rGoal, state.aimingInwards);
u32 dg = CalculateCostDelta(pi, pj, i, j, state.tiles); u32 dg = CalculateCostDelta(pi, pj, i, j, state.tiles);
u32 g = pg + dg; // cost to this tile = cost to predecessor + delta from predecessor u32 g = pg + dg; // cost to this tile = cost to predecessor + delta from predecessor
@ -624,7 +636,6 @@ static void ProcessNeighbour(u16 pi, u16 pj, u16 i, u16 j, u32 pg, PathfinderSta
// If this is a better path (possible when we use inadmissible heuristics), reopen it // If this is a better path (possible when we use inadmissible heuristics), reopen it
if (g < n.cost) if (g < n.cost)
{ {
state.closed.remove(i, j);
// (don't return yet) // (don't return yet)
} }
else else
@ -643,7 +654,23 @@ static void ProcessNeighbour(u16 pi, u16 pj, u16 i, u16 j, u32 pg, PathfinderSta
state.open.push(t); state.open.push(t);
} }
void CCmpPathfinder::ComputePath(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, Path& path) static bool AtGoal(u16 i, u16 j, u16 iGoal, u16 jGoal, u16 rGoal, bool aimingInwards)
{
// If we're aiming towards a point, stop when we get there
if (aimingInwards && rGoal == 0)
return (i == iGoal && j == jGoal);
// Otherwise compute the distance and compare to desired radius
i32 dist2 = ((i32)i-iGoal)*((i32)i-iGoal) + ((i32)j-jGoal)*((i32)j-jGoal);
if (aimingInwards && (dist2 <= rGoal*rGoal))
return true;
if (!aimingInwards && (dist2 >= rGoal*rGoal))
return true;
return false;
}
void CCmpPathfinder::ComputePath(entity_pos_t x0, entity_pos_t z0, const Goal& goal, Path& path)
{ {
UpdateGrid(); UpdateGrid();
@ -651,9 +678,29 @@ void CCmpPathfinder::ComputePath(entity_pos_t x0, entity_pos_t z0, entity_pos_t
PathfinderState state; PathfinderState state;
// Convert the start/end coordinates to tile indexes
u16 i0, j0; u16 i0, j0;
NearestTile(x0, z0, i0, j0); NearestTile(x0, z0, i0, j0);
NearestTile(x1, z1, state.iTarget, state.jTarget); NearestTile(goal.x, goal.z, state.iGoal, state.jGoal);
// If we start closer than min radius, aim for the min radius
// If we start further than max radius, aim for the max radius
// Otherwise we're there already
CFixed_23_8 initialDist = (CFixedVector2D(x0, z0) - CFixedVector2D(goal.x, goal.z)).Length();
if (initialDist < goal.minRadius)
{
state.aimingInwards = false;
state.rGoal = (goal.minRadius / CELL_SIZE).ToInt_RoundToZero(); // TODO: what rounding mode is appropriate?
}
else if (initialDist > goal.maxRadius)
{
state.aimingInwards = true;
state.rGoal = (goal.maxRadius / CELL_SIZE).ToInt_RoundToZero(); // TODO: what rounding mode is appropriate?
}
else
{
return;
}
state.steps = 0; state.steps = 0;
@ -662,7 +709,7 @@ void CCmpPathfinder::ComputePath(entity_pos_t x0, entity_pos_t z0, entity_pos_t
state.iBest = i0; state.iBest = i0;
state.jBest = j0; state.jBest = j0;
state.hBest = CalculateHeuristic(i0, j0, state.iTarget, state.jTarget); state.hBest = CalculateHeuristic(i0, j0, state.iGoal, state.jGoal, state.rGoal, state.aimingInwards);
QueueItem start = { i0, j0, 0 }; QueueItem start = { i0, j0, 0 };
state.open.push(start); state.open.push(start);
@ -687,11 +734,10 @@ void CCmpPathfinder::ComputePath(entity_pos_t x0, entity_pos_t z0, entity_pos_t
// Move best tile from open to closed // Move best tile from open to closed
QueueItem curr = state.open.top(); QueueItem curr = state.open.top();
state.open.pop(); state.open.pop();
state.closed.push(curr);
state.tiles->get(curr.i, curr.j).status = PathfindTile::STATUS_CLOSED; state.tiles->get(curr.i, curr.j).status = PathfindTile::STATUS_CLOSED;
// If we've reached the destination, stop // If we've reached the destination, stop
if (curr.i == state.iTarget && curr.j == state.jTarget) if (AtGoal(curr.i, curr.j, state.iGoal, state.jGoal, state.rGoal, state.aimingInwards))
{ {
state.iBest = curr.i; state.iBest = curr.i;
state.jBest = curr.j; state.jBest = curr.j;

View File

@ -91,7 +91,7 @@ public:
} }
} }
virtual void MoveToPoint(entity_pos_t x, entity_pos_t z) virtual void MoveToPoint(entity_pos_t x, entity_pos_t z, entity_pos_t minRadius, entity_pos_t maxRadius)
{ {
CmpPtr<ICmpPathfinder> cmpPathfinder (*m_Context, SYSTEM_ENTITY); CmpPtr<ICmpPathfinder> cmpPathfinder (*m_Context, SYSTEM_ENTITY);
if (cmpPathfinder.null()) if (cmpPathfinder.null())
@ -105,18 +105,23 @@ public:
m_Path.m_Waypoints.clear(); m_Path.m_Waypoints.clear();
u32 cost; // u32 cost;
entity_pos_t r = entity_pos_t::FromInt(0); // TODO: should get this from the entity's size // entity_pos_t r = entity_pos_t::FromInt(0); // TODO: should get this from the entity's size
if (cmpPathfinder->CanMoveStraight(pos.X, pos.Z, x, z, r, cost)) // if (cmpPathfinder->CanMoveStraight(pos.X, pos.Z, x, z, r, cost))
// {
// m_TargetX = x;
// m_TargetZ = z;
// m_HasTarget = true;
// }
// else
{ {
m_TargetX = x; ICmpPathfinder::Goal goal;
m_TargetZ = z; goal.x = x;
m_HasTarget = true; goal.z = z;
} goal.minRadius = minRadius;
else goal.maxRadius = maxRadius;
{ cmpPathfinder->SetDebugPath(pos.X, pos.Z, goal);
cmpPathfinder->SetDebugPath(pos.X, pos.Z, x, z); cmpPathfinder->ComputePath(pos.X, pos.Z, goal, m_Path);
cmpPathfinder->ComputePath(pos.X, pos.Z, x, z, m_Path);
if (!m_Path.m_Waypoints.empty()) if (!m_Path.m_Waypoints.empty())
PickNextWaypoint(pos); PickNextWaypoint(pos);
} }

View File

@ -35,6 +35,12 @@
class ICmpPathfinder : public IComponent class ICmpPathfinder : public IComponent
{ {
public: public:
struct Goal
{
entity_pos_t x, z;
entity_pos_t minRadius, maxRadius;
};
/** /**
* Returned paths are currently represented as a series of waypoints. * Returned paths are currently represented as a series of waypoints.
* These happen to correspond to the centers of horizontally/vertically adjacent tiles * These happen to correspond to the centers of horizontally/vertically adjacent tiles
@ -82,12 +88,12 @@ public:
/** /**
* Compute a path between the given points, and return the set of waypoints. * Compute a path between the given points, and return the set of waypoints.
*/ */
virtual void ComputePath(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, Path& ret) = 0; virtual void ComputePath(entity_pos_t x0, entity_pos_t z0, const Goal& goal, Path& ret) = 0;
/** /**
* Compute a path between the given points, and draw the latest such path as a terrain overlay. * Compute a path between the given points, and draw the latest such path as a terrain overlay.
*/ */
virtual void SetDebugPath(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) = 0; virtual void SetDebugPath(entity_pos_t x0, entity_pos_t z0, const Goal& goal) = 0;
DECLARE_INTERFACE_TYPE(Pathfinder) DECLARE_INTERFACE_TYPE(Pathfinder)
}; };

View File

@ -22,5 +22,5 @@
#include "simulation2/system/InterfaceScripted.h" #include "simulation2/system/InterfaceScripted.h"
BEGIN_INTERFACE_WRAPPER(UnitMotion) BEGIN_INTERFACE_WRAPPER(UnitMotion)
DEFINE_INTERFACE_METHOD_2("MoveToPoint", void, ICmpUnitMotion, MoveToPoint, entity_pos_t, entity_pos_t) DEFINE_INTERFACE_METHOD_4("MoveToPoint", void, ICmpUnitMotion, MoveToPoint, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t)
END_INTERFACE_WRAPPER(UnitMotion) END_INTERFACE_WRAPPER(UnitMotion)

View File

@ -34,7 +34,7 @@
class ICmpUnitMotion : public IComponent class ICmpUnitMotion : public IComponent
{ {
public: public:
virtual void MoveToPoint(entity_pos_t x, entity_pos_t z) = 0; virtual void MoveToPoint(entity_pos_t x, entity_pos_t z, entity_pos_t minRadius, entity_pos_t maxRadius) = 0;
DECLARE_INTERFACE_TYPE(UnitMotion) DECLARE_INTERFACE_TYPE(UnitMotion)
}; };