Unity算法——A*(AStar)寻路算法概要及简单应用
2018-01-19 20:45
471 查看
非常简陋的版本的GIF图,放在开头。
![](https://img-blog.csdn.net/20180119180100135?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSHRsYXM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
前言:
再Unity中寻路导航是游戏开发的最基本的需求之一,但是使用unity自带的NavMeshAgent方法来做的话经常会达不到我们想要的效果,首先是Nav会极大的消耗性能,从游戏优化的角度来看的话是不推荐使用Nav的,再就是Nav是通过渲染网格来实现的,有时候会寻路不准,如果期望方向和期望速度不准确的话还会出现其他的 一些情况,那么如何运用A*来寻路导航呢,我们下面会给大家讲到
什么是A*寻路算法:
为什么这个算法被称为A*算法呢,*又是什么呢,以一个网格为中心点,他周围八个方向的网格就是*,
![](https://img-blog.csdn.net/20180119182331483?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSHRsYXM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
A寻路算法的估量代价*
在A*算法中核心的寻路依据就是估量代价,在A*中通常用 F 表示。F
= G + H
其中G表示当前点到起始点的估量代价,H表示当前点到终点的代价。
![](https://img-blog.csdn.net/20180119183038027?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSHRsYXM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
(起始点周围的八个点)
每个点里面的三个数字分别为:1.左下角是距离起始点的估量代价,记为G。A距离中心点的距离为1的直线距离,B距离中心点的距离为根号2,约1.4,在这里基础单位为10的话,A的起始点估量代价G=10,B的起始点估量代价G=14 。 2.右下角是距离终点的估量代价,记为H,为该点到终点的步数既几步可达终点。3.左上角是综合估量代价,记为F=G+H,起始点估量代价与终点估量代价的和;
![](https://img-blog.csdn.net/20180119183601821?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSHRsYXM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
(网图,其中一些估量代价是错的,仅供参考,侵删)
A*算法的核心是两个集合分别为开放列表与关闭列表:Open List,CloseList
原理:
实现步骤:
1.把起始格添加到开启列表。
2.重复如下的工作:
a) 寻找开启列表中估量代价F值最低的格子。我们称它为当前格。
b) 把它切换到关闭列表。
c) 对相邻的8格中的每一个进行如下操作
* 如果它不可通过或者已经在关闭列表中,略过它。反之如下。
* 如果它不在开启列表中,把它添加进去。把当前格作为这一格的父节点。记录这一格的F,G,和H值。
* 如果它已经在开启列表中,用G值为参考检查新的路径是否更好。更低的G值意味着更好的路径。如果是这样,就把这一格的父节点改成当前格,并且重新计算这一格的G和F值。如果你保持你的开启列表按F值排序,改变之后你可能需要重新对开启列表排序。
d) 停止,当你
* 把目标格添加进了关闭列表(注解),这时候路径被找到,或者
* 没有找到目标格,开启列表已经空了。这时候,路径不存在。
3.保存路径。从目标格开始,沿着每一格的父节点移动直到回到起始格。这就是你的路径。
代码实现:
1.AStarController
2.GridController
前言:
再Unity中寻路导航是游戏开发的最基本的需求之一,但是使用unity自带的NavMeshAgent方法来做的话经常会达不到我们想要的效果,首先是Nav会极大的消耗性能,从游戏优化的角度来看的话是不推荐使用Nav的,再就是Nav是通过渲染网格来实现的,有时候会寻路不准,如果期望方向和期望速度不准确的话还会出现其他的 一些情况,那么如何运用A*来寻路导航呢,我们下面会给大家讲到
什么是A*寻路算法:
为什么这个算法被称为A*算法呢,*又是什么呢,以一个网格为中心点,他周围八个方向的网格就是*,
A寻路算法的估量代价*
在A*算法中核心的寻路依据就是估量代价,在A*中通常用 F 表示。F
= G + H
其中G表示当前点到起始点的估量代价,H表示当前点到终点的代价。
(起始点周围的八个点)
每个点里面的三个数字分别为:1.左下角是距离起始点的估量代价,记为G。A距离中心点的距离为1的直线距离,B距离中心点的距离为根号2,约1.4,在这里基础单位为10的话,A的起始点估量代价G=10,B的起始点估量代价G=14 。 2.右下角是距离终点的估量代价,记为H,为该点到终点的步数既几步可达终点。3.左上角是综合估量代价,记为F=G+H,起始点估量代价与终点估量代价的和;
(网图,其中一些估量代价是错的,仅供参考,侵删)
A*算法的核心是两个集合分别为开放列表与关闭列表:Open List,CloseList
原理:
从中心位置对相邻格子 假设A是起始格子 OpenList CloseList A A1 ... A6 A7 从中心位置对相邻8个格子进行判断最小代价,将A移除并添加进CloseList中,并对OpenList进行排序 假设最小代价是是A4,将A4放在OpenList表头,并查找A4周围的8个格子,A41-A48 OpenList CloseList A4 A A1 ... A6 A7 A41 A42 ... A48 将A4从Open中移除并添加到CloseList中, 对OpenList进行排序,假设A42的最小代价最小 将A42放在Open的表头,并查询A42相邻8个格子 OpenList CloseList A42 A A1 A4 ... A6 A7 A41 ... A48 ........................... OpenList CloseList A4234 A A1 A4 ... A42 A6 A423 A7 A4234 A41 ... A48 最后可能得到的CloseList如上所示, A4->A4234就是A*算法得到的最短路径
实现步骤:
1.把起始格添加到开启列表。
2.重复如下的工作:
a) 寻找开启列表中估量代价F值最低的格子。我们称它为当前格。
b) 把它切换到关闭列表。
c) 对相邻的8格中的每一个进行如下操作
* 如果它不可通过或者已经在关闭列表中,略过它。反之如下。
* 如果它不在开启列表中,把它添加进去。把当前格作为这一格的父节点。记录这一格的F,G,和H值。
* 如果它已经在开启列表中,用G值为参考检查新的路径是否更好。更低的G值意味着更好的路径。如果是这样,就把这一格的父节点改成当前格,并且重新计算这一格的G和F值。如果你保持你的开启列表按F值排序,改变之后你可能需要重新对开启列表排序。
d) 停止,当你
* 把目标格添加进了关闭列表(注解),这时候路径被找到,或者
* 没有找到目标格,开启列表已经空了。这时候,路径不存在。
3.保存路径。从目标格开始,沿着每一格的父节点移动直到回到起始格。这就是你的路径。
代码实现:
1.AStarController
using System.Collections; using System.Collections.Generic; using UnityEngine; public class AStarController : MonoBehaviour { //起点坐标 public int startPosX, startPosY; //终点坐标 public int endPosX, endPosY; //障碍物比率 public int obstacleRate; private GameObject gridPrefab; private List<Grid> openList; private List<Grid> closeList; //结果栈 private Stack<Grid> result; //所有格子数组 private Grid[,] allGrids = null; void Awake() { result = new Stack<Grid> (); openList = new List<Grid> (); closeList = new List<Grid> (); //设置数组长度 allGrids = new Grid[(int)(transform.localScale.x * 20),(int)(transform.localScale.z * 20)]; } void Start() { gridPrefab = Resources.Load<GameObject> ("Grid"); //遍历生成格子 for (int i = 0; i < transform.localScale.x * 20; i++) { for (int j = 0; j < transform.localScale.z * 20; j++) { //生成 Grid currentGrid = Instantiate (gridPrefab). GetComponent<Grid>(); //计算偏移量 Vector2 offset = new Vector2 (-4.7f * transform. localScale.x,-4.7f * transform.localScale.z); //设置方块的世界坐标 currentGrid.transform.position = new Vector3 ( offset.x + i * 0.5f, 0, offset.y + j * 0.5f); //设置格子坐标 currentGrid.x = i; currentGrid.y = j; //存储起来 allGrids[i,j] = currentGrid; //随机障碍物 int r = Random.Range(1,101); if (r <= obstacleRate) { currentGrid.MyGridType = GridType.Obstacle; } } } //设置起点和终点 allGrids[startPosX,startPosY].MyGridType = GridType.Start; allGrids [endPosX, endPosY].MyGridType = GridType.End; //调用AStar计算 AStarCount(); } /// <summary> /// A*计算 /// </summary> void AStarCount() { //将起点放置到OpenList openList.Add (allGrids [startPosX, startPosY]); //获取当前要发现的中心格子 Grid currentGrid = openList[0]; //循环递归 //开启列表中有对象&&当前的中心不是终点 while (openList.Count > 0 && currentGrid.MyGridType != GridType.End) { //重新排序 openList.Sort(); //获取新的中心格子 currentGrid = openList[0]; //判断最新的格子是否是终点 if (currentGrid.MyGridType == GridType.End) { ///TODO:生成结果 GetParent(currentGrid); return; } //上下左右,左上右上左下右下 for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (i != 0 || j != 0) { //获取新格子的格子坐标 int x = currentGrid.x + i; int y = currentGrid.y + j; //判断格子坐标合法 //前四个条件判断坐标的合法性 //新格子不能是障碍物 //新格子没有被遍历过 if (x > 0 && y > 0 && x < allGrids.GetLength (0) && y < allGrids.GetLength (1) && allGrids [x, y].MyGridType != GridType.Obstacle && !closeList.Contains (allGrids [x, y])) { //计算G值 int g = (int)(currentGrid.G + Mathf.Sqrt(Mathf.Abs(i) + Mathf.Abs(j))*10); //判断新格子是否被遍历过 //如果被遍历过,判断当前G值是否比之前的更小 if (allGrids [x, y].G == 0 || g < allGrids [x, y].G) { //更新G值 allGrids[x,y].G = g; //更新父格子 allGrids[x,y].parent = currentGrid; } //计算H allGrids[x,y].H = (Mathf.Abs(x - endPosX) + Mathf.Abs(y- endPosY)) * 10; //计算F allGrids[x,y].F = allGrids[x,y].G + allGrids[x,y].H; //加入到开启列表 if (!openList.Contains (allGrids [x, y])) { Debug.Log (1); openList.Add (allGrids [x, y]); } } } } } //将当前格子移除OpenList openList.Remove(currentGrid); //放到CloseList里 closeList.Add(currentGrid); //OpenList空了 if (openList.Count == 0) { Debug.Log ("Can Not Arrave!!!"); } } } private void GetParent(Grid current) { //进栈 result.Push (current); //判断是否继续递归 if (current.parent != null) { GetParent (current.parent); } else { //展示结果 StartCoroutine (ShowResult ()); } } IEnumerator ShowResult() { //获取总长度 int resultCount = result.Count; while (result.Count > 0) { yield return new WaitForSeconds(0.1f); //出栈 Grid currentResultGrid = result.Pop(); //计算比例 float scale = (resultCount - result.Count)/(float)resultCount; //上色 Color currentC = Color.Lerp(Color.red,Color.green,scale); currentResultGrid.SetColor(currentC); } } void Update() { if (Input.GetKeyDown (KeyCode.Space)) { UnityEngine.SceneManagement.SceneManager.LoadScene (0); } } }
2.GridController
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; public enum GridType { //正常类型 Normal, //障碍物类型 Obstacle, //起点类型 Start, //终点类型 End } public class GridController : MonoBehaviour,IComparable { //坐标 public int x ,y; //FGH public int F, G, H; //坐标 public GridController parent; //格子类型 private GridType gridType; public GridType myGridType { get { return gridType; } set { gridType = value; //设置显示颜色 Color tempColor = Color.white; switch (gridType) { case GridType.Start: tempColor = Color.red; break; case GridType.End: tempColor = Color.green; break; case GridType.Obstacle: tempColor = Color.blue; break; default: break; } SetColor(tempColor); } } private MeshRenderer meshRenderer; private void Awake() { meshRenderer = GetComponent<MeshRenderer>(); } public void SetColor(Color c) { meshRenderer.material.color = c; } public int CompareTo(object obj) { GridController target = obj as GridController; if (F < target.F) { return -1; }else if (F > target.F) { return 1; }else { return 0; } } }
相关文章推荐
- Unity A*寻路三个简单实用的算法
- Unity A*寻路三个简单实用的算法
- 光滑寻路算法在绘图中的应用
- 用简单直白的方式讲解A星寻路算法原理
- Unity IOC容器的简单应用
- 神经网络(BP)算法Python实现及简单应用
- Java写一条吃满屏幕的贪吃蛇(A*自动寻路算法和一些简单的策略) 二
- SVM算法简单应用
- PHP实现的简单排列组合算法应用示例
- JS中数组的应用与简单算法(冒泡排序)
- sklearn库之各分类算法简单应用
- Unity自动寻路的简单演示
- STL在算法比赛中简单应用
- WWW在unity中的简单应用实践
- 数据结构与算法学习笔记——堆栈及其应用(10以内简单四则计算器)
- unity 委托事件简单应用
- Java写一条吃全屏幕的贪吃蛇(A*自动寻路算法和一些简单的策略) 三
- Hololens入门之使用Unity开发一个简单的应用
- 【Unity】12.2 导航网格寻路简单示例
- 关于A*寻路在Unity3D里面的简单应用