天天看点

Unity A*寻路算法(人物移动AI)

最近闲来无事,从网上看了一些大神的心得,再融合自己的体会,写了一个简单点的寻路算法,废话不多说,直接上代码

<Grid>

using UnityEngine ; using System . Collections ; using System . Collections . Generic ; using System ; /// <summary> /// 格子类型 /// </summary> public enum GridType {         //正常类型         Normal ,         //障碍物类型         Obstacle ,         //起点类型         Start ,         //终点类型         End } /// <summary> /// 格子类(实现IComparable方便排序) /// </summary> public class Grid : IComparable {         //格子坐标x-y         public int x ;         public int y ;         //格子A*三属性f-g-h         public int f ;         public int g ;         public int h ;         //格子类型         public GridType type ;         //格子的归属(父格子)         public Grid parent ;         //构造赋值         public Grid ( int x , int y )        {                this . x = x ;                this . y = y ;        }         /// <summary>         /// 实现排序接口方法         /// </summary>         /// <returns> The to. </returns>         /// <param name="obj"> Object. </param>         public int CompareTo ( object obj )        {                Grid grid = ( Grid ) obj ;                if ( this . f < grid . f ) {                       //升序                       return -1;               }                if ( this . f > grid . f ) {                       //降序                       return 1;               }                return 0;        } }

<MyAStar>

using UnityEngine ; using System . Collections ; using System . Collections . Generic ; public class MyAStar : MonoBehaviour {         /// <summary>         /// 单例脚本         /// </summary>         public static MyAStar instance ;         // 白色格子         public GameObject reference ;         //格子数组         public Grid [,] grids ;         //格子数组对应的参考物(方块)对象         public GameObject [,] objs ;         // 开启和关闭列表         public List < Grid > openList ;         public List < Grid > closeList ;         //目标点坐标         public int targetX ;         public int targetY ;         //起始点坐标         public int startX ;         public int startY ;         //格子行列数         private int row ;         private int colomn ;         //结果栈         private Stack < string > parentList ;         //基础物体         private Transform plane ;         //流颜色参数         private float alpha = 0;         private float incrementPer = 0;         void Awake ()        {                instance = this ;                plane = GameObject . Find ( "Plane" ). transform ;                parentList = new Stack < string > ();                openList = new List < Grid > ();                closeList = new List < Grid > ();        }         void Start ()        {                Init ();                StartCoroutine ( Count ());        }         /// <summary>         /// 初始化操作         /// </summary>         void Init ()        {                //计算行列数                // 单位1,放大20倍,每行放20个格子                int x = ( int )( plane . localScale . x * 20);                int y = ( int )( plane . localScale . z * 20);                row = x ;                colomn = y ;                // 创建20*20的格子                grids = new Grid [ x , y ];                objs = new GameObject [ x , y ];                // 白色方块起始坐标(-2,0,-2)                Vector3 startPos =                       new Vector3 ( plane . localScale . x *-2 , 0, plane . localScale . z *-2 );                //生成参考物体(Cube)                for ( int i = 0; i < x ; i ++) {                       for ( int j = 0; j < y ; j ++) {                             grids [ i , j ] = new Grid ( i , j );                             GameObject item = ( GameObject ) Instantiate ( reference ,                                                     new Vector3 ( i * 0.5f, 0, j * 0.5f) + startPos ,                                                     Quaternion . identity );                                                         item . GetComponentInChildren < Reference > (). x = i ;                             item . GetComponentInChildren < Reference > (). y = j ;                             objs [ i , j ] = item ;                      }               }        }         /// <summary>         /// A*计算         /// </summary>         IEnumerator Count ()        {                //等待前面操作完成                yield return new WaitForSeconds (0.1f);                //添加起始点                openList . Add ( grids [ startX , startY ]);                //声明当前格子变量,并赋初值                Grid currentGrid = openList [0];                //循环遍历路径最小F的点                while ( openList . Count > 0 && currentGrid . type != GridType . End ) {                       //获取此时最小F点,因为移除后[0]表示的不是同一个格子                       currentGrid = openList [0];                       //如果当前点就是目标                       if ( currentGrid . type == GridType . End ) {                             Debug . Log ( "Find" );                             //生成结果                             GenerateResult ( currentGrid );                             StartCoroutine ( ShowResult ());                      }                       //上下左右,左上左下,右上右下,遍历                       for ( int i = -1; i <= 1; i ++) {                             for ( int j = -1; j <= 1; j ++) {                                    // no self                                    if ( i != 0 || j != 0) {                                           //计算坐标                                           int x = currentGrid . x + i ;                                           int y = currentGrid . y + j ;                                           //如果未超出所有格子范围,不是障碍物,不是重复点                                           if ( x >= 0 && y >= 0 && x < row && y < colomn                                              && grids [ x , y ]. type != GridType . Obstacle                                              && ! closeList . Contains ( grids [ x , y ])) {                                                  // 计算G值                                                  int g = currentGrid . g + ( int )( Mathf . Sqrt ( i * i + j * j ) * 10);                                                  //与原G值对照                                                  if ( grids [ x , y ]. g == 0 || grids [ x , y ]. g > g ) {                                                         //更新G值                                                         grids [ x , y ]. g = g ;                                                         // 当前九个格子的父物体都是,中间的格子                                                         grids [ x , y ]. parent = currentGrid ;                                                 }                                                  //计算H值                                                  grids [ x , y ]. h = Manhattan ( x , y );                                                  //计算F值                                                  grids [ x , y ]. f = grids [ x , y ]. g + grids [ x , y ]. h ;                                                  //如果未添加到开启列表                                                  if (! openList . Contains ( grids [ x , y ])) {                                                         //添加                                                         openList . Add ( grids [ x , y ]);                                                 }                                                  //重新排序,把f值小的,放到数组前面,路径选择需要选择F值最小的                                                  openList . Sort (); //                                              foreach (var item in openList) { //                                                     Debug.Log (item.f); //                                              }                                          }                                   }                            }                      }                       //完成遍历添加该点到关闭列表                       closeList . Add ( currentGrid );                       //从开启列表中移除                       openList . Remove ( currentGrid );                       //如果开启列表空,未能找到路径                       if ( openList . Count == 0) {                             Debug . Log ( "Can not Find" );                      }               }        }         /// <summary>         /// 生成结果         /// </summary>         /// <param name="currentGrid"> Current grid. </param>         /// 找到终点之后,才会执行递归,传进去的是最后一个格子         /// 因为先进后出的原则,所以整个进栈的过程是从结尾->开始         /// 所以读取的时候就是从开始->结尾         void GenerateResult ( Grid currentGrid )        {                //如果当前格子有父格子                if ( currentGrid . parent != null ) {                       //添加到父对象栈(即结果栈)                       parentList . Push ( currentGrid . x + "|" + currentGrid . y );                       //递归获取                       GenerateResult ( currentGrid . parent );               }        }         /// <summary>         /// 显示结果         /// </summary>         /// <returns> The result. </returns>         IEnumerator ShowResult ()        {                //等待前面计算完成                yield return null ;                //计算每帧颜色值增量                incrementPer = 1 / ( float ) parentList . Count ;                //展示结果                while ( parentList . Count != 0) {                       //出栈                       /// 因为先进后出的原则,所以整个进栈的过程是从结尾->开始                       /// 所以读取的时候就是从开始->结尾                       string str = parentList . Pop ();                       //等0.3秒                       yield return new WaitForSeconds (0.3f);                       //拆分获取坐标,分割,得到数组                       string [] xy = str . Split ( new char []{ '|' });                       int x = int . Parse ( xy [0]);                       int y = int . Parse ( xy [1]);                       //当前颜色值                       alpha += incrementPer ;                       //以颜色方式绘制路径                       objs [ x , y ]. GetComponentInChildren < MeshRenderer > (). material . color                      = new Color (1 - alpha , alpha , 0, 1);               }                StopAllCoroutines ();        }         /// <summary>         /// 曼哈顿方式计算H值         /// </summary>         /// <param name="x"> The x coordinate. </param>         /// <param name="y"> The y coordinate. </param>         int Manhattan ( int x , int y )        {                return ( int )( Mathf . Abs ( targetX - x ) + Mathf . Abs ( targetY - y )) * 10;        } }

<OnTriggerEnter> using UnityEngine ; using System . Collections ; using UnityEngine . UI ; /// <summary> /// 每个白色小方块都带有此脚本 /// </summary> public class Reference : MonoBehaviour {         //颜色材质区分         public Material startMat ;         public Material endMat ;         public Material obstacleMat ;         //显示信息Text         private Text text ;         //当前格子坐标         public int x ;         public int y ;         //判断当前格子的类型         void OnTriggerEnter ( Collider other )        {                if ( other . name == "Start" ) {                       GetComponent < MeshRenderer > (). material = startMat ;                       MyAStar . instance . grids [ x , y ]. type = GridType . Start ;                       MyAStar . instance . openList . Add ( MyAStar . instance . grids [ x , y ]);                       MyAStar . instance . startX = x ;                       MyAStar . instance . startY = y ;               } else if ( other . name == "End" ) {                       GetComponent < MeshRenderer > (). material = endMat ;                       MyAStar . instance . grids [ x , y ]. type = GridType . End ;                       MyAStar . instance . targetX = x ;                       MyAStar . instance . targetY = y ;               } else if ( other . name == "Obstacle" ) {                       GetComponent < MeshRenderer > (). material = obstacleMat ;                       MyAStar . instance . grids [ x , y ]. type = GridType . Obstacle ;                       MyAStar . instance . closeList . Add ( MyAStar . instance . grids [ x , y ]);               }        }         /// <summary>         /// 鼠标点击显示当前格子基础信息         /// </summary>         void OnMouseDown ()        {                text = GameObject . Find ( "Text" ). GetComponent < Text > ();                text . text = "XY(" + x + "," + y + ")" + "\n" +                "FGH(" + MyAStar . instance . grids [ x , y ]. f + "," +                MyAStar . instance . grids [ x , y ]. g + "," +                MyAStar . instance . grids [ x , y ]. h + ")" ;                text . color = GetComponent < MeshRenderer > (). material . color ;        } }

继续阅读