Min/max range support in pathfinder
This was SVN commit r7311.
This commit is contained in:
parent
b21e798243
commit
b25076ed4d
@ -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).
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
};
|
};
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user