A*寻路(3)—— 核心代码

提示:个人学习总结

A*寻路(1)——理论知识

A*寻路(2)—— 代码基础架构

格子类:

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

/// <summary>
/// 格子类型
/// </summary>
public enum E_Node_Type
{
    Walk,//可以走的地方
    Stop,//不能走的地方
}

/// <summary>
/// A*格子类
/// </summary>
public class AStarNode 
{
    //格子对象的坐标
    public int x;
    public int y;

    public float f;//寻路消耗
    public float g;//离起点的距离
    public float h;//离终点的距离
    public AStarNode father;//父对象

    public E_Node_Type type;//格子的类型

    /// <summary>
    /// 构造函数,传入坐标和类型
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="type"></param>
    public AStarNode(int x,int y,E_Node_Type type)
    {
        this.x = x; 
        this.y = y;
        this.type = type;
    }
   

}

A*管理器:
 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
///A*寻路管理类
/// </summary>
public class AStarMgr:Singleton<AStarMgr>
{
    //地图的宽高
    private int mapW;
    private int mapH;

    //地图相关的所有的格子对象容器
    public  AStarNode[,] nodes;
    //开启列表
    private List<AStarNode> openList=new List<AStarNode>();
    //关闭列表
    private List<AStarNode> closeList=new List<AStarNode>();

    private Canvas canvas;

    /// <summary>
    /// 初始化地图信息
    /// </summary>
    /// <param name="w"></param>
    /// <param name="h"></param>
    public void InitMap(int w, int h)
    {

        canvas=GameObject.Find("Canvas").GetComponent<Canvas>();

        mapW = w;
        mapH = h;
        nodes=new AStarNode[w, h];
        //根据宽高创建格子 随机阻挡
        for (int i = 0; i < w; i++)
        { 
            for (int j = 0; j < h; j++)
            {
                AStarNode node = new AStarNode(i, j, Random.Range(0, 100)<20?E_Node_Type.Stop:E_Node_Type.Walk);
                nodes[i,j] = node;
            }
        }
    }
    /// <summary>
    /// 寻路方法 
    /// </summary>
    /// <param name="startPos"></param>
    /// <param name="endPos"></param>
    /// <returns></returns>
    public List<AStarNode> FindPath(Vector2 startPos,Vector2 endPos) 
    {
        //判断这些格子是否是边界 是否是阻挡点 是否在开启关闭列表中 符合条件放入开启列表
        //判断传入的两个点是否合法
        //1.是否在地图范围内
        //不合法 返回null 
        if (startPos.x<0 || startPos.x>=mapW || 
            startPos.y<0 || startPos.y>=mapH ||
            endPos.x < 0 || startPos.x >= mapW ||
            endPos.y < 0 || startPos.y >= mapH )
            return null;
        //合法 得到起点和终点对应的格子
        AStarNode start = nodes[(int)startPos.x,(int)startPos.y];
        AStarNode end = nodes[(int)endPos.x, (int)endPos.y];
        //2.是否是阻挡点
        if (start.type == E_Node_Type.Stop ||
            end.type == E_Node_Type.Stop)
        {
            Debug.Log("开始或结束点是阻挡");
            return null;
        }
        //清空上一次相关的数据 避免他们影响这一次的寻路计算
        closeList.Clear();
        openList.Clear();
        //把开始点放入关闭列表
        start.father = null;
        start.f = 0;
        start.g = 0;
        start.h = 0;
        closeList.Add(start);

       
        while (true)
        {
            //从起点开始 找周围的点 并放入开启列表中
            //左上 x-1 y-1
            FindNearlyNodeToOpenList(start.x - 1, start.y - 1, 1.4f, start, end);
            //上   x y-1
            FindNearlyNodeToOpenList(start.x, start.y - 1, 1, start, end);
            //右上 x+1 y-1
            FindNearlyNodeToOpenList(start.x + 1, start.y - 1, 1.4f, start, end);
            //左   x-1 y
            FindNearlyNodeToOpenList(start.x - 1, start.y, 1f, start, end);
            //右   x+1 y
            FindNearlyNodeToOpenList(start.x + 1, start.y, 1f, start, end);
            //左下 x-1 y+1
            FindNearlyNodeToOpenList(start.x - 1, start.y + 1, 1.4f, start, end);
            //下   x y+1
            FindNearlyNodeToOpenList(start.x, start.y + 1, 1, start, end);
            //右下 x+1 y+1
            FindNearlyNodeToOpenList(start.x + 1, start.y + 1, 1.4f, start, end);

            if (openList.Count == 0)
            {
                Debug.Log("是死路");
                return null;
            }
            //选出开启列表中 寻路消耗最小的点
            //找出最优的点 从开启列表中拿出放入关闭列表 在开启列表中移除
            openList.Sort(SortOpenList);
            closeList.Add(openList[0]);

            //找到这个点 又变成新的起点 进行下一次寻路计算了
            start = openList[0];
            openList.RemoveAt(0);
            //判断这个点是否是终点 是就返回 不是继续找
            if (start == end)
            {
                //找完了 找到路径了
                List<AStarNode> path = new List<AStarNode>();
                path.Add(end);
                while(end.father != null)
                {
                    path.Add(end.father);
                    end = end.father;
                }
                //列表翻转
                path.Reverse();

                return path;
            }
            
        }
        //return null;
    }
    private int SortOpenList(AStarNode a, AStarNode b)
    {
        if (a.f > b.f) return 1;
        else if (a.f == b.f) return 1;
        else return -1;
    }
    /// <summary>
    /// 把附近点放入开启列表中的函数
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    private void FindNearlyNodeToOpenList(int x, int y, float g,AStarNode father,AStarNode end)
    {
        //判断格子是否是边界
        if(x<0 || x>=mapW ||
            y<0 || y>=mapH) 
            return;
        AStarNode node = nodes[x, y];
        //判断这些格子是否是边界 是否是阻挡点 是否在开启关闭列表中 符合条件放入开启列表
        if (node == null ||
            node.type == E_Node_Type.Stop||
            closeList.Contains(node)||
            openList.Contains(node))
            return;

        //计算f值 f=g+h
        //记录father父对象
        node.father = father;
        node.g = father.g + g;
        if (openList.Contains(node))
        {
            float gThis = father.g + g;
            if (gThis < node.g)
            {
                node.g = gThis;
                node.f = node.g + node.h;
                node.father = father;
                return;
            }
            else
            {
                return;
            }
        }
        node.h = Mathf.Abs(end.x - node.x) + Mathf.Abs(end.y - node.y);
        node.f=node.g+node.h;

        //符合条件就放入开启列表中
        openList.Add(node);
    }
}

测试类:
 

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

public class TastAStar : MonoBehaviour
{
    //左上角第一个立方体的位置
    public int baginX=-3;
    public int baginY=5;
    //之后每一个立方体之间的 偏移位置
    public int offsectX=2;
    public int offsectY=2;
    //地图格子的宽高
    public int mapW=10;
    public int mapH=10;

    private Vector2 beginPos = Vector2.right * -1;

    private Dictionary<string, GameObject> cubes = new Dictionary<string, GameObject>();

    private List<AStarNode> list;

    public Material red;
    // Start is called before the first frame update
    void Start()
    {
        AStarMgr.GetInstance().InitMap(mapW, mapH);
        for (int i = 0; i < mapW; i++)
        {
            for (int j = 0; j < mapH; j++)
            {
                GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                obj.transform.position= new Vector3(baginX + i * offsectX, baginY + j * offsectY, 0);
                obj.name=i+"_"+j;
                cubes.Add(obj.name, obj);
                AStarNode node = AStarMgr.GetInstance().nodes[i, j];
                if (node.type == E_Node_Type.Stop)
                {
                    obj.GetComponent<MeshRenderer>().material = red;
                }
            }
        }
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            RaycastHit info;
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            if (Physics.Raycast(ray, out info, 1000))
            {
                
                if (beginPos == Vector2.right * -1)
                {
                    //清理上一次的路径
                    if (list != null)
                    {
                        for (int i = 0; i < list.Count; i++)
                        {
                            cubes[list[i].x + "_" + list[i].y].GetComponent<MeshRenderer>().material.color = Color.white;
                        }
                    }

                    string[] strs = info.collider.gameObject.name.Split('_');
                    //得到行列位置 即开始位置
                    beginPos=new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));
                    info.collider.gameObject.GetComponent<MeshRenderer>().material.color=Color.yellow;
                }
                else//有起点 那就是终点 进行寻路
                {
                    string[] strs = info.collider.gameObject.name.Split('_');
                    Vector2 endPos = new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));

                    list = AStarMgr.GetInstance().FindPath(beginPos, endPos);
                    //不为空 证明成功了
                    if (list != null)
                    {
                        for (int i = 0; i < list.Count; i++)
                        {
                            cubes[list[i].x + "_" + list[i].y].GetComponent<MeshRenderer>().material.color = Color.green;
                        }
                    }
                    //初始化开始点的位置
                    beginPos=Vector2.right * -1;

                }
            }
            

        }
    }
}

测试效果:

原码项目:
UnityA*寻路参考项目资源-CSDN文库

四.参考视频

原视频链接:【【手把手教你】Unity中实现A星寻路算法】https://www.bilibili.com/video/BV147411u7r5?vd_source=42f4411b8309016e682f2d58e46aaba7

猜你喜欢

转载自blog.csdn.net/2302_76830411/article/details/132669348
今日推荐