unity中使用A*算法寻路典例

几个月前开始做那个小游戏时,发现需要用到A寻路,看了很多讲解,终于把A搞懂了。过程描述:

①:通过循环提取Open-List中的值并不断地比较寻找此时列表中代价最低的路径点,将代价最低点移除Open-List,加入Close-List后进入②。持续循环此步骤直到Open-List中的路径点个数为0。

②:判断此路径点是否为寻路终点,若是则计算路径,直接进入④。否则,获得此点周围的非障碍点,进入③。

③:判断周围非障碍点是否已经存在于Open-List中,如果存在,重新计算并更新路径点代价函数值。如若不存在,计算路径点代价函数值后并将路径点储存在Open-List中。后将①中的提取点与此周围非障碍点设为父子关系,方便于最终路径点的提取。

④:得到最终点,依次根据节点的父子关系寻找父节点并存入数列中,直至寻找到初始路径点。数列中所有点的集合,即为寻路路径。

代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//传说中的最优寻路方法,A*
public class Navigation : MonoBehaviour
{
    private Vector2 chushiposition;
    public GameObject gametest;
    public GameObject gametestlujing;
    [Range(0.5f, 1f)]
    public float lengthofbox;//我的tilemap每个格子是1所以我设置的是1,这个可以根据实际情况改
    public LayerMask NodeLayer;//选择障碍物所在的层
    public Transform player;//拖上目标对象(被追踪者)
    private Transform startPos;
    public Transform meshOrigin;//格子起点对象
    public class NodeItem
    {//单位格子类
        public bool iswall;
        public Vector2 worldpos;
        public int x, y;
        public int g;
        public int h;
        public int f
        {
            get { return g + h; }
        }
        public NodeItem parentNode;
        public NodeItem(bool iswall, Vector2 worldpos, int x, int y)
        {
            this.iswall = iswall;
            this.worldpos = worldpos;
            this.x = x;
            this.y = y;
        }
    }
    private NodeItem[,] nodeItems;
    private int w, h;
    private void Awake()
    {//初始化所有格子
        chushiposition = this.transform.position;
        w = 40; h = 40;
        nodeItems = new NodeItem[w, h];
        for (int i = 0; i < h; i++)
        {
            for (int j = 0; j < w; j++)
            {
                Vector2 pos = new Vector2(i * lengthofbox, j * lengthofbox) + (Vector2)meshOrigin.position;
                bool iswall = false;
                // bool iswall = Physics2D.BoxCast(pos, new Vector2(lengthofbox-0.5f, lengthofbox-0.5f), 0, new Vector2(0, 0), NodeLayer);
                RaycastHit2D hit = Physics2D.BoxCast(pos, new Vector2(lengthofbox - 0.5f, lengthofbox - 0.5f), 0, new Vector2(0, 0), NodeLayer);
                if (hit)
                {
                    iswall = true;
                    if (hit.collider.gameObject.tag == "Player" || hit.collider.gameObject.tag == "guai")
                    {
                        iswall = false;
                    }
                }
                nodeItems[i, j] = new NodeItem(iswall, pos, i, j);
                //if (nodeItems[i, j].iswall)
                //{
                //    GameObject.Instantiate(gametest, nodeItems[i, j].worldpos,Quaternion.identity);
                //}
            }
        }
    }
    private void Start()
    {
        startPos = this.transform;       
    }
    public NodeItem GetIdem(Vector2 pos)
    {//获取坐标的格子坐标
        int i = Mathf.RoundToInt((pos.x - ((Vector2)meshOrigin.position).x) / lengthofbox);
        int j = Mathf.RoundToInt((pos.y - ((Vector2)meshOrigin.position).y) / lengthofbox);
        i = Mathf.Clamp(i, 0, w - 1);
        j = Mathf.Clamp(j, 0, h - 1);
        return nodeItems[i, j];
    }
    public List<NodeItem> Idemaround(NodeItem node)
    {//获取周围8个格子信息
        List<NodeItem> aroundlist = new List<NodeItem>();
        for (int i = -1; i <= 1; i++)
        {
            for (int j = -1; j <= 1; j++)
            {
                if (i != 0 || j != 0)
                {
                    int x = node.x + i;
                    int y = node.y + j;
                    if (x < w && x >= 0 && y < h && y >= 0)
                    {
                        aroundlist.Add(nodeItems[node.x + i, node.y + j]);
                    }
                }
            }
        }
        //  for(int c = 0; c < aroundlist.Count; c++)
        // {
        //     print(aroundlist[c].x + "  " + aroundlist[c].y);
        //  }
        return aroundlist;
    }    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.B))
        {
            findingpath((Vector2)startPos.position, (Vector2)player.position);
        }
    }
    private void findingpath(Vector2 startpos, Vector2 distpos)
    {//寻路
        NodeItem startnode = GetIdem(startpos);
        NodeItem distnode = GetIdem(distpos);
        List<NodeItem> Openlist = new List<NodeItem>();
        HashSet<NodeItem> Closelist = new HashSet<NodeItem>();
        Openlist.Add(startnode);
        while (Openlist.Count > 0)
        {
            NodeItem current = Openlist[0];
            for (int i = 0, max = Openlist.Count; i < max; i++)
            {
                // print(Openlist[i].x + "  " + Openlist[i].y + "  " + Openlist[i].f);
                if (current.f >= Openlist[i].f && current.h > Openlist[i].h)
                {
                    current = Openlist[i];
                }
            }
            Openlist.Remove(current);
            Closelist.Add(current);
            //Openlist.Clear();
            if (current == distnode)
            {
                generatepath(startnode, distnode);
                return;
            }
            foreach (NodeItem item in Idemaround(current))
            {
                if (item.iswall == false)
                {
                    GameObject.Instantiate(gametest, item.worldpos, Quaternion.identity);
                }
                //print(item.x + "  " + item.y + "  " + item.f);
                if (item.iswall || Closelist.Contains(item)) continue;
                int newcost = current.g + getdistance(current, item);
                if (newcost < item.g || !Openlist.Contains(item))
                {
                    item.g = newcost;
                    item.h = getdistance(item, distnode);
                    item.parentNode = current;
                    if (!Openlist.Contains(item))
                    {
                        Openlist.Add(item); 
                    }
                }
            }
        }
        generatepath(startnode, null);
    }
    private void generatepath(NodeItem A, NodeItem B)
    {//生成路径泛型队列
        List<NodeItem> path = new List<NodeItem>();
        if (B != null)
        {
            NodeItem node = B;
            while (node != A)
            {
                path.Add(node);
                node = node.parentNode;
            }
            path.Add(A);
            path.Reverse();
            print(path.Count);
            for(int i = 0; i < path.Count; i++)
            {
                GameObject.Instantiate(gametestlujing, path[i].worldpos, Quaternion.identity);
            }
        }
    }
    private int getdistance(NodeItem curr, NodeItem item)
    {//估算权值,对角线算法
        //int costx = Mathf.Abs(curr.x - item.x);
        //int costy = Mathf.Abs(curr.y - item.y);
        //if (costx > costy)
        //{
        //    return 10 * costx ;
        //}
        //else
        //{
        //    return 10 *costy;
        //}
        //曼哈顿算法
        return Mathf.Abs(curr.x - item.x) * 10 + Mathf.Abs(curr.y - item.y) * 10;
    }
}

为了方便理解,我和同学画了好久的流程图:在这里插入图片描述这是PartA部分

这是PartA部分

这是PartB部分

这是PartB部分

可能避免不了有错误。hh~

推荐几个厉害的讲解:
https://blog.csdn.net/dengheCSDN/article/details/78778769
https://blog.csdn.net/codingriver/article/details/83186067
https://blog.csdn.net/u012234115/article/details/47152137
这几个很厉害,
我的也有自己的样子在这里插入图片描述

还改进了一个搜索距离大的。

在这里插入图片描述
想要工程的可以私聊我哦!

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会尝试回答你的问题。 首先,要使用Unity脚本实现A*寻路算法,需要在Unity创建一个脚本,并将其添加到场景游戏对象上。 以下是实现A*寻路算法Unity脚本示例代码: ```csharp using UnityEngine; using System.Collections; using System.Collections.Generic; public class AStarPathfinding : MonoBehaviour { public Transform seeker, target; //起点和终点 Grid grid; //寻路所需的网格 void Awake() { grid = GetComponent<Grid>(); } void Update() { FindPath(seeker.position, target.position); } void FindPath(Vector3 startPos, Vector3 targetPos) { Node startNode = grid.NodeFromWorldPoint(startPos); Node targetNode = grid.NodeFromWorldPoint(targetPos); List<Node> openSet = new List<Node>(); HashSet<Node> closedSet = new HashSet<Node>(); openSet.Add(startNode); while (openSet.Count > 0) { Node currentNode = openSet[0]; for (int i = 1; i < openSet.Count; i++) { if (openSet[i].fCost < currentNode.fCost || (openSet[i].fCost == currentNode.fCost && openSet[i].hCost < currentNode.hCost)) { currentNode = openSet[i]; } } openSet.Remove(currentNode); closedSet.Add(currentNode); if (currentNode == targetNode) { RetracePath(startNode, targetNode); return; } foreach (Node neighbour in grid.GetNeighbours(currentNode)) { if (!neighbour.walkable || closedSet.Contains(neighbour)) { continue; } int newMovementCostToNeighbour = currentNode.gCost + GetDistance(currentNode, neighbour); if (newMovementCostToNeighbour < neighbour.gCost || !openSet.Contains(neighbour)) { neighbour.gCost = newMovementCostToNeighbour; neighbour.hCost = GetDistance(neighbour, targetNode); neighbour.parent = currentNode; if (!openSet.Contains(neighbour)) { openSet.Add(neighbour); } } } } } void RetracePath(Node startNode, Node endNode) { List<Node> path = new List<Node>(); Node currentNode = endNode; while (currentNode != startNode) { path.Add(currentNode); currentNode = currentNode.parent; } path.Reverse(); grid.path = path; } int GetDistance(Node nodeA, Node nodeB) { int dstX = Mathf.Abs(nodeA.gridX - nodeB.gridX); int dstY = Mathf.Abs(nodeA.gridY - nodeB.gridY); if (dstX > dstY) { return 14 * dstY + 10 * (dstX - dstY); } return 14 * dstX + 10 * (dstY - dstX); } } ``` 该脚本的A*寻路算法会在每次Update()函数调用时寻找从起点到终点的最短路径,并将其保存在网格的路径。 实现A*寻路算法需要一个网格,该网格由一系列节点组成。每个节点包含了该节点在网格的位置、该节点到起点的距离(gCost)、

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值