unity打造基于AStar的寻路导航系统

Unity是一款非常流行的游戏引擎,它提供了丰富的功能和组件,其中包括寻路导航系统。本文将详细介绍如何在Unity中打造基于AStar的寻路导航系统,并给出相关的代码实现。

对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白,也有一些正在从事游戏开发的技术大佬,欢迎你来交流学习。

一、什么是AStar算法?

AStar算法是一种常用的寻路算法,它可以在地图中找到一条最短的路径。AStar算法的基本思想是通过估价函数来评估每个节点的优先级,并按照优先级从高到低的顺序进行搜索,直到找到目标节点为止。

在AStar算法中,每个节点都有一个估价值,用于评估该节点到目标节点的距离。该估价值可以通过欧几里得距离或曼哈顿距离等方法计算得出。在搜索时,AStar算法会优先搜索估价值较小的节点,以期望更快地找到目标节点。

二、Unity中的寻路导航系统

Unity中的寻路导航系统是基于NavMesh(导航网格)实现的。NavMesh是一种用于描述游戏场景中可行走区域的网格,它可以帮助游戏对象在场景中自动寻路,并且可以避免游戏对象穿过障碍物等问题。

在Unity中,我们可以通过NavMeshAgent组件来实现寻路导航。NavMeshAgent组件是一种用于控制游戏对象寻路的组件,它可以自动地计算并跟随NavMesh中的路径,并在寻路过程中自动避开障碍物等问题。

三、基于AStar的寻路导航系统实现

在Unity中,我们可以通过代码实现基于AStar的寻路导航系统。下面是一个简单的实现示例:

首先,我们需要定义一个Node类,用于表示地图中的节点:

public class Node
{
    public int X { get; set; }
    public int Y { get; set; }
    public bool Walkable { get; set; }
    public int GCost { get; set; }
    public int HCost { get; set; }
    public int FCost { get { return GCost + HCost; } }
    public Node Parent { get; set; }

    public Node(int x, int y, bool walkable)
    {
        X = x;
        Y = y;
        Walkable = walkable;
    }
}

在Node类中,我们定义了X和Y两个变量,用于表示节点的坐标;Walkable变量表示该节点是否可行走;GCost、HCost和FCost变量分别表示从起点到该节点的实际代价、从该节点到终点的估价代价和总代价;Parent变量表示该节点的父节点,用于记录路径。

接下来,我们需要定义一个AStar类,用于实现AStar算法:

public class AStar
{
    private Node[,] nodes;
    private List<Node> openList;
    private List<Node> closedList;

    public AStar(Node[,] nodes)
    {
        this.nodes = nodes;
    }

    public List<Node> FindPath(Node startNode, Node endNode)
    {
        openList = new List<Node>();
        closedList = new List<Node>();

        openList.Add(startNode);

        while (openList.Count > 0)
        {
            Node currentNode = openList[0];

            for (int i = 1; i < openList.Count; i++)
            {
                if (openList[i].FCost < currentNode.FCost || openList[i].FCost == currentNode.FCost && openList[i].HCost < currentNode.HCost)
                {
                    currentNode = openList[i];
                }
            }

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

            if (currentNode == endNode)
            {
                return GetPath(startNode, endNode);
            }

            foreach (Node neighborNode in GetNeighborNodes(currentNode))
            {
                if (!neighborNode.Walkable || closedList.Contains(neighborNode))
                {
                    continue;
                }

                int newGCost = currentNode.GCost + GetDistance(currentNode, neighborNode);

                if (newGCost < neighborNode.GCost || !openList.Contains(neighborNode))
                {
                    neighborNode.GCost = newGCost;
                    neighborNode.HCost = GetDistance(neighborNode, endNode);
                    neighborNode.Parent = currentNode;

                    if (!openList.Contains(neighborNode))
                    {
                        openList.Add(neighborNode);
                    }
                }
            }
        }

        return null;
    }

    private List<Node> GetPath(Node startNode, Node endNode)
    {
        List<Node> path = new List<Node>();
        Node currentNode = endNode;

        while (currentNode != startNode)
        {
            path.Add(currentNode);
            currentNode = currentNode.Parent;
        }

        path.Reverse();

        return path;
    }

    private List<Node> GetNeighborNodes(Node node)
    {
        List<Node> neighborNodes = new List<Node>();

        for (int x = -1; x <= 1; x++)
        {
            for (int y = -1; y <= 1; y++)
            {
                if (x == 0 && y == 0)
                {
                    continue;
                }

                int checkX = node.X + x;
                int checkY = node.Y + y;

                if (checkX >= 0 && checkX < nodes.GetLength(0) && checkY >= 0 && checkY < nodes.GetLength(1))
                {
                    neighborNodes.Add(nodes[checkX, checkY]);
                }
            }
        }

        return neighborNodes;
    }

    private int GetDistance(Node nodeA, Node nodeB)
    {
        int distanceX = Mathf.Abs(nodeA.X - nodeB.X);
        int distanceY = Mathf.Abs(nodeA.Y - nodeB.Y);

        if (distanceX > distanceY)
        {
            return 14 * distanceY + 10 * (distanceX - distanceY);
        }
        else
        {
            return 14 * distanceX + 10 * (distanceY - distanceX);
        }
    }
}

在AStar类中,我们定义了nodes、openList和closedList三个变量。nodes表示地图中的节点;openList表示待搜索的节点列表;closedList表示已搜索的节点列表。

我们还定义了FindPath方法,用于实现AStar算法。该方法接受两个参数,分别是起点和终点。在FindPath方法中,我们首先将起点加入openList中,并循环搜索openList中的节点,直到找到终点为止。在搜索过程中,我们会计算每个节点的代价,并按照代价从小到大的顺序进行搜索。当找到终点时,我们会调用GetPath方法获取路径,并返回该路径。

在GetPath方法中,我们定义了一个path列表,用于存储路径。接着,我们从终点开始遍历父节点,直到遍历到起点为止,并将每个节点加入path列表中。最后,我们将path列表反转后返回结果。

在GetNeighborNodes方法中,我们定义了一个neighborNodes列表,用于存储邻居节点。我们遍历当前节点周围的所有节点,并将可行走的节点加入neighborNodes列表中。

在GetDistance方法中,我们计算了两个节点之间的距离,并返回代价。

最后,我们需要在Unity中实现寻路导航。我们可以通过NavMeshAgent组件来实现自动寻路,并在寻路过程中避开障碍物。下面是一个简单的示例:

首先,我们需要创建一个游戏对象并添加NavMeshAgent组件。接着,我们需要在场景中创建NavMesh,用于描述可行走区域。最后,我们可以通过代码来控制NavMeshAgent组件的目标位置,并让游戏对象自动寻路。

public class Player : MonoBehaviour
{
    public Transform target;

    private NavMeshAgent navMeshAgent;

    private void Start()
    {
        navMeshAgent = GetComponent<NavMeshAgent>();
    }

    private void Update()
    {
        if (target != null)
        {
            navMeshAgent.SetDestination(target.position);
        }
    }
}

在Player类中,我们首先获取NavMeshAgent组件,并在Update方法中设置目标位置。当目标位置发生变化时,NavMeshAgent组件会自动计算并跟随NavMesh中的路径,并在寻路过程中避开障碍物等问题。

四、总结

基于AStar的寻路导航系统是一种非常常用的游戏开发技术,它可以帮助我们更加高效地实现游戏中的自动寻路功能。在Unity中,我们可以通过NavMeshAgent组件实现寻路导航,并通过AStar算法来计算最短路径。本文介绍了Unity打造基于AStar的寻路导航系统,并给出了相关的代码实现。希望读者能够从中受益,并在实际的游戏开发中灵活运用。

猜你喜欢

转载自blog.csdn.net/voidinit/article/details/129936160