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 Idemaround(NodeItem node)
    {//获取周围8个格子信息
        List aroundlist = new List();
        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 Openlist = new List();
        HashSet Closelist = new HashSet();
        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 path = new List();
        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;
    }
}

为了方便理解,我和同学画了好久的流程图:unity中使用A*算法寻路典例_第1张图片这是PartA部分

unity中使用A*算法寻路典例_第2张图片

这是PartB部分

unity中使用A*算法寻路典例_第3张图片

可能避免不了有错误。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
这几个很厉害,
我的也有自己的样子unity中使用A*算法寻路典例_第4张图片

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

unity中使用A*算法寻路典例_第5张图片
想要工程的可以私聊我哦!

你可能感兴趣的:(Unity,算法总结,案列学习总结)