所以我试图在LibGDX中实现一个A * PathFinding算法,这样我就可以了解它是如何工作的。我的实施基于此写作:http://www.policyalmanac.org/games/aStarTutorial.htm
目前,我已经获得了计算路径的算法,如果目标不可访问,甚至会返回不存在的路径,但是如果墙壁部分阻挡目标,找到的路径所采用的路线实际上是不寻常的正如你在这里看到的那样:
所以我想知道的是我的实现有什么问题,看看为什么闭包列表会产生这样的路径,或者我错过了一步。
这些是PathFinder类中的变量:
public class PathFinder {
private Array<Array<GridNode>> grid = new Array<Array<GridNode>>();
private Array<GridNode> openPath = new Array<GridNode>();
private Array<GridNode> closedPath = new Array<GridNode>();
private int GridX, GridY;
private int StartX = -1, StartY = -1;
private int EndX = -1, EndY = -1;
public static int Found = 1;
public static int NonExistant = 2;
这是PathFinder类中的Path Finding代码:
public int findPath()
{
//Clear current path
openPath.clear();
closedPath.clear();
//Reset all F, G and H values to 0
for (int y = 0; y < grid.size; y++)
{
for (int x = 0; x < grid.get(y).size; x++)
{
grid.get(y).get(x).Reset();
}
}
//If no Start or End nodes have been set, quit the findPath.
if (StartX == -1 || StartY == -1 || EndX == -1 || EndY == -1)
{
return NonExistant;
}
else if (StartX == EndX && StartY == EndY) // If Start = End, return found.
{
return Found;
}
else
{
openPath.add(grid.get(StartY).get(StartX)); //Add Start node to open
SetOpenList(StartX, StartY); //Set neighbours.
closedPath.add(grid.get(StartY).get(StartX)); //Add Start node to closed.
openPath.removeValue(grid.get(StartY).get(StartX),true); //Remove Start Node from open.
while (closedPath.get(closedPath.size - 1) != grid.get(EndY).get(EndX))
{
//Set Neighbours for parent
SetOpenList(closedPath.get(closedPath.size-1).X/GridX, closedPath.get(closedPath.size-1).Y/GridY);
if (openPath.size != 0)
{
int bestF = 10000;
int bestFIndex = -1;
//Get node with lowest F in open.
for (int i = 0; i < openPath.size; i++)
{
if (openPath.get(i).F < bestF)
{
bestF = openPath.get(i).F;
bestFIndex = i;
}
}
if (bestFIndex != -1)
{
closedPath.add(openPath.get(bestFIndex)); //Add node to closed list
openPath.removeIndex(bestFIndex); //remove from open list
}
else
return NonExistant;
}
else
{
return NonExistant;
}
}
}
return Found;
}
public void CompareParentwithOpen(GridNode Parent, GridNode Open)
{
//If the G value of open is smaller than the G value in Parent, recalculate F, G and H.
if (Open.G < Parent.G)
{
openPath.get(
openPath.indexOf(Open, true)).CalculateNode(
Parent,
grid.get(StartY).get(StartX),
grid.get(EndY).get(EndX));
}
}
public void SetOpenList(int X, int Y)
{
//Check position of X and Y to avoid IndexOutofBounds.
Boolean ignoreLeft = (X - 1 < 0);
Boolean ignoreRight = (X + 1 >= grid.get(Y).size);
Boolean ignoreUp = (Y - 1 < 0);
Boolean ignoreDown = (Y + 1 >= grid.size);
tempOpen = new Array<GridNode>();
//For each check, if the checked grid is not an unpassable type and not in the closed list, continute checking.
//If checked grid is already in Open List, go to Compare parent with open.
if (!ignoreLeft && !ignoreUp)
{
if (grid.get(Y-1).get(X-1).type != GridNode.GridType.UNPASSABLE &&
!closedPath.contains(grid.get(Y-1).get(X-1), true))
{
if (!(openPath.contains(grid.get(Y-1).get(X-1), true) || openPath.contains(grid.get(Y-1).get(X-1), false)))
{
grid.get(Y-1).get(X-1).CalculateNode(grid.get(Y).get(X), grid.get(StartY).get(StartX), grid.get(EndY).get(EndX));
openPath.add(grid.get(Y-1).get(X-1));
}
else
{
CompareParentwithOpen(grid.get(Y).get(X),
openPath.get(openPath.indexOf(grid.get(Y-1).get(X-1), true)));
}
}
}
if (!ignoreUp)
{
if (grid.get(Y-1).get(X).type != GridNode.GridType.UNPASSABLE &&
!closedPath.contains(grid.get(Y-1).get(X), true))
{
if (!(openPath.contains(grid.get(Y-1).get(X), true) || openPath.contains(grid.get(Y-1).get(X), false)))
{
grid.get(Y-1).get(X).CalculateNode(grid.get(Y).get(X), grid.get(StartY).get(StartX), grid.get(EndY).get(EndX));
openPath.add(grid.get(Y-1).get(X));
}
else
{
CompareParentwithOpen(grid.get(Y).get(X),
openPath.get(openPath.indexOf(grid.get(Y-1).get(X), true)));
}
}
}
if (!ignoreRight && !ignoreUp)
{
if (grid.get(Y-1).get(X+1).type != GridNode.GridType.UNPASSABLE &&
!closedPath.contains(grid.get(Y-1).get(X+1), true))
{
if (!(openPath.contains(grid.get(Y-1).get(X+1), true) || openPath.contains(grid.get(Y-1).get(X+1), false)))
{
grid.get(Y-1).get(X+1).CalculateNode(grid.get(Y).get(X), grid.get(StartY).get(StartX), grid.get(EndY).get(EndX));
openPath.add(grid.get(Y-1).get(X+1));
}
else
{
CompareParentwithOpen(grid.get(Y).get(X),
openPath.get(openPath.indexOf(grid.get(Y-1).get(X+1), true)));
}
}
}
if (!ignoreLeft)
{
if (grid.get(Y).get(X-1).type != GridNode.GridType.UNPASSABLE &&
!closedPath.contains(grid.get(Y).get(X-1), true))
{
if (!(openPath.contains(grid.get(Y).get(X-1), true) || openPath.contains(grid.get(Y).get(X-1), false)))
{
grid.get(Y).get(X-1).CalculateNode(grid.get(Y).get(X), grid.get(StartY).get(StartX), grid.get(EndY).get(EndX));
openPath.add(grid.get(Y).get(X-1));
}
else
{
CompareParentwithOpen(grid.get(Y).get(X),
openPath.get(openPath.indexOf(grid.get(Y).get(X-1), true)));
}
}
}
if (!ignoreRight)
{
if (grid.get(Y).get(X+1).type != GridNode.GridType.UNPASSABLE &&
!closedPath.contains(grid.get(Y).get(X+1), true))
{
if (!(openPath.contains(grid.get(Y).get(X+1), true) || openPath.contains(grid.get(Y).get(X+1), false)))
{
grid.get(Y).get(X+1).CalculateNode(grid.get(Y).get(X), grid.get(StartY).get(StartX), grid.get(EndY).get(EndX));
openPath.add(grid.get(Y).get(X+1));
}
else
{
CompareParentwithOpen(grid.get(Y).get(X),
openPath.get(openPath.indexOf(grid.get(Y).get(X+1), true)));
}
}
}
if (!ignoreLeft && !ignoreDown)
{
if (grid.get(Y+1).get(X-1).type != GridNode.GridType.UNPASSABLE &&
!closedPath.contains(grid.get(Y+1).get(X-1), true))
{
if (!(openPath.contains(grid.get(Y+1).get(X-1), true) || openPath.contains(grid.get(Y+1).get(X-1), false)))
{
grid.get(Y+1).get(X-1).CalculateNode(grid.get(Y).get(X), grid.get(StartY).get(StartX), grid.get(EndY).get(EndX));
openPath.add(grid.get(Y+1).get(X-1));
}
else
{
CompareParentwithOpen(grid.get(Y).get(X),
openPath.get(openPath.indexOf(grid.get(Y+1).get(X-1), true)));
}
}
}
if (!ignoreDown)
{
if (grid.get(Y+1).get(X).type != GridNode.GridType.UNPASSABLE &&
!closedPath.contains(grid.get(Y+1).get(X), true))
{
if (!(openPath.contains(grid.get(Y+1).get(X), true) || openPath.contains(grid.get(Y+1).get(X), false)))
{
grid.get(Y+1).get(X).CalculateNode(grid.get(Y).get(X), grid.get(StartY).get(StartX), grid.get(EndY).get(EndX));
openPath.add(grid.get(Y+1).get(X));
}
else
{
CompareParentwithOpen(grid.get(Y).get(X),
openPath.get(openPath.indexOf(grid.get(Y+1).get(X), true)));
}
}
}
if (!ignoreRight && !ignoreDown)
{
if (grid.get(Y+1).get(X+1).type != GridNode.GridType.UNPASSABLE &&
!closedPath.contains(grid.get(Y+1).get(X+1), true))
{
if (!(openPath.contains(grid.get(Y+1).get(X+1), true) || openPath.contains(grid.get(Y+1).get(X+1), false)))
{
grid.get(Y+1).get(X+1).CalculateNode(grid.get(Y).get(X), grid.get(StartY).get(StartX), grid.get(EndY).get(EndX));
openPath.add(grid.get(Y+1).get(X+1));
}
else
{
CompareParentwithOpen(grid.get(Y).get(X),
openPath.get(openPath.indexOf(grid.get(Y+1).get(X+1), true)));
}
}
}
for (GridNode c : closedPath) //incase any nodes in closed are in open, remove closed nodes.
{
openPath.removeValue(c, true);
}
}
public void SetGridNode(int screenX, int screenY, GridNode.GridType Type)
{
int pointX = (int)(screenX/GridX);
int pointY = (int)(screenY/GridY);
if (pointY >= 0 && pointY < grid.size)
{
if (pointX >=0 && pointX < grid.get(pointY).size)
{
if (Type == GridNode.GridType.START || Type == GridNode.GridType.END)
{
for (int y = 0; y < grid.size; y++)
{
for (int x = 0; x < grid.get(y).size; x++)
{
if (grid.get(y).get(x).type == Type)
{
if (Type == GridNode.GridType.START)
{
StartX = -1;
StartY = -1;
}
else if (Type == GridNode.GridType.END)
{
EndX = -1;
EndY = -1;
}
grid.get(y).get(x).type = GridNode.GridType.NONE;
}
}
}
}
if (grid.get(pointY).get(pointX).type == Type)
grid.get(pointY).get(pointX).type = GridNode.GridType.NONE;
else
{
if (Type == GridNode.GridType.START)
{
StartX = pointX;
StartY = pointY;
}
else if (Type == GridNode.GridType.END)
{
EndX = pointX;
EndY = pointY;
}
grid.get(pointY).get(pointX).type = Type;
}
}
}
}
public Array<GridNode> GetPath()
{
return closedPath;
}
这些是GridNode类中的变量:
public class GridNode {
public int X = 0, Y = 0;
public int Width = 10, Height = 10;
public GridType type = GridType.NONE;
public int F = 0, G = 0, H = 0;
public static enum GridType
{
NONE,
UNPASSABLE,
START,
END
}
以下是计算GridNode类中F,G和H值的代码:
public void CalculateNode(GridNode Parent, GridNode Start, GridNode End)
{
if (Parent != null)
{
if (Math.abs(X - Parent.X) != 0 &&
Math.abs(Y - Parent.Y) != 0)
{
G = Parent.G + 14;
}
else
{
G = Parent.G + 10;
}
H = (int)((Math.abs(X-End.X)/Width) + (Math.abs(Y - End.Y)/Height)) * 10;
/*int xDistance = (Math.abs(X-End.X)/Width);
int yDistance = (Math.abs(Y - End.Y)/Height);
if (xDistance > yDistance)
H = 14*yDistance + 10*(xDistance-yDistance);
else
H = 14*xDistance + 10*(yDistance-xDistance);*/
F = G + H;
}
}
public void Reset()
{
F = G = H = 0;
}
答案 0 :(得分:0)
在Twitter上@KommaKameleon的帮助下发现问题。您可以查看GitHub Repo以获取完整的工作代码。
问题是我正在按照上面的写法执行所有步骤。除最后一步之外的所有步骤,要求您通过每个节点Parent从终点到起点。
因此,我所做的是在GridNode类中添加一个GridNode变量来存储用于计算F,G和H的Parent节点。
public class GridNode {
public float X = 0, Y = 0;
public int Width = 10, Height = 10;
public GridType type = GridType.NONE;
public float F = 0, G = 0, H = 0;
public GridNode Parent = null;
...
public void CalculateNode(GridNode Parent, GridNode Start, GridNode End)
{
this.Parent = Parent;
if (Parent != null)
{
...
然后在findPath()方法中,在while循环之后,我做了另一个while循环来设置一个名为finalPath的新Array属性:
private Array<GridNode> finalPath = new Array<GridNode>();
这是我过去从终点到起点经过每个父母的算法。 (End节点位于closedPath数组的最后一个元素中)
GridNode g = closedPath.peek();
finalPath.add(g);
while (g != grid.get(StartY).get(StartX))
{
g = g.Parent;
finalPath.add(g);
}
finalPath.reverse();