zoukankan      html  css  js  c++  java
  • unity A*寻路 (三)A*算法

    这里我就不解释A*算法

    如果你还不知道A*算法

    网上有很多简单易懂的例子

    我发几个我看过的链接

    http://www.cnblogs.com/lipan/archive/2010/07/01/1769420.html

    https://zhuanlan.zhihu.com/p/24112879

    我这里就当你会A*算法

    三角网格的A*算法寻路

    需要用到多边形方法

    这里我引入了一个Polygon库

    在一个工具类中调用这个库文件

    如果你想自己写这些逻辑或者有更好的库 可以替换

    using System.Collections.Generic;
    using Polygon;
    
    namespace AStar
    {
        public class AStarTools
        {
            /// <summary>
            /// 判断点在哪个三角形中
            /// </summary>
            /// <param name="point"></param>
            /// <param name="allAStarTriangle"></param>
            /// <returns></returns>
            public static Triangle GetAStarTriangleByPoint(Ponit point, List<Triangle> allAStarTriangle)
            {
                if (allAStarTriangle == null)
                {
                    return new Triangle();
                }
    
                Point Point = new Point(point.x, point.z);
    
                for (int i = 0; i < allAStarTriangle.Count; i++)
                {
                    Triangle AStarTriangle = allAStarTriangle[i];
    
                    List<Point> pointList = new List<Point>();
                    pointList.Add(new Point(AStarTriangle.a.x, AStarTriangle.a.z));
                    pointList.Add(new Point(AStarTriangle.b.x, AStarTriangle.b.z));
                    pointList.Add(new Point(AStarTriangle.c.x, AStarTriangle.c.z));
    
                    if (Utility.CheckPointInPolygon(Point, new Polygon.Polygon(pointList)))
                    {
                        return AStarTriangle;
                    }
                }
                return new Triangle();
            }
        }
    }
    AStarTools

    算法需要用到一些数据 起点 终点  导航网格信息 等等

    我统一保存在一个类中

    using UnityEngine;
    
    namespace AStar
    {
        public class AStarData
        {
            /// <summary>
            /// 开始.
            /// </summary>
            public Ponit start;
            /// <summary>
            /// 终点.
            /// </summary>
            public Ponit end;
            /// <summary>
            /// 导航网格信息
            /// </summary>
            public NavMeshInfo navMeshInfo;
    
    
            /// <summary>
            /// 起点所在的三角形
            /// </summary>
            public Triangle startPlace;
            /// <summary>
            /// 终点所在的三角形
            /// </summary>
            public Triangle endPlace;
            /// <summary>
            /// 起点新建三角形
            /// </summary>
            public Triangle startAStarTriangle;
            /// <summary>
            /// 终点新建三角形
            /// </summary>
            public Triangle endAStarTriangle;
    
            public bool Init(Ponit start, Ponit end, NavMeshInfo navMeshInfo)
            {
                this.start = start;
                this.end = end;
                this.navMeshInfo = navMeshInfo;
    
                startAStarTriangle = new Triangle(start, start, start);
                endAStarTriangle = new Triangle(end, end, end);
    
                startPlace = AStarTools.GetAStarTriangleByPoint(start, navMeshInfo.allTriangle);
                endPlace = AStarTools.GetAStarTriangleByPoint(end, navMeshInfo.allTriangle);
    
                if (startPlace == new Triangle())
                {
                    Debug.Log("起点不在导航网格信息中");
                    return false;
                }
    
                if (endPlace == new Triangle())
                {
                    Debug.Log("终点不在导航网格信息中");
                    return false;
                }
    
                return true;
            }
        }
    }
    AStarData

    最后是我写的A*算法

    using System.Collections.Generic;
    using System.Linq;
    
    namespace AStar
    {
        public delegate void AStarCallback(List<Ponit> list);
    
        public class Pathfinding
        {
            /// <summary>
            /// 数据
            /// </summary>
            AStarData aStarData;
    
    
            /// <summary>
            /// 待计算列表.
            /// </summary>
            List<Triangle> openList;
            /// <summary>
            /// 关闭列表.
            /// </summary>
            List<Triangle> closeList;
    
            /// <summary>
            /// 父级索引.
            /// </summary>
            Dictionary<Triangle, Triangle> parentIndexes;
    
            /// <summary>
            /// G值.
            /// </summary>
            Dictionary<Triangle, float> triangle_G;
            /// <summary>
            /// H值.
            /// </summary>
            Dictionary<Triangle, float> triangle_H;
            /// <summary>
            /// F值.
            /// </summary>
            Dictionary<Triangle, float> triangle_F;
    
            /// <summary>
            /// 回调.
            /// </summary>
            AStarCallback callback;
    
            public void Start(Ponit start, Ponit end, NavMeshInfo navMeshInfo, AStarCallback callback)
            {
                if (callback == null)
                {
                    return;
                }
    
                aStarData = new AStarData();
    
                if (aStarData.Init(start, end, navMeshInfo) == false)
                {
                    return;
                }
    
    
                this.callback = callback;
    
    
                openList = new List<Triangle>();
                closeList = new List<Triangle>();
                parentIndexes = new Dictionary<Triangle, Triangle>();
                triangle_G = new Dictionary<Triangle, float>();
                triangle_H = new Dictionary<Triangle, float>();
                triangle_F = new Dictionary<Triangle, float>();
    
                Core();
            }
    
            /// <summary>
            /// 核心
            /// </summary>
            void Core()
            {
                openList.Add(aStarData.startAStarTriangle);
    
                //开始寻路 寻路到终点结束
                while (!openList.Contains(aStarData.endAStarTriangle) && openList.Count != 0)
                {
                    Triangle AStarTriangle = GetOpenListMin();
    
                    openList.Remove(AStarTriangle);
    
                    closeList.Add(AStarTriangle);
    
                    Explore(AStarTriangle);
                }
    
                List<Triangle> list = new List<Triangle>();
    
                Triangle T = aStarData.endAStarTriangle;
                while (parentIndexes.ContainsKey(T) && parentIndexes[T] != null)
                {
                    list.Add(T);
    
                    T = parentIndexes[T];
                }
    
                Callback(list);
            }
    
            /// <summary>
            /// 获取待计算列表中F值最小的一个.
            /// </summary>
            /// <returns></returns>
            Triangle GetOpenListMin()
            {
                Triangle AStarTriangle = openList[0];
                for (int i = 0; i < openList.Count; i++)
                {
                    if (GetDictionaryValue(triangle_F, AStarTriangle) > GetDictionaryValue(triangle_F, openList[i]))
                    {
                        AStarTriangle = openList[i];
                    }
                }
    
                return AStarTriangle;
            }
    
            /// <summary>
            /// 获取当前三角形周围的三角形.
            /// </summary>
            /// <param name="current"></param>
            /// <param name="end"></param>
            /// <param name="map"></param>
            void Explore(Triangle current)
            {
                //获取当前三角形所有的邻边三角形
                List<Triangle> list = GetRuoundAStarTriangle(current, aStarData);
    
    
                for (int i = 0; i < list.Count; i++)
                {
                    Triangle AStarTriangle = list[i];
    
                    //去掉当前三角形
                    if (AStarTriangle == current)
                    {
                        continue;
                    }
    
                    //去掉已经关闭的三角形
                    if (closeList.Contains(AStarTriangle))
                    {
                        continue;
                    }
    
                    //如果不在待检测的集合  则加入待检测集合
                    if (!openList.Contains(AStarTriangle))
                    {
                        SetParentIndexes(AStarTriangle, current);
    
                        SetDictionaryValue(triangle_G, AStarTriangle, GetG(AStarTriangle, current));
                        SetDictionaryValue(triangle_H, AStarTriangle, GetH(AStarTriangle));
    
                        openList.Add(AStarTriangle);
                    }
                    //如果在待检测的集合  则判断是否修改父级
                    else
                    {
                        float G = GetDictionaryValue(triangle_G, AStarTriangle);
                        float H = GetDictionaryValue(triangle_H, AStarTriangle);
                        float F = GetG(AStarTriangle, current) + GetH(AStarTriangle);
    
                        if (G + H > F)
                        {
                            SetParentIndexes(AStarTriangle, current);
                            SetDictionaryValue(triangle_G, AStarTriangle, GetG(AStarTriangle, current));
                            SetDictionaryValue(triangle_H, AStarTriangle, GetH(AStarTriangle));
    
                            openList.Remove(AStarTriangle);
                        }
                    }
                }
            }
    
    
    
    
    
            /// <summary>
            /// 获取G值.
            /// </summary>
            /// <param name="grid"></param>
            /// <param name="parent"></param>
            /// <returns></returns>
            float GetG(Triangle grid, Triangle parent)
            {
                float distance = Ponit.Distance(grid.centroid, parent.centroid);
    
                distance += GetDictionaryValue(triangle_G, parent);
    
                return distance;
            }
    
            /// <summary>
            /// 获取H值.
            /// </summary>
            /// <param name="grid"></param>
            /// <param name="end"></param>
            /// <returns></returns>
            float GetH(Triangle grid)
            {
                float distance = Ponit.Distance(grid.centroid, aStarData.end);
    
                return distance;
            }
    
    
    
    
            /// <summary>
            /// 添加父级索引.
            /// </summary>
            /// <param name="current"></param>
            /// <param name="parent"></param>
            void SetParentIndexes(Triangle current, Triangle parent)
            {
                if (parentIndexes.ContainsKey(current))
                {
                    parentIndexes[current] = parent;
                }
                else
                {
                    parentIndexes.Add(current, parent);
                }
            }
    
    
    
    
            /// <summary>
            /// 回调
            /// </summary>
            /// <param name="listAStarTriangle"></param>
            void Callback(List<Triangle> listAStarTriangle)
            {
                if (callback == null || listAStarTriangle == null)
                {
                    return;
                }
    
    
                listAStarTriangle.Reverse();
    
                List<Ponit> list = new List<Ponit>();
    
                //进距离移动 不超过一个三角形
                if (listAStarTriangle.Count == 1)
                {
                    list.Add(aStarData.end);
                    callback(list);
                    return;
                }
    
                for (int i = 0; i < listAStarTriangle.Count; i++)
                {
                    list.Add(listAStarTriangle[i].centroid);
                }
    
                callback(list);
    
            }
    
    
    
            /// <summary>
            /// 获取周围的三角形
            /// </summary>
            /// <param name="current"></param>
            /// <returns></returns>
            public static List<Triangle> GetRuoundAStarTriangle(Triangle current, AStarData aStarData)
            {
                //该点为开始点 则获取该点三角重心来取值
                if (current.a == aStarData.start && current.b == aStarData.start)
                {
                    current = aStarData.startPlace;
                }
    
                //获取三角形所有的邻边三角形
                List<Triangle> list = new List<Triangle>();
    
                list.AddRange(GetListAStarTriangle(current.a, aStarData.navMeshInfo.pointIndexes));
    
                list.AddRange(GetListAStarTriangle(current.b, aStarData.navMeshInfo.pointIndexes));
    
                list.AddRange(GetListAStarTriangle(current.c, aStarData.navMeshInfo.pointIndexes));
    
                //去掉重复的三角形
                list = list.Distinct().ToList();
    
                if (list.Contains(aStarData.endPlace))
                {
                    list.Add(aStarData.endAStarTriangle);
                }
    
                return list;
            }
    
            /// <summary>
            /// 获取顶点对应的三角形列表.
            /// </summary>
            /// <param name="ponit</param>
            /// <param name="map"></param>
            /// <returns></returns>
            public static List<Triangle> GetListAStarTriangle(Ponit ponit, Dictionary<Ponit, List<Triangle>> pointIndexes)
            {
                List<Triangle> list = null;
    
                if (pointIndexes == null)
                {
                    return list;
                }
    
                if (pointIndexes.ContainsKey(ponit))
                {
                    list = pointIndexes[ponit];
                }
    
                return list;
            }
    
            /// <summary>
            /// 设置字典的值.
            /// </summary>
            /// <param name="data"></param>
            /// <param name="key"></param>
            /// <param name="value"></param>
            public static void SetDictionaryValue(Dictionary<Triangle, float> data, Triangle key, float value)
            {
                if (data == null)
                {
                    return;
                }
    
                if (data.ContainsKey(key))
                {
                    data[key] = value;
                }
                else
                {
                    data.Add(key, value);
                }
            }
    
            /// <summary>
            /// 获取字典的值.
            /// </summary>
            /// <param name="data"></param>
            /// <param name="key"></param>
            /// <returns></returns>
            public static float GetDictionaryValue(Dictionary<Triangle, float> data, Triangle key)
            {
                if (data == null)
                {
                    return 0;
                }
    
                if (!data.ContainsKey(key))
                {
                    data.Add(key, 0);
                }
                return data[key];
            }
        }
    }
    AStar

    我们创建一个Capsule游戏对象 

    让他成为我们需要移动的主角

    挂上简单的移动代码

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    using AStar;
    
    public delegate void CharacterMoveCallback();
    
    /// <summary>
    /// 角色移动
    /// </summary>
    public class CharacterMove : MonoBehaviour
    {
        /// <summary>
        /// 目标
        /// </summary>
        public Vector3 target;
        /// <summary>
        /// 速度
        /// </summary>
        public float speed  =  2;
        /// <summary>
        /// 目标距离
        /// </summary>
        public float distance = 0.05f;
    
    
    
        /// <summary>
        /// 移动经过的点
        /// </summary>
        List<Vector3> points;
        /// <summary>
        /// 回调
        /// </summary>
        CharacterMoveCallback callback;
    
        private void Awake()
        {
            target = transform.position;
        }
    
    
        public void Move(Ponit vector3, CharacterMoveCallback callback = null)
        {
            target = Ponit.Convert(vector3);
            this.callback = callback;
        }
    
        public void Move(List<Ponit> points, CharacterMoveCallback callback = null)
        {
            this.points = new List<Vector3>();
    
            for (int i = 0; i < points.Count; i++)
            {
                this.points.Add(Ponit.Convert(points[i]));
            }
            this.callback = callback;
    
            GetTarget();
        }
    
        void GetTarget()
        {
            if (points != null && points.Count >= 1)
            {
                target = points[0];
                points.Remove(points[0]);
    
                Debug.Log("前进点 :" + target);
            }
        }
    
        void Callback()
        {
            if (callback != null)
            {
                callback();
                callback = null;
            }
        }
    
        void Update()
        {
            //当前位置与目标距离小于间距 移动停止
            if (Vector3.Distance(target, transform.position) < distance)
            {
                Callback();
                return;
            }
    
            //获取移动向量;
            Vector3 vector = target - transform.position;
            vector = vector.normalized;
    
            //移动
            transform.position += vector * Time.deltaTime * speed;
    
            //如果到达目标点则替换下一个目标
            if (Vector3.Distance(target, transform.position) < distance)
            {
                GetTarget();
            }
    
        }
    }
    CharacterMove

    再挂上一个简单的开始测试代码

    using UnityEngine;
    using AStar;
    using System.Collections.Generic;
    
    public class test : MonoBehaviour
    {
        NavMeshInfo navMeshInfo;
    
        void Start()
        {
            NavMeshLoad navMeshLoad = new NavMeshLoad();
    
            navMeshInfo = navMeshLoad.Load(Application.dataPath + "/AStar/obj/test.obj");
        }
    
        private void Update()
        {
            if (Input.GetMouseButtonUp(0))
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    
                RaycastHit hit;//
                if (Physics.Raycast(ray, out hit))//函数是对射线碰撞的检测
                {
                    Vector3 Point = hit.point;//得到碰撞点的坐标
    
                    Debug.Log("开始点: " + transform.position);
                    Debug.Log("目标点: " + Point);
                    Debug.Log("目标点对象: " + hit.transform.name);
    
                    Vector3 start = transform.position;
                    Vector3 end = Point;
    
                    NavMeshHit tmpNavMeshHit;
    
                    if (NavMesh.SamplePosition(start, out tmpNavMeshHit, 5.0f, NavMesh.AllAreas))
                    {
                        start = tmpNavMeshHit.position;
                    }
    
                    Debug.Log("修改后 开始点: " + start);
    
                    StartNavigation(start, end, navMeshInfo);
                }
            }
        }
    
    
        public void StartNavigation(Vector3 start, Vector3 end, NavMeshInfo navMeshInfo)
        {
            if (navMeshInfo == null)
            {
                return;
            }
    
            Pathfinding pathfinding = new Pathfinding();
    
            Ponit startPonit = new Ponit(start);
            Ponit endPonit = new Ponit(end);
    
    
            pathfinding.Start(startPonit, endPonit, navMeshInfo, (List<Ponit> list) =>
           {
               CharacterMove controller = GetComponent<CharacterMove>();
    
               controller.Move(list, () =>
               {
                   Debug.Log("寻路结束");
               });
    
           });
        }
    
    
    }
    test

    运行 点击地面  就可以看到Capsule寻路自动行走了

    工程下载:

    链接: https://pan.baidu.com/s/1qY_zUrqIHB6W4K8wC3PxWQ 密码: iipb

  • 相关阅读:
    UE4_武器4_武器切换(下)
    UE4_武器3_武器切换(上)
    UE4_武器2_矛的攻击与动画
    UE4_武器1_创建武器
    Java知识体系复习笔记
    今日份学习: 毕设。。。。
    笔记:Spring security认证
    一个项目的记录:多人博客
    今日份学习: Spring
    今日份学习:部署?
  • 原文地址:https://www.cnblogs.com/zouqiang/p/8758721.html
Copyright © 2011-2022 走看看