Refactor movement cost method to avoid repeated terrain information lookups.

As the world tileset is fixed, the pathfinder can look up the terrain information for that tileset on creation. This is implemented by the WorldMovementInfo struct. When calculating node costs, this allows the pathfinder to avoid having to repeat this expensive dictionary lookup on every node.
This commit is contained in:
RoosterDragon
2015-08-19 23:50:36 +01:00
parent 4eacb6e5c9
commit ac1658c9ce
2 changed files with 32 additions and 17 deletions

View File

@@ -71,6 +71,7 @@ namespace OpenRA.Mods.Common.Pathfinder
readonly CellConditions checkConditions;
readonly MobileInfo mobileInfo;
readonly MobileInfo.WorldMovementInfo worldMovementInfo;
CellLayer<CellInfo> cellInfo;
public PathGraph(CellLayer<CellInfo> cellInfo, MobileInfo mobileInfo, Actor actor, World world, bool checkForBlocked)
@@ -78,6 +79,7 @@ namespace OpenRA.Mods.Common.Pathfinder
this.cellInfo = cellInfo;
World = world;
this.mobileInfo = mobileInfo;
worldMovementInfo = mobileInfo.GetWorldMovementInfo(world);
Actor = actor;
LaneBias = 1;
checkConditions = checkForBlocked ? CellConditions.TransientActors : CellConditions.None;
@@ -123,17 +125,9 @@ namespace OpenRA.Mods.Common.Pathfinder
int GetCostToNode(CPos destNode, CVec direction)
{
int movementCost;
if (mobileInfo.CanEnterCell(
World,
Actor,
destNode,
out movementCost,
IgnoredActor,
checkConditions) && !(CustomBlock != null && CustomBlock(destNode)))
{
var movementCost = mobileInfo.MovementCostToEnterCell(worldMovementInfo, Actor, destNode, IgnoredActor, checkConditions);
if (movementCost != int.MaxValue && !(CustomBlock != null && CustomBlock(destNode)))
return CalculateCellCost(destNode, direction, movementCost);
}
return Constants.InvalidNode;
}

View File

@@ -125,6 +125,17 @@ namespace OpenRA.Mods.Common.Traits
}
}
public struct WorldMovementInfo
{
internal readonly World World;
internal readonly TerrainInfo[] TerrainInfos;
internal WorldMovementInfo(World world, MobileInfo info)
{
World = world;
TerrainInfos = info.TilesetTerrainInfo[world.TileSet];
}
}
public readonly Cache<TileSet, TerrainInfo[]> TilesetTerrainInfo;
public readonly Cache<TileSet, int> TilesetMovementClass;
@@ -136,14 +147,19 @@ namespace OpenRA.Mods.Common.Traits
public int MovementCostForCell(World world, CPos cell)
{
if (!world.Map.Contains(cell))
return MovementCostForCell(world.Map, TilesetTerrainInfo[world.TileSet], cell);
}
int MovementCostForCell(Map map, TerrainInfo[] terrainInfos, CPos cell)
{
if (!map.Contains(cell))
return int.MaxValue;
var index = world.Map.GetTerrainIndex(cell);
var index = map.GetTerrainIndex(cell);
if (index == byte.MaxValue)
return int.MaxValue;
return TilesetTerrainInfo[world.TileSet][index].Cost;
return terrainInfos[index].Cost;
}
public int CalculateTilesetMovementClass(TileSet tileset)
@@ -236,12 +252,17 @@ namespace OpenRA.Mods.Common.Traits
return false;
}
public bool CanEnterCell(World world, Actor self, CPos cell, out int movementCost, Actor ignoreActor = null, CellConditions check = CellConditions.All)
public WorldMovementInfo GetWorldMovementInfo(World world)
{
if ((movementCost = MovementCostForCell(world, cell)) == int.MaxValue)
return false;
return new WorldMovementInfo(world, this);
}
return CanMoveFreelyInto(world, self, cell, ignoreActor, check);
public int MovementCostToEnterCell(WorldMovementInfo worldMovementInfo, Actor self, CPos cell, Actor ignoreActor = null, CellConditions check = CellConditions.All)
{
var cost = MovementCostForCell(worldMovementInfo.World.Map, worldMovementInfo.TerrainInfos, cell);
if (cost == int.MaxValue || !CanMoveFreelyInto(worldMovementInfo.World, self, cell, ignoreActor, check))
return int.MaxValue;
return cost;
}
public SubCell GetAvailableSubCell(