Merge pull request #2738 from Mailaender/a-star

A-star debug overlay
This commit is contained in:
Chris Forbes
2013-03-10 16:20:34 -07:00
11 changed files with 254 additions and 18 deletions

View File

@@ -0,0 +1,67 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Drawing;
using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
public class QuadRenderer : Renderer.IBatchRenderer
{
Renderer renderer;
IShader shader;
Vertex[] vertices = new Vertex[Renderer.TempBufferSize];
int nv = 0;
public QuadRenderer(Renderer renderer, IShader shader)
{
this.renderer = renderer;
this.shader = shader;
}
public void Flush()
{
if (nv > 0)
{
shader.Render(() =>
{
var vb = renderer.GetTempVertexBuffer();
vb.SetData(vertices, nv);
renderer.DrawBatch(vb, 0, nv, PrimitiveType.QuadList);
});
nv = 0;
}
}
public void FillRect(RectangleF r, Color color)
{
Renderer.CurrentBatchRenderer = this;
if (nv + 4 > Renderer.TempBufferSize)
Flush();
vertices[nv] = new Vertex(new float2(r.Left, r.Top), new float2(color.R / 255.0f, color.G / 255.0f), new float2(color.B / 255.0f, color.A / 255.0f));
vertices[nv + 1] = new Vertex(new float2(r.Right, r.Top), new float2(color.R / 255.0f, color.G / 255.0f), new float2(color.B / 255.0f, color.A / 255.0f));
vertices[nv + 2] = new Vertex(new float2(r.Right, r.Bottom), new float2(color.R / 255.0f, color.G / 255.0f), new float2(color.B / 255.0f, color.A / 255.0f));
vertices[nv + 3] = new Vertex(new float2(r.Left, r.Bottom), new float2(color.R / 255.0f, color.G / 255.0f), new float2(color.B / 255.0f, color.A / 255.0f));
nv += 4;
}
public void SetShaderParams(ITexture palette, Size screen, float zoom, float2 scroll)
{
shader.SetVec("Scroll", (int)scroll.X, (int)scroll.Y);
shader.SetVec("r1", zoom*2f/screen.Width, -zoom*2f/screen.Height);
shader.SetVec("r2", -1, 1);
}
}
}

View File

@@ -28,6 +28,7 @@ namespace OpenRA.Graphics
internal static int TempBufferCount;
public SpriteRenderer WorldSpriteRenderer { get; private set; }
public QuadRenderer WorldQuadRenderer { get; private set; }
public LineRenderer WorldLineRenderer { get; private set; }
public LineRenderer LineRenderer { get; private set; }
public SpriteRenderer RgbaSpriteRenderer { get; private set; }
@@ -48,6 +49,7 @@ namespace OpenRA.Graphics
WorldSpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp"));
WorldLineRenderer = new LineRenderer(this, device.CreateShader("line"));
LineRenderer = new LineRenderer(this, device.CreateShader("line"));
WorldQuadRenderer = new QuadRenderer(this, device.CreateShader("line"));
RgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba"));
SpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp"));
@@ -67,6 +69,7 @@ namespace OpenRA.Graphics
device.Clear();
WorldSpriteRenderer.SetShaderParams(PaletteTexture, Resolution, zoom, scroll);
WorldLineRenderer.SetShaderParams(PaletteTexture, Resolution, zoom, scroll);
WorldQuadRenderer.SetShaderParams(PaletteTexture, Resolution, zoom, scroll);
SpriteRenderer.SetShaderParams(PaletteTexture, Resolution, 1f, float2.Zero);
LineRenderer.SetShaderParams(PaletteTexture, Resolution, 1f, float2.Zero);
RgbaSpriteRenderer.SetShaderParams(PaletteTexture, Resolution, 1f, float2.Zero);

View File

@@ -80,6 +80,7 @@
<Compile Include="ActorInitializer.cs" />
<Compile Include="ActorMap.cs" />
<Compile Include="ActorReference.cs" />
<Compile Include="Graphics\QuadRenderer.cs" />
<Compile Include="PSubVec.cs" />
<Compile Include="PSubPos.cs" />
<Compile Include="PVecInt.cs" />

View File

@@ -49,8 +49,9 @@ namespace OpenRA.Mods.RA.Move
{
this.getPath = (self,mobile) =>
self.World.WorldActor.Trait<PathFinder>().FindPath(
PathSearch.FromPoint( self.World, mobile.Info, self, mobile.toCell, destination, false )
.WithIgnoredBuilding( ignoreBuilding ));
PathSearch.FromPoint(self.World, mobile.Info, self, mobile.toCell, destination, false)
.WithIgnoredBuilding(ignoreBuilding)
);
this.destination = destination;
this.nearEnough = 0;

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Support;
using OpenRA.Traits;
@@ -89,13 +90,29 @@ namespace OpenRA.Mods.RA.Move
using (new PerfSample("Pathfinder"))
{
using (search)
{
List<CPos> path = null;
while (!search.queue.Empty)
{
var p = search.Expand(world);
if (search.heuristic(p) == 0)
return MakePath(search.cellInfo, p);
{
path = MakePath(search.cellInfo, p);
break;
}
}
var dbg = world.WorldActor.TraitOrDefault<DebugOverlay>();
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);
}
if (path != null)
return path;
}
// no path exists
return new List<CPos>(0);
}
@@ -125,21 +142,43 @@ namespace OpenRA.Mods.RA.Move
{
using (fromSrc)
using (fromDest)
{
List<CPos> path = null;
while (!fromSrc.queue.Empty && !fromDest.queue.Empty)
{
/* 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)
return MakeBidiPath(fromSrc, fromDest, p);
if (fromDest.cellInfo[p.X, p.Y].Seen &&
fromDest.cellInfo[p.X, p.Y].MinCost < float.PositiveInfinity)
{
path = MakeBidiPath(fromSrc, fromDest, p);
break;
}
/* 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)
return MakeBidiPath(fromSrc, fromDest, q);
if (fromSrc.cellInfo[q.X, q.Y].Seen &&
fromSrc.cellInfo[q.X, q.Y].MinCost < float.PositiveInfinity)
{
path = MakeBidiPath(fromSrc, fromDest, q);
break;
}
}
var dbg = world.WorldActor.TraitOrDefault<DebugOverlay>();
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);
}
if (path != null)
return path;
}
return new List<CPos>(0);
}
}

View File

@@ -10,6 +10,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Traits;
@@ -26,7 +27,9 @@ namespace OpenRA.Mods.RA.Move
public bool checkForBlocked;
public Actor ignoreBuilding;
public bool inReverse;
public HashSet<CPos> considered;
public int maxCost;
Pair<CVec, int>[] nextDirections;
MobileInfo mobileInfo;
Actor self;
public Player owner { get { return self.Owner; } }
@@ -39,6 +42,9 @@ namespace OpenRA.Mods.RA.Move
this.self = self;
customCost = null;
queue = new PriorityQueue<PathDistance>();
considered = new HashSet<CPos>();
maxCost = 0;
nextDirections = directions.Select(d => new Pair<CVec, int>(d, 0)).ToArray();
}
public PathSearch InReverse()
@@ -108,14 +114,23 @@ namespace OpenRA.Mods.RA.Move
return p.Location;
}
foreach( CVec d in directions )
// This current cell is ok; check all immediate directions:
considered.Add(p.Location);
for (int i = 0; i < nextDirections.Length; ++i)
{
CVec d = nextDirections[i].First;
nextDirections[i].Second = int.MaxValue;
CPos newHere = p.Location + d;
if (!world.Map.IsInMap(newHere.X, newHere.Y)) continue;
// Is this direction flat-out unusable or already seen?
if (!world.Map.IsInMap(newHere.X, newHere.Y))
continue;
if (cellInfo[newHere.X, newHere.Y].Seen)
continue;
// Now we may seriously consider this direction using heuristics:
var costHere = mobileInfo.MovementCostForCell(world, newHere);
if (costHere == int.MaxValue)
@@ -134,8 +149,12 @@ namespace OpenRA.Mods.RA.Move
int cellCost = costHere;
if (d.X * d.Y != 0) cellCost = (cellCost * 34) / 24;
int userCost = 0;
if (customCost != null)
cellCost += customCost(newHere);
{
userCost = customCost(newHere);
cellCost += userCost;
}
// directional bonuses for smoother flow!
if (LaneBias != 0)
@@ -151,14 +170,23 @@ namespace OpenRA.Mods.RA.Move
int newCost = cellInfo[p.Location.X, p.Location.Y].MinCost + cellCost;
if (newCost >= cellInfo[newHere.X, newHere.Y].MinCost)
// Cost is even higher; next direction:
if (newCost > cellInfo[newHere.X, newHere.Y].MinCost)
continue;
cellInfo[newHere.X, newHere.Y].Path = p.Location;
cellInfo[newHere.X, newHere.Y].MinCost = newCost;
nextDirections[i].Second = newCost + est;
queue.Add(new PathDistance(newCost + est, newHere));
if (newCost > maxCost) maxCost = newCost;
considered.Add(newHere);
}
// Sort to prefer the cheaper direction:
//Array.Sort(nextDirections, (a, b) => a.Second.CompareTo(b.Second));
return p.Location;
}
@@ -263,10 +291,11 @@ namespace OpenRA.Mods.RA.Move
{
return here =>
{
CVec d = (here - destination).Abs();
int diag = Math.Min(d.X, d.Y);
int straight = Math.Abs(d.X - d.Y);
return (3400 * diag / 24) + (100 * straight);
int diag = Math.Min(Math.Abs(here.X - destination.X), Math.Abs(here.Y - destination.Y));
int straight = (Math.Abs(here.X - destination.X) + Math.Abs(here.Y - destination.Y));
int h = (3400 * diag / 24) + 100 * (straight - (2 * diag));
h = (int)(h * 1.001);
return h;
};
}

View File

@@ -403,6 +403,7 @@
<Compile Include="Widgets\WorldCommandWidget.cs" />
<Compile Include="Widgets\WorldTooltipWidget.cs" />
<Compile Include="World\ChooseBuildTabOnSelect.cs" />
<Compile Include="World\DebugOverlay.cs" />
<Compile Include="World\ResourceClaim.cs" />
<Compile Include="World\ResourceClaimLayer.cs" />
<Compile Include="World\PlayMusicOnMapLoad.cs" />

View File

@@ -59,6 +59,11 @@ namespace OpenRA.Mods.RA.Widgets.Logic
widget.Get<ButtonWidget>("RESET_EXPLORATION").OnClick = () =>
world.IssueOrder(new Order("DevResetExploration", world.LocalPlayer.PlayerActor, false));
var dbgOverlay = world.WorldActor.TraitOrDefault<DebugOverlay>();
var showAstarCostCheckbox = widget.Get<CheckboxWidget>("SHOW_ASTAR");
showAstarCostCheckbox.IsChecked = () => dbgOverlay != null ? dbgOverlay.Visible : false;
showAstarCostCheckbox.OnClick = () => { if (dbgOverlay != null) dbgOverlay.Visible ^= true; };
widget.Get<ButtonWidget>("CLOSE").OnClick = () => { Ui.CloseWindow(); onExit(); };
}

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
class DebugOverlayInfo : Traits.TraitInfo<DebugOverlay>
{
}
class DebugOverlay : IRenderOverlay, IWorldLoaded
{
Dictionary<Player, int[,]> layers;
int refreshTick;
World world;
public bool Visible;
public void WorldLoaded(World w)
{
this.world = w;
this.refreshTick = 0;
this.layers = new Dictionary<Player, int[,]>(8);
// Enabled via Cheats menu
this.Visible = false;
}
public void AddLayer(IEnumerable<Pair<CPos, int>> cellWeights, int maxWeight, Player pl)
{
if (maxWeight == 0) return;
int[,] layer;
if (!layers.TryGetValue(pl, out layer))
{
layer = new int[world.Map.MapSize.X, world.Map.MapSize.Y];
layers.Add(pl, layer);
}
foreach (var p in cellWeights)
layer[p.First.X, p.First.Y] = Math.Min(128, (layer[p.First.X, p.First.Y]) + ((maxWeight - p.Second) * 64 / maxWeight));
}
public void Render(WorldRenderer wr)
{
if (!Visible) return;
var qr = Game.Renderer.WorldQuadRenderer;
bool doDim = refreshTick - world.FrameNumber <= 0;
if (doDim) refreshTick = world.FrameNumber + 20;
var viewBounds = Game.viewport.WorldBounds(world);
var mapBounds = world.Map.Bounds;
foreach (var pair in layers)
{
Color c = (pair.Key != null) ? pair.Key.ColorRamp.GetColor(0f) : Color.PaleTurquoise;
var layer = pair.Value;
for (int j = mapBounds.Top; j <= mapBounds.Bottom; ++j)
for (int i = mapBounds.Left; i <= mapBounds.Right; ++i)
{
if (layer[i, j] <= 0) continue;
var w = Math.Max(0, Math.Min(layer[i, j], 128));
if (doDim)
{
layer[i, j] = layer[i, j] * 5 / 6;
}
if (!viewBounds.Contains(i, j)) continue;
// Only render quads in viewing range:
var ploc = new CPos(i, j).ToPPos();
qr.FillRect(new RectangleF(ploc.X, ploc.Y, Game.CellSize, Game.CellSize), Color.FromArgb(w, c));
}
}
}
}
}

View File

@@ -3,7 +3,7 @@ Background@CHEATS_PANEL:
X:(WINDOW_RIGHT - WIDTH)/2
Y:(WINDOW_BOTTOM - HEIGHT)/2
Width:350
Height:420
Height:450
Visible:true
Children:
Label@LABEL_TITLE:
@@ -75,6 +75,12 @@ Background@CHEATS_PANEL:
Width:PARENT_RIGHT - 30
Height:20
Text:Build Anywhere
Checkbox@SHOW_ASTAR:
X:30
Y:320
Width:PARENT_RIGHT - 30
Height:20
Text:Show A* Cost
Button@CLOSE:
X:30
Y:360

View File

@@ -455,7 +455,7 @@ Player:
BaseAttackNotifier:
PlayerStatistics:
World:
World:
OpenWidgetAtGameStart:
Widget: INGAME_ROOT
ObserverWidget: OBSERVER_ROOT
@@ -562,6 +562,7 @@ World:
Type:Crater
Types:cr1,cr2,cr3,cr4,cr5,cr6
Depths:5,5,5,5,5,5
DebugOverlay:
SpawnMapActors:
CreateMPPlayers:
MPStartLocations: