因此,我尝试在 LibGDX 中实现 A* 寻路算法,以便了解它的工作原理。我的实现基于这篇文章: < Link >
目前,我已经获得了计算路径的算法,如果目标不可访问,甚至将路径返回为不存在,但是如果有墙壁部分阻挡目标,则找到的路径所采用的路线确实不寻常正如您在这里看到的:
所以我想找出我的实现出了什么问题,看看为什么封闭列表会生成这样的路径,或者我是否遗漏了一个步骤。
这些是 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 类中的路径查找代码:
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;
}
最佳答案
在 Twitter 上的 @KommaKameleon 的帮助下发现了该问题。您可以查看GitHub Repo完整的工作代码。
问题是我遵循了上面的所有步骤。除了最后一步之外的所有步骤,这需要您通过每个节点的父节点从终点到起点。
因此,我所做的就是在 GridNode 类中添加一个 GridNode 变量来存储计算 F、G 和 H 时使用的父节点。
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 的新数组属性:
private Array<GridNode> finalPath = new Array<GridNode>();
这是我用来从终点到起点遍历每个父级的算法。 (其中 End 节点位于 closePath 数组的最后一个元素中)
GridNode g = closedPath.peek();
finalPath.add(g);
while (g != grid.get(StartY).get(StartX))
{
g = g.Parent;
finalPath.add(g);
}
finalPath.reverse();
关于java - 我的 A* 寻路实现有问题吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21991668/