A*寻路

牢记A*寻路的核心

F(n) = G(n) + H(n)
G(n): 从起始位置到n位置所需的实际消耗
H(n): 从n位置到目标位置的预计消耗
所以F(n)就是从起始位置经过n位置到目标位置的路径总消耗
A*寻路的过程就是不断找最小F(n)的过程

A*寻路流程

1.加入“开启列表”
先将起点作为当前检查点加入“开启列表”,“开启列表”是一个等待检查方格的列表,起点格子是最开始的检查点
2.检查周围
将当前检查点周围可到达的点加入“开启列表”,并设置它们的"父节点"为当前的检查点,并且计算出这些点的G(n)、H(n)、F(n)值
如果周围的点已经有在“开启列表” 中,则我们需要对比新旧路径的G值,如果新路径的G值更小,则使用新路径,需要更新“父节点”
如果是障碍物,则忽略
3.移入“关闭列表”
将这个检查点从“开启列表”移到“关闭列表”,"关闭列表"中存放的都是不需要再次检查的格子点
4.继续找直到找不到或者找到为止
再从“开启列表”中找出F值最小的格子,然后从上述的第1步开始重复继续,直到找到的F值最小的格子为终点或者“开启列表”中没有格子了,即为结束

几点注意点

  1. H(n)预计消耗的计算的准确度非常重要
    由于G(n)是已经走过的路,所需的消耗是明确的,只有H(n)是预计的,预计的越准,我们就能越快的找到目标位置,减少计算量,也能更贴近最短路径
  2. A*寻路的结果并不一定是最短
    由于H(n)是预计的消耗,这个预计的消耗可能会出现与实际情况相差较大的情况,这个时候寻路结果可能不是最短的

纸上得来终觉浅,绝知此事要躬行

简单做了个示例演示A*寻路的过程,以辅助理解
地图随机生成指定数量的障碍物,随机指定起始位置和终点位置
Game视图 Esc重置地图 Space 执行一步寻路步骤
白色:未检查过的格子
黑色:障碍物
黄色:在“开启列表”中的格子
灰色:在“关闭列表”中的格子
红色:目标格子
绿色:当前正在检查的格子
绿色链:最终的寻路结果
格子左上角:G值
格子右上角:H值
格子左下角:F值
格子右下角:坐标
箭头:指向寻路过程中的父格子

这里随机两次寻路过程来看下:


看下最终的寻路图

代码

完整工程:
Unity版本:2020.3.10

AStarGrid

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class AStarGrid
{public int X { get; set; }  //坐标Xpublic int Y { get; set; }  //坐标Ypublic float F { get { return G + H; } }  public float G { get; set; }public float H { get; set; }public bool IsObstacle { get; set; }public AStarGrid ParentGrid { get; set; }public AStarGrid(int x, int y){this.X = x;this.Y = y;}public void Reset(){G = 0;H = 0;IsObstacle = false;ParentGrid = null;}
}

AStarMap

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class AStarMap
{AStarGrid[,] map;int mapWidth;int mapHeight;//开启列表Dictionary<int, AStarGrid> openList = new Dictionary<int, AStarGrid>();//关闭列表Dictionary<int, AStarGrid> closeList = new Dictionary<int, AStarGrid>();public AStarMap(int mapWidth, int mapHeight){this.mapWidth = mapWidth;this.mapHeight = mapHeight;map = new AStarGrid[mapWidth, mapHeight];for (int w = 0; w < mapWidth; w++){for (int h = 0; h < mapHeight; h++){map[w, h] = new AStarGrid(w, h);}}}public void SetObstacle(int x, int y, bool isObstacle){map[x, y].IsObstacle = isObstacle;}public AStarGrid GetGrid(int x, int y){return map[x, y];}/// <summary>/// 直接寻路,返回寻路结果/// </summary>/// <param name="startX"></param>/// <param name="startY"></param>/// <param name="targetX"></param>/// <param name="targetY"></param>/// <returns></returns>public List<AStarGrid> FindPath(int startX, int startY, int targetX, int targetY){openList.Clear();closeList.Clear();var startGrid = map[startX, startY];var targetGrid = map[targetX, targetY];var grid = startGrid;AddToOpenList(grid, null, targetGrid);  //先将起点加入“开启列表”,“开启列表”是一个等待检查方格的列表,起点格子是最开始的检查点while (grid != null)  //null时意味着找不到路径{if (grid == targetGrid)  //找到了最终目标点{break;}//将当前检查点周围可到达的点加入“开启列表”,并设置它们的"父节点"为当前的检查点 //在这个过程中我们需要更新格子的F、G、H的值,F(n) = G(n) + H(n)//G(n): 从起始点到n点所需的消耗//H(n): 从n点到目标点预计的消耗//F(n): 当前经过n点的这条路径的总消耗//至于如何计算G、H值,需要自己定,G值是确定的,而H值是预计的,因此H值的计算准确直接会影响到寻路的效率(预计耗费有很多计算方法,我们要尽量找出最适合的计算方法,这样子才能更快速的找出“最短”路径)AddAroundGridToOpenList(grid, targetGrid);//将这个检查点移至“关闭列表”, "关闭列表"中存放的都是不需要再次检查的格子点MoveGridToCloseFromOpen(grid);//从“开启列表”中找出F值最低的格子作为下一个检查点,F值最低代表着可能是最快到达的路径grid = FindMinFGrid();}List<AStarGrid> path = new List<AStarGrid>();while (grid != null){//通过不断的连接父节点,这样子列表的格子就是最终的路径path.Add(grid);grid = grid.ParentGrid;}return path;}#region 一步一步寻路演示AStarGrid pathStartGrid, pathTargetGrid, pathCurrentGrid;List<AStarGrid> pathResult = new List<AStarGrid>();public void SetFindPath(int startX, int startY, int targetX, int targetY){openList.Clear();closeList.Clear();pathResult.Clear();pathStartGrid = map[startX, startY];pathTargetGrid = map[targetX, targetY];pathCurrentGrid = pathStartGrid;AddToOpenList(pathCurrentGrid, null, pathTargetGrid);}/// <summary>/// 执行一次寻路步骤/// </summary>/// <returns>寻路是否完成</returns>public bool DoFindPathStep(){if (pathCurrentGrid == null || pathCurrentGrid == pathTargetGrid)return true;AddAroundGridToOpenList(pathCurrentGrid, pathTargetGrid);MoveGridToCloseFromOpen(pathCurrentGrid);pathCurrentGrid = FindMinFGrid();if (pathCurrentGrid == null)return true;if (pathCurrentGrid == pathTargetGrid){while (pathCurrentGrid != null){pathResult.Add(pathCurrentGrid);pathCurrentGrid = pathCurrentGrid.ParentGrid;}return true;}return false;}public List<AStarGrid> GetPathResult(){return pathResult;}public bool IsCurrentCheckGrid(AStarGrid grid){return grid == pathCurrentGrid;}public bool IsTargetGrid(AStarGrid grid){return grid == pathTargetGrid;}
#endregionpublic bool IsInOpenList(AStarGrid grid){return openList.ContainsKey(GetGridIndex(grid));}public bool IsInCloseList(AStarGrid grid){return closeList.ContainsKey(GetGridIndex(grid));}void AddToOpenList(AStarGrid grid, AStarGrid parentGrid, AStarGrid targetGrid){int index = GetGridIndex(grid);if (openList.ContainsKey(index)){float newG = ComputeG(grid, parentGrid);if (newG < grid.G){grid.ParentGrid = parentGrid;grid.G = newG;}}else{openList.Add(index, grid);grid.ParentGrid = parentGrid;UpdateGridGH(grid, targetGrid);}}int GetGridIndex(AStarGrid grid){return grid.Y * mapWidth + grid.X;}int GetGridIndex(int x, int y){return y * mapWidth + x;}void AddAroundGridToOpenList(AStarGrid grid, AStarGrid targetGrid){int cx, cy;AStarGrid g;for (int x = -1; x <= 1; x++){for (int y = -1; y <= 1; y++){cx = grid.X + x;cy = grid.Y + y;if (cx >= 0 && cx < mapWidth && cy >= 0 && cy < mapHeight){g = map[cx, cy]; if (!g.IsObstacle && !closeList.ContainsKey(GetGridIndex(g))){AddToOpenList(g, grid, targetGrid);}}}}}void MoveGridToCloseFromOpen(AStarGrid grid){int index = GetGridIndex(grid);openList.Remove(index);if (!closeList.ContainsKey(index)){closeList.Add(index, grid);}}void UpdateGridGH(AStarGrid grid, AStarGrid targetGrid){var parentGrid = grid.ParentGrid;grid.G = ComputeG(grid, parentGrid);grid.H = Mathf.Sqrt((float)((targetGrid.X - grid.X) * (targetGrid.X - grid.X) + (targetGrid.Y - grid.Y) * (targetGrid.Y - grid.Y)));}float ComputeG(AStarGrid grid, AStarGrid parentGrid){float g = 0;if (parentGrid != null){g = parentGrid.G + Mathf.Sqrt((float)((parentGrid.X - grid.X) * (parentGrid.X - grid.X) + (parentGrid.Y - grid.Y) * (parentGrid.Y - grid.Y)));}return g;}AStarGrid FindMinFGrid(){AStarGrid minFGrid = null;foreach (var grid in openList.Values){if (minFGrid == null){minFGrid = grid;}else{if (grid.F < minFGrid.F){minFGrid = grid;}}}return minFGrid;}}

GridCtrl

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class GridCtrl : MonoBehaviour
{public Text txtF;public Text txtG;public Text txtH;public Text txtCoordinate;public RectTransform directionObj;public Image bg;AStarMap map;AStarGrid grid;public void SetGrid(AStarGrid grid, AStarMap map){this.map = map;this.grid = grid;var rect = (transform as RectTransform).rect;transform.position = new Vector3((rect.width + 5) * grid.X , (rect.height + 5) * grid.Y + 5, 0);}public void UpdateShow(){txtF.text = grid.F.ToString("f2");txtG.text = grid.G.ToString("f2");txtH.text = grid.H.ToString("f2");txtCoordinate.text = string.Format("({0},{1})", grid.X, grid.Y);if (grid.ParentGrid != null){Vector2 parentGridDir = new Vector2(grid.ParentGrid.X - grid.X, grid.ParentGrid.Y - grid.Y);float angle = Vector2.SignedAngle(parentGridDir, new Vector2(1, 0));directionObj.rotation = Quaternion.Euler(0, 0, -angle);directionObj.gameObject.SetActive(true);}else{directionObj.gameObject.SetActive(false);}if(grid.IsObstacle){bg.color = Color.black;}else if(map.IsCurrentCheckGrid(grid)){bg.color = Color.green;}else if(map.IsTargetGrid(grid)){bg.color = Color.red;}else if(map.IsInOpenList(grid)){bg.color = Color.yellow;}else if(map.IsInCloseList(grid)){bg.color = Color.grey;}else{bg.color = Color.white;}}public void SetBgColor(Color color){bg.color = color;}
}

AStarMapGenerator

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class AStarMapGenerator : MonoBehaviour
{public int mapWidth = 5;public int mapHeight = 5;public GameObject GridPrefab;public Vector2 startGrid;public Vector2 targetGrid;public bool isRandomMap;public int randomObstacleCount;public Vector2[] obstacles;GridCtrl[,] gridCtrlMap;AStarMap map;void Start(){GenerateMap();}void Update(){if (Input.GetKeyDown(KeyCode.Space)){DoPathFindStep();}if (Input.GetKeyDown(KeyCode.Escape)){ResetMap();}}public void GenerateMap(){map = new AStarMap(mapWidth, mapHeight);gridCtrlMap = new GridCtrl[mapWidth, mapHeight];for (int y = 0; y < mapHeight; y++){for (int x = 0; x < mapWidth; x++){var grid = map.GetGrid(x, y);var gridCtrl = GameObject.Instantiate<GameObject>(GridPrefab, transform).GetComponent<GridCtrl>();gridCtrl.SetGrid(grid, map);gridCtrlMap[x, y] = gridCtrl;}}InitMap();}void ResetMap(){for (int y = 0; y < mapHeight; y++){for (int x = 0; x < mapWidth; x++){var grid = map.GetGrid(x, y);grid.Reset();}}InitMap();}void InitMap(){if (isRandomMap){int obsCount = 0;int rx, ry;while (obsCount < randomObstacleCount){rx = Random.Range(0, mapWidth);ry = Random.Range(0, mapHeight);if (!map.GetGrid(rx, ry).IsObstacle){map.SetObstacle(rx, ry, true);obsCount++;}}AStarGrid sGrid, tGrid;while (true){rx = Random.Range(0, mapWidth);ry = Random.Range(0, mapHeight);if (!map.GetGrid(rx, ry).IsObstacle){sGrid = map.GetGrid(rx, ry);break;}}while (true){rx = Random.Range(0, mapWidth);ry = Random.Range(0, mapHeight);tGrid = map.GetGrid(rx, ry);if (!tGrid.IsObstacle && tGrid != sGrid){break;}}map.SetFindPath(sGrid.X, sGrid.Y, tGrid.X, tGrid.Y);Debug.Log(sGrid.X + " " + sGrid.Y + "    " + tGrid.X + " " + tGrid.Y);}else{for (int i = 0; i < obstacles.Length; i++){map.SetObstacle((int)obstacles[i].x, (int)obstacles[i].y, true);}map.SetFindPath((int)startGrid.x, (int)startGrid.y, (int)targetGrid.x, (int)targetGrid.y);}UpdateShow();}void DoPathFindStep(){bool isComplete = map.DoFindPathStep();UpdateShow();if (isComplete){var path = map.GetPathResult();if (path.Count > 0){foreach (var grid in path){gridCtrlMap[grid.X, grid.Y].SetBgColor(Color.green);}}}}void UpdateShow(){for (int y = 0; y < mapHeight; y++){for (int x = 0; x < mapWidth; x++){gridCtrlMap[x, y].UpdateShow();}}}
}

简单AStar寻路2D示例相关推荐

  1. 光速AStar寻路算法(C++)

    光速AStar寻路算法(C++) 一.最终效果 可以看到,我创建了500个小动物,也有110的FPS.虽然它们并不是每一帧都在计算寻路,但是平均下来不卡顿也不错了.我是i7 6700 + GT 720 ...

  2. 【Astar寻路算法图解】Java实现

    Astar 寻路算法 1. 什么是Astar寻路算法 拥有一个地图,地图上面有起点和终点 一个机器人在起点,希望用最短的距离到达终点 Astar算法可以用来解决这个问题 2. 算法引入的三个工具 2. ...

  3. AStar寻路算法的Python实现

    AStar寻路算法的Python实现 人工智能课老师让整的,简单的python,加上matplotlib生成了一个散点图,矩阵点生成有10以内的误差,红色点是障碍物,百分之十的几率变成障碍物,绿色的点 ...

  4. 通过RobotFramework简单的接口请求示例

    为什么80%的码农都做不了架构师?>>>    一个简单的接口请求示例,接口测试将在此基础上进行! 库文件的导入: 需要导入的库 Collections 和 RequestsLibr ...

  5. Playmaker全面实践教程之简单的使用Playmaker示例

    Playmaker全面实践教程之简单的使用Playmaker示例 简单的使用Playmaker示例 通过本章前面部分的学习,相信读者已经对Playmaker有了一个整体的认识和印象了.在本章的最后,我 ...

  6. python画折线图代码-python绘制简单折线图代码示例

    1.画最简单的直线图 代码如下: import numpy as np import matplotlib.pyplot as plt x=[0,1] y=[0,1] plt.figure() plt ...

  7. python画折线图详解-python绘制简单折线图代码示例

    1.画最简单的直线图 代码如下: import numpy as np import matplotlib.pyplot as plt x=[0,1] y=[0,1] plt.figure() plt ...

  8. WPF MVVM 架构 Step By Step(2)(简单的三层架构示例及粘合代码GLUE code)

    WPF MVVM 架构 Step By Step(2)(简单的三层架构示例及粘合代码GLUE code) 原文:WPF MVVM 架构 Step By Step(2)(简单的三层架构示例及粘合代码GL ...

  9. DUMP文件分析6:简单的堆破坏示例

    本节,我们来看一个简单的堆破坏示例,程序依旧来自前面的示例,Crash Me!按钮的消息函数如下: void Cdump3Dlg::OnBnClickedButton1() {int* a = new ...

最新文章

  1. 1108轮播图和定时器this问题
  2. MVC4建立DBContext的EF6数据
  3. android应用开发(16)---AndroidManifest.xml
  4. 人生苦短我用python梗_为什么说人生苦短,我用Python?
  5. nginx 静态文件
  6. 江苏大学考研计算机录取率,报考数据分析—江苏大学
  7. 【转】对 Rust 语言的分析
  8. nginx日志的监控【转】
  9. 缩短与时间服务器同步时间,局域网内时间服务器架设与客户端设置
  10. java servlet,action,struts,struts2输出流中文编码问题
  11. usg6000v报错45_华为USG6000V防火墙视图化配置
  12. Android进阶知识(五):IPC基本概念之Binder、Binder工作机制及其原理
  13. 科普:什么是CPU?CPU和芯片关系?CPU怎么做的?CPU有什么用?不同CPU有什么区别?我们怎么选CPU?(待补充完整)
  14. 人民币与美元汇率兑换程序
  15. Liunx操作指令大全(基础知识到应用(易上手),全网最全)
  16. Dell Inspiron 5576 显卡驱动程序无法安装或不认识显卡
  17. dxdiag是什么 dxdiag命令怎么用
  18. 服务器每天自动变密码,Windows自动修改系统密码分享
  19. 国内新锐买手品牌BSiEE 本涩启动第三届品牌代言人招募活动
  20. Elang之ETS系列函数功能与用法详解

热门文章

  1. cupsd进程_Linux进程及作业管理
  2. 中基鸿业在投资互联网理财时需要注意哪些
  3. 中学信息技术教材c语言,中学信息技术校本教材.doc
  4. Photoshop分级快捷键
  5. java编程加载窗口,制作动画(屏保泡泡)
  6. Threejs渐变光柱效果
  7. 关于安装Adobe Illustrator(AI) CC 2017中遇到的问题总结
  8. 汽车充电桩的功率如何选择对保养电池更好?
  9. Google IOT Core之 APP获取设备数据(二)
  10. Python图片读写方式之OpenCV 色彩模式转换