使用航点的Unity A *寻路

时间:2017-01-30 01:17:02

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

我正在尝试使用我在地形上使用单独脚本生成的路标创建A *寻路算法。 地形上的可穿越和不可穿越的航点由其颜色定义 - 绿色可穿越。

通过以下链接可以看到节点的布局: https://i.stack.imgur.com/JDZdx.jpg

可通过的航路点存储在列表中。

为了启动寻路算法,我创建了一个字典,将每个可遍历的路点存储为唯一键(type = gameobject)。每个键的值是使用距离函数计算的邻居(type = List)。

然后我尝试使用在线参考创建A *算法并根据我的情况调整这些算法。每个部分的注释可以在下面的代码中看到。功能' findPath'是实际的算法(好吧,我最好的尝试)。

 void findPath()
    {

        // Adds start node to open list
        // take nodes around start node and add to lista*

        open.Add(startNode);


        while (open.Count > 0)
        {
            nodeDistances = 1000;
            foreach (GameObject node in open)
            {
                GCost = Vector3.Distance(startNode.transform.position, node.transform.position);// G distance form node to start
                HCost = Vector3.Distance(targetNode.transform.position, node.transform.position); // H distance from node to target
                FCost = GCost + HCost;

                if (FCost < nodeDistances) // if current node has smaller F cost than the node before that
                {
                    tempGCost = GCost; // Gets the lowest G cost
                    tempFCost = FCost;
                    current = node; // Replaces Game Object variable
                    nodeDistances = FCost;
                }

            }

            if (current.transform.position == targetNode.transform.position)
            {



                Debug.Log("Goal reached");
                break;
            }
            open.Remove(current);
            closed.Add(current);




            neighbours = attachedToWaypoint[current];
            // path.Add(current);

            foreach (GameObject n in neighbours)
            {
                if (!closed.Contains(n))
                {
                    float g_score = tempGCost + 1; // Takes current node GCost and adds value of 1 for each neighbour
                    float h_score = Vector3.Distance(n.transform.position, targetNode.transform.position); // Gets distance from each neighbour to the target node
                    float f_score = g_score + h_score;


                    if (closed.Contains(n) && f_score >= tempFCost) // If F cost of neighbour is greater than F cost of current node

                        continue;


                    if (!open.Contains(n) || f_score < tempFCost) // If 'open' list does not currently contain neighbour or neighbours F score is smaller than that of the current node
                    {

                        GCost = g_score;
                        HCost = h_score;
                        tempFCost = GCost + HCost; // update current node F score value to get neighbour with the smallest F score


                        if (!open.Contains(n))
                        {
                            open.Add(n); // if neihgbour is not in open list, add to open list
                        }
                    }

                }
            }
        }
    }

但是,查看存储在关闭列表中的节点,很明显出现了问题 - 封闭列表中的节点图片:http://imgur.com/5cF7voO

有经验的人可以给我一些关于我应该关注的方面的指示吗?另外,你如何使用它来找到最便宜的路径?

任何帮助都将非常感激。

谢谢, 卡尔文

以下完整脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class TEST : MonoBehaviour
{

    Dictionary<GameObject, List<GameObject>> attachedToWaypoint = new Dictionary<GameObject, List<GameObject>>();
    List<GameObject> gameObjectForDic = new List<GameObject>();
    GameObject[] waypoints;
    List<List<GameObject>> nodeData = new List<List<GameObject>>();



    List<GameObject> neighbours = new List<GameObject>(); // Not currently used

    public GameObject enemy;
    public GameObject player;
    GameObject startNode;
    GameObject targetNode;



    List<GameObject> open = new List<GameObject>();
    List<GameObject> closed = new List<GameObject>();

    float distanceToEnemy;
    float distanceToPlayer;

    float tempFCost;
    float FCost;
    float GCost;
    float HCost;
    float tempGCost;
    float accumulatedCost;
    float accumulatedCostTemp;
    float nodeDistances;

    GameObject current;

    public GameObject parent;

    List<GameObject> path = new List<GameObject>();


    // Use this for initialization
    void Start()
    {
        distanceToEnemy = 1000;
        distanceToPlayer = 1000;
        nodeDistances = 10000;

        waypoints = GameObject.FindGameObjectsWithTag("Waypoint");

        storeNodesInDictionary();
        findPath();
        // findPathtoPlayer();
        testing();








    }

    void storeNodesInDictionary()
    {
        for (int i = 0; i < waypoints.Length; i++)
        {

            nodeData.Add(new List<GameObject>());


            float distEnemy = Vector3.Distance(enemy.transform.position, waypoints[i].transform.position); // Closest node to enemy

            if (distEnemy < distanceToEnemy)
            {
                startNode = waypoints[i];
                distanceToEnemy = distEnemy;

            }



            float distPlayer = Vector3.Distance(player.transform.position, waypoints[i].transform.position);// Closest node to player

            if (distPlayer < distanceToPlayer)
            {
                targetNode = waypoints[i];
                distanceToPlayer = distPlayer;
            }




            for (int j = 0; j < waypoints.Length; j++)
            {
                float distanceSqr = (waypoints[i].transform.position - waypoints[j].transform.position).sqrMagnitude; // Gets distance values
                if (distanceSqr < 60 && waypoints[i] != waypoints[j]) // Is waypoints are within a spcific distance
                {
                    nodeData[i].Add(waypoints[j]);

                }
            }

            attachedToWaypoint.Add(waypoints[i], nodeData[i]); // Adds parent node and neighbouring nodes within a 3x3 grid

        }

    }





    void findPath()
    {

        // Adds start node to open list
        // take nodes around start node and add to lista*

        open.Add(startNode);


        while (open.Count > 0)
        {
            nodeDistances = 1000;
            foreach (GameObject node in open)
            {
                GCost = Vector3.Distance(startNode.transform.position, node.transform.position);// G distance form node to start
                HCost = Vector3.Distance(targetNode.transform.position, node.transform.position); // H distance from node to target
                FCost = GCost + HCost;

                if (FCost < nodeDistances) // if current node has smaller F cost than the node before that
                {
                    tempGCost = GCost; // Gets the lowest G cost
                    tempFCost = FCost;
                    current = node; // Replaces Game Object variable
                    nodeDistances = FCost;
                }

            }

            if (current.transform.position == targetNode.transform.position)
            {



                Debug.Log("Goal reached");
                break;
            }
            open.Remove(current);
            closed.Add(current);




            neighbours = attachedToWaypoint[current];
            // path.Add(current);

            foreach (GameObject n in neighbours)
            {
                if (!closed.Contains(n))
                {
                    float g_score = tempGCost + 1; // Takes current node GCost and adds value of 1 for each neighbour
                    float h_score = Vector3.Distance(n.transform.position, targetNode.transform.position); // Gets distance from each neighbour to the target node
                    float f_score = g_score + h_score;


                    if (closed.Contains(n) && f_score >= tempFCost) // If F cost of neighbour is greater than F cost of current node

                        continue;


                    if (!open.Contains(n) || f_score < tempFCost) // If 'open' list does not currently contain neighbour or neighbours F score is smaller than that of the current node
                    {

                        GCost = g_score;
                        HCost = h_score;
                        tempFCost = GCost + HCost; // update current node F score value to get neighbour with the smallest F score


                        if (!open.Contains(n))
                        {
                            open.Add(n); // if neihgbour is not in open list, add to open list
                        }
                    }

                }
            }
        }
    }

}

1 个答案:

答案 0 :(得分:0)

一些建议:

Closed的良好数据结构是HashSet,这肯定会加快您的代码速度并且不需要太多更改。

不幸的是Open的正确数据结构是priority queue,但是c#没有内置的数据结构,如果您使用可以提供最佳性能的第三方实现,那么f#成本将是您的首要任务。否则你需要自己编写。

至于你的封闭列表图片看起来并不坏,但是从我能够弄清楚错误可能是以下几行:

GCost = Vector3.Distance(startNode.transform.position, node.transform.position); // G distance from node to start

g cost应该是从startNode到节点的实际距离,但是您使用相同的函数来计算用于计算h的g。这就像在整个时间里使用两条直线一样,如果你想象它会如何通过一条直线路径绕过它的锯齿形路径,它会一直沿着曲折的路径走下去,一直认为它是直的从startNode到当前节点。

以下是我在调查时对代码所做的工作,你可以用它做什么。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Some.Namespace.That.Gets.PriorityQueue;

public class TEST: MonoBehaviour {

    Dictionary<GameObject, List<GameObject>> attachedToWaypoint = new Dictionary<GameObject, List<GameObject>>();

    GameObject[] waypoints;
    GameObject startNode;
    GameObject targetNode;
    GameObject current;

    PriorityQueue<float, GameObject> open = new PriorityQueue<float, GameObject>();
    HashSet<GameObject> closed = new HashSet<GameObject>();
    List<GameObject> path = new List<GameObject>();

    float distanceToEnemy;
    float distanceToPlayer;
    float FCost;
    float GCost;
    float HCost;
    float nodeDistances;

    public GameObject enemy;
    public GameObject player;
    public GameObject parent;

    // Use this for initialization
    void Start() {
        distanceToEnemy = 1000;
        distanceToPlayer = 1000;
        nodeDistances = 10000;

        waypoints = GameObject.FindGameObjectsWithTag("Waypoint");
        storeNodesInDictionary();
        findPath();
        // findPathtoPlayer();
        testing();
    }

    void storeNodesInDictionary() {
        for (int i = 0; i < waypoints.Length; i++) {
            var waypoint = waypoints[i];
            var nodeData = new List<GameObject>();
            var waypointPosition = waypoint.transform.position;

            float distEnemy = Vector3.Distance(enemy.transform.position, waypointPosition); // Closest node to enemy
            if (distEnemy < distanceToEnemy) {
                startNode = waypoint;
                distanceToEnemy = distEnemy;
            }

            float distPlayer = Vector3.Distance(player.transform.position, waypointPosition); // Closest node to player
            if (distPlayer < distanceToPlayer) {
                targetNode = waypoint;
                distanceToPlayer = distPlayer;
            }

            for (int j = 0; j < waypoints.Length; j++) {
                if (i == j)
                    continue;
                var otherWaypoint = waypoints[j];
                float distanceSqr = (waypointPosition - otherWaypoint.transform.position).sqrMagnitude; // Gets distance values
                if (distanceSqr < 60) // Is waypoints are within a spcific distance
                {
                    nodeData.Add(otherWaypoint);
                }
            }
            attachedToWaypoint.Add(waypoint, nodeData); // Adds parent node and neighbouring nodes within a 3x3 grid
        }
    }

    void findPath() {
        var startPosition = startNode.transform.position;
        var targetPosition = targetNode.transform.position;
        open.Push(Vector3.Distance(startPosition, targetPosition), startNode);
        while (open.Count > 0) {
            FCost = open.Pop(out current);
            var currentPosition = current.transform.position;
            if (currentPosition == targetPosition) {
                Debug.Log("Goal reached");
                break;
            }
            HCost = Vector3.Distance(currentPosition, targetPosition);
            GCost = FCost - HCost;
            closed.Add(current);
            var neighbours = attachedToWaypoint[current];
            // path.Add(current);
            foreach(GameObject n in neighbours) {
                if (!closed.Contains(n) && !open.Contains(n)) // If 'open' list does not currently contain neighbour or neighbours F score is smaller than that of the current node
                {
                    var neighbourPosition = n.transform.position;
                    var neighbourFCost = GCost + Vector3.Distance(currentPosition, neighbourPosition) + Vector3.Distance(neighbourPosition, targetPosition)
                    open.Push(neighbourFCost, n); // if neighbour is not in open list, add to open list
                }
            }
        }
    }
}