Use CellLayer for pathfinding.

This commit is contained in:
Paul Chote
2014-05-17 14:12:43 +12:00
parent 435f47158f
commit 441971f6d7
2 changed files with 42 additions and 39 deletions

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using OpenRA;
using OpenRA.Primitives;
using OpenRA.Support;
using OpenRA.Traits;
@@ -136,9 +137,7 @@ namespace OpenRA.Mods.RA.Move
var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>();
if (dbg != null)
{
dbg.AddLayer(search.Considered.Select(p => new Pair<CPos, int>(p, search.CellInfo[p.X, p.Y].MinCost)), search.MaxCost, search.Owner);
}
dbg.AddLayer(search.Considered.Select(p => new Pair<CPos, int>(p, search.CellInfo[p].MinCost)), search.MaxCost, search.Owner);
if (path != null)
return path;
@@ -149,15 +148,15 @@ namespace OpenRA.Mods.RA.Move
}
}
static List<CPos> MakePath(CellInfo[,] cellInfo, CPos destination)
static List<CPos> MakePath(CellLayer<CellInfo> cellInfo, CPos destination)
{
var ret = new List<CPos>();
var pathNode = destination;
while (cellInfo[pathNode.X, pathNode.Y].Path != pathNode)
while (cellInfo[pathNode].Path != pathNode)
{
ret.Add(pathNode);
pathNode = cellInfo[pathNode.X, pathNode.Y].Path;
pathNode = cellInfo[pathNode].Path;
}
ret.Add(pathNode);
@@ -165,9 +164,8 @@ namespace OpenRA.Mods.RA.Move
return ret;
}
public List<CPos> FindBidiPath( /* searches from both ends toward each other */
PathSearch fromSrc,
PathSearch fromDest)
// Searches from both ends toward each other
public List<CPos> FindBidiPath(PathSearch fromSrc, PathSearch fromDest)
{
using (new PerfSample("Pathfinder"))
{
@@ -181,8 +179,8 @@ namespace OpenRA.Mods.RA.Move
/* make some progress on the first search */
var p = fromSrc.Expand(world);
if (fromDest.CellInfo[p.X, p.Y].Seen &&
fromDest.CellInfo[p.X, p.Y].MinCost < float.PositiveInfinity)
if (fromDest.CellInfo[p].Seen &&
fromDest.CellInfo[p].MinCost < float.PositiveInfinity)
{
path = MakeBidiPath(fromSrc, fromDest, p);
break;
@@ -191,8 +189,8 @@ namespace OpenRA.Mods.RA.Move
/* make some progress on the second search */
var q = fromDest.Expand(world);
if (fromSrc.CellInfo[q.X, q.Y].Seen &&
fromSrc.CellInfo[q.X, q.Y].MinCost < float.PositiveInfinity)
if (fromSrc.CellInfo[q].Seen &&
fromSrc.CellInfo[q].MinCost < float.PositiveInfinity)
{
path = MakeBidiPath(fromSrc, fromDest, q);
break;
@@ -202,8 +200,8 @@ namespace OpenRA.Mods.RA.Move
var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>();
if (dbg != null)
{
dbg.AddLayer(fromSrc.Considered.Select(p => new Pair<CPos, int>(p, fromSrc.CellInfo[p.X, p.Y].MinCost)), fromSrc.MaxCost, fromSrc.Owner);
dbg.AddLayer(fromDest.Considered.Select(p => new Pair<CPos, int>(p, fromDest.CellInfo[p.X, p.Y].MinCost)), fromDest.MaxCost, fromDest.Owner);
dbg.AddLayer(fromSrc.Considered.Select(p => new Pair<CPos, int>(p, fromSrc.CellInfo[p].MinCost)), fromSrc.MaxCost, fromSrc.Owner);
dbg.AddLayer(fromDest.Considered.Select(p => new Pair<CPos, int>(p, fromDest.CellInfo[p].MinCost)), fromDest.MaxCost, fromDest.Owner);
}
if (path != null)
@@ -222,19 +220,19 @@ namespace OpenRA.Mods.RA.Move
var ret = new List<CPos>();
var q = p;
while (ca[q.X, q.Y].Path != q)
while (ca[q].Path != q)
{
ret.Add(q);
q = ca[q.X, q.Y].Path;
q = ca[q].Path;
}
ret.Add(q);
ret.Reverse();
q = p;
while (cb[q.X, q.Y].Path != q)
while (cb[q].Path != q)
{
q = cb[q.X, q.Y].Path;
q = cb[q].Path;
ret.Add(q);
}

View File

@@ -10,14 +10,16 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA;
using OpenRA.Primitives;
namespace OpenRA.Mods.RA.Move
{
public sealed class PathSearch : IDisposable
{
public CellInfo[,] CellInfo;
public CellLayer<CellInfo> CellInfo;
public PriorityQueue<PathDistance> Queue;
public Func<CPos, int> Heuristic;
public bool CheckForBlocked;
@@ -141,7 +143,7 @@ namespace OpenRA.Mods.RA.Move
public CPos Expand(World world)
{
var p = Queue.Pop();
while (CellInfo[p.Location.X, p.Location.Y].Seen)
while (CellInfo[p.Location].Seen)
{
if (Queue.Empty)
return p.Location;
@@ -149,7 +151,9 @@ namespace OpenRA.Mods.RA.Move
p = Queue.Pop();
}
CellInfo[p.Location.X, p.Location.Y].Seen = true;
var pCell = CellInfo[p.Location];
pCell.Seen = true;
CellInfo[p.Location] = pCell;
var thisCost = mobileInfo.MovementCostForCell(world, p.Location);
@@ -177,7 +181,7 @@ namespace OpenRA.Mods.RA.Move
if (!world.Map.IsInMap(newHere))
continue;
if (CellInfo[newHere.X, newHere.Y].Seen)
if (CellInfo[newHere].Seen)
continue;
// Now we may seriously consider this direction using heuristics:
@@ -224,14 +228,16 @@ namespace OpenRA.Mods.RA.Move
cellCost += laneBias;
}
var newCost = CellInfo[p.Location.X, p.Location.Y].MinCost + cellCost;
var newCost = CellInfo[p.Location].MinCost + cellCost;
// Cost is even higher; next direction:
if (newCost > CellInfo[newHere.X, newHere.Y].MinCost)
if (newCost > CellInfo[newHere].MinCost)
continue;
CellInfo[newHere.X, newHere.Y].Path = p.Location;
CellInfo[newHere.X, newHere.Y].MinCost = newCost;
var hereCell = CellInfo[newHere];
hereCell.Path = p.Location;
hereCell.MinCost = newCost;
CellInfo[newHere] = hereCell;
nextDirections[i].Second = newCost + est;
Queue.Add(new PathDistance(newCost + est, newHere));
@@ -252,27 +258,28 @@ namespace OpenRA.Mods.RA.Move
if (!self.World.Map.IsInMap(location))
return;
CellInfo[location.X, location.Y] = new CellInfo(0, location, false);
CellInfo[location] = new CellInfo(0, location, false);
Queue.Add(new PathDistance(Heuristic(location), location));
}
static readonly Queue<CellInfo[,]> CellInfoPool = new Queue<CellInfo[,]>();
static readonly Queue<CellLayer<CellInfo>> CellInfoPool = new Queue<CellLayer<CellInfo>>();
static CellInfo[,] GetFromPool()
static CellLayer<CellInfo> GetFromPool()
{
lock (CellInfoPool)
return CellInfoPool.Dequeue();
}
static void PutBackIntoPool(CellInfo[,] ci)
static void PutBackIntoPool(CellLayer<CellInfo> ci)
{
lock (CellInfoPool)
CellInfoPool.Enqueue(ci);
}
CellInfo[,] InitCellInfo()
CellLayer<CellInfo> InitCellInfo()
{
CellInfo[,] result = null;
CellLayer<CellInfo> result = null;
var mapSize = new Size(self.World.Map.MapSize.X, self.World.Map.MapSize.Y);
// HACK: Uses a static cache so that double-ended searches (which have two PathSearch instances)
// can implicitly share data. The PathFinder should allocate the CellInfo array and pass it
@@ -280,8 +287,7 @@ namespace OpenRA.Mods.RA.Move
while (CellInfoPool.Count > 0)
{
var cellInfo = GetFromPool();
if (cellInfo.GetUpperBound(0) != self.World.Map.MapSize.X - 1 ||
cellInfo.GetUpperBound(1) != self.World.Map.MapSize.Y - 1)
if (cellInfo.Size != mapSize)
{
Log.Write("debug", "Discarding old pooled CellInfo of wrong size.");
continue;
@@ -292,11 +298,10 @@ namespace OpenRA.Mods.RA.Move
}
if (result == null)
result = new CellInfo[self.World.Map.MapSize.X, self.World.Map.MapSize.Y];
result = new CellLayer<CellInfo>(self.World.Map);
for (var x = 0; x < self.World.Map.MapSize.X; x++)
for (var y = 0; y < self.World.Map.MapSize.Y; y++)
result[x, y] = new CellInfo(int.MaxValue, new CPos(x, y), false);
foreach (var cell in self.World.Map.Cells)
result[cell] = new CellInfo(int.MaxValue, cell, false);
return result;
}