【问题标题】:Specific A* Pathfinding Issue with Implementation实施的特定 A* 寻路问题
【发布时间】:2017-05-09 08:38:43
【问题描述】:

我的 A* 实施存在一些问题。它偶尔会决定在我的网格上做一些奇怪的事情,比如忽略移动成本并通过高成本区域,或者在回到正轨之前朝错误的方向移动。

我正式在这上面花了太多时间,所以我想承认,所以我伸出手来寻找一双新鲜的眼睛。

private List<Vector2> PathFromTo(Vector2 startID, Vector2 targetID){
    List<Vector2> path = new List<Vector2> ();
    List<Node> closedList = new List<Node> ();
    List<Node> openList = new List<Node> ();
    Node startNode = nodeList.Find (tgt => tgt.nodeID == new Vector2 (startID.x, startID.y));
    if (startNode == null)
        return path;
    Node targetNode = nodeList.Find (tgt => tgt.nodeID == new Vector2 (targetID.x, targetID.y));
    if (targetNode == null)
        return path;

    openList.Add (startNode);

    while (openList.Count > 0) {
        Node current = openList [0];
        for (int i = 1; i < openList.Count; i++) 
            if (openList [i].GetFCost () <= current.GetFCost () && openList [i].GetHCost () < current.GetHCost ()) 
                current = openList [i];

        openList.Remove (current);
        closedList.Add (current);

        if (current == targetNode) {
            RetracePath (startNode, targetNode, ref path);
            return path;
        }

        foreach(Vector2 neighbour in current.neighbors) {
            Node neighbourNode = nodeList.Find (tgt => tgt.nodeID == new Vector2 (neighbour.x, neighbour.y));
            CheckNeighbor(ref neighbourNode, ref current, ref targetNode, ref closedList, ref openList);
        }
    }
    return path;
}

private void CheckNeighbor(ref Node neighborTile, ref Node currentTile, ref Node targetTile, ref List<Node> closedList, ref List<Node> openList){
    if (neighborTile != null) {
        if (!neighborTile.passable || closedList.Contains (neighborTile)) {
        } else {
            int newCostToNeighbor = (int)(currentTile.moveCost + CalculateDistance (currentTile.position, neighborTile.position));
            if (newCostToNeighbor < neighborTile.GetGCost() || !openList.Contains (neighborTile)) {
                neighborTile.SetGCost (newCostToNeighbor);
                neighborTile.SetHCost (CalculateDistance (neighborTile.position, targetTile.position));
                neighborTile.SetParent (currentTile);

                if (!openList.Contains (neighborTile)) 
                    openList.Add (neighborTile);
            }
        }
    }
}

public float CalculateDistance(Vector2 tileA_pos, Vector2 tileB_pos){ 
    float dX = Mathf.Abs (tileB_pos.x - tileA_pos.x); 
    float dY = Mathf.Abs (tileB_pos.y - tileA_pos.y); 
    float shift1 = -(tileA_pos.x + tileA_pos.y); 
    float shift2 = -(tileB_pos.x + tileB_pos.y); 
    float dZ = Mathf.Abs (shift2 - shift1); 
    return Mathf.Max (dX, dY, dZ); 
}

private void RetracePath(Node start, Node end, ref List<Vector2> pathInfo){
    pathInfo = new List<Vector2> ();
    Node current = end;
    while (current != start) {
        pathInfo.Add (current.nodeID);
        current = current.GetParent ();
    }
    pathInfo.Reverse ();
}

【问题讨论】:

  • 你已经尝试了什么?你能重现你的问题吗?如果是这样,您是否尝试过调试代码以至少尝试查明哪里出了问题?
  • 我尝试过弄乱路径成本和起始位置,但我使用的电路板使用了一些随机生成,因此可能有点难以确定确切的模式。最坏的地方是瓷砖可以通过但移动成本很高的地方。它不是绕过瓷砖,而是穿过它们。瓦片成本是正确的,它只是生成了不正确的路径,因此,我认为实现是不正确的。
  • 您可以记录/存储随机函数的种子,以便更轻松地重现问题。您可以使用种子初始化随机函数,以使其始终产生相同的结果。无论如何,关于你的代码:我的感觉是你的问题与CheckNeighbor 中的int newCostToNeighbor = ... 行有关,你确定你需要currentTile.moveCost 而不是neighborTile.moveCost
  • 您提到“高成本瓷砖”,这在您的代码示例中并不明显。 (如何)这是在CalculateDistance 中处理的吗?
  • @NickB。您应该编辑您的问题以添加您的 CalculateDistance 方法。代码太多,无法很好地放在评论中。之后也请删除该评论。哦,当您编辑您的问题时,请同时删除其中的 [oop] 标签,因为您的问题与面向对象编程无关(例如,您没有任何继承或抽象问题)。

标签: c# unity3d path-finding a-star


【解决方案1】:

鉴于您在 cmets 中显示的 CalculateDistance 方法,我编写了以下测试程序:(假设您的 Mathf 类似于 System.Math

for (int y = -4; y < 5; y++)
{
    for (int x = -4; x < 5; x++)
    {
        var dst = CalculateDistance(new Vector2(x, y), new Vector2());
        Console.Write($"{((int)dst):D1} ");
    }
    Console.WriteLine();
}

测试程序测试 (-4,-4) 和 (4, 4) 之间的所有坐标,并计算它们到 (0,0) 的距离输出:

8 7 6 5 4 4 4 4 4
7 6 5 4 3 3 3 3 4
6 5 4 3 2 2 2 3 4
5 4 3 2 1 1 2 3 4
4 3 2 1 0 1 2 3 4
4 3 2 1 1 2 3 4 5
4 3 2 2 2 3 4 5 6
4 3 3 3 3 4 5 6 7
4 4 4 4 4 5 6 7 8

您可以看到输出完全古怪,您可能希望右下角与右上角的 (0,0) 一样远,但事实并非如此。也许你需要重写你的CalculateDistance 方法。

您似乎在计算 dX、dY 和 dZ,鉴于您只有 2 个坐标 (Vector2),这是不可能的。


编辑:如果没有记录“权重”,您可以简单地使用毕达哥拉斯来计算两点之间的距离:

var dist = Math.Sqrt(Math.Pow(point1.x - point2.x, 2) + Math.Pow(point1.y - point2.y, 2));

【讨论】:

  • 很好的分析和调试,但技术上这不是答案。如果您能提出解决方法,您将获得更高的分数。
  • @BradleyUffner 点。添加了一个简单的解决方案。请注意 OP 寻求调试帮助,这里有什么问题?,不一定是完整的解决方案。也许这种风格的问题不太适合 StackOverflow。
  • 我换了新配方,还是没有解决问题。
【解决方案2】:

您没有说明是否允许对角线移动,但CalculateDistance 的这两个例程之一就足够了:

    public static readonly int D = 1;
    public static readonly int D2 = 1;

    public static float CalculateDistance(Vector2 tileA_pos, Vector2 tileB_pos)
    {
        float dX = Math.Abs(tileB_pos.x - tileA_pos.x);
        float dY = Math.Abs(tileB_pos.y - tileA_pos.y);
        return D * (dX + dY);
    }

    public static float CalculateDistanceDiagonalsAllowed(Vector2 tileA_pos, Vector2 tileB_pos)
    {
        float dX = Math.Abs(tileB_pos.x - tileA_pos.x);
        float dY = Math.Abs(tileB_pos.y - tileA_pos.y);
        return D * (dX + dY) + (D2 - 2 * D) * (dX < dY ? dX : dY);
    }

其中 D 是垂直/水平移动的成本,D2 是对角线移动的成本 - 您可能希望根据需要将其设置为 1 或 Sqrt(2)。我假设 currentTile.moveCost 被用来定义高/低成本瓷砖

【讨论】:

  • 我尝试了这两种解决方案,但都没有解决问题。
【解决方案3】:

经过太多小时(以及整晚的睡眠)后,我终于弄明白了。该问题与 CheckNeighbor 函数有关。新方法如下所示:

private void CheckNeighbor(ref Node neighborTile, ref Node currentTile, ref Node targetTile, ref List<Node> closedList, ref List<Node> openList, bool ignoreMoveCost = false){
    if (neighborTile != null) {
        if (!neighborTile.passable || closedList.Contains (neighborTile)) {
        } else {
            int newCostToNeighbor = (int)((ignoreMoveCost ? 1 : neighborTile.moveCost) + currentTile.GetGCost() + CalculateDistance (currentTile.position, neighborTile.position));
            if (!openList.Contains (neighborTile)) {
                openList.Add (neighborTile);
            } else if (newCostToNeighbor >= neighborTile.GetGCost ()) {
                return;
            }
            neighborTile.SetParent (currentTile);
            neighborTile.SetGCost (newCostToNeighbor);
            neighborTile.SetHCost (CalculateDistance (currentTile.position, neighborTile.position));
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多