一、程序演示

二、 思路解析

我们先来了解一下A*寻路的思路:

1.每个块需要一个数据类(PointTest.cs),这个类中要存的数据方法有:

1.每个移动方块都有三个值FGH,其中G (代表起点到当前点的距离)、H(当前点到终点的距离)F=G+H。

2.并且每个移动方块都会存储自己上一个块(Parent)

2.首先 创建一个开放列表(OpenList:存放已经找到并计算好FGH值的周围块)和一个关闭列表(closeList:不可移动的块,如起点、障碍物和已经走过的块)

3.将起点放入开放列表(openList)中,只要开放列表(openList)中存在可移动的点就执行While循环while(openList.Count>0)。其中while循环中要执行的方法如下:

3.1 我们先在开放列表(openList)中找到最小F值的块为当前块。

3.2将找到的最小F值的块放入到关闭列表(closeList)中。

3.3 找寻当前块周围的八个可移动块放入周围块列表(surroundPointsList)中

3.4 剔除周围块列表(surroundPointsList)中存在的 如:起点,障碍物,走过的块等不可移动的块,这样周围块列表(surroundPointsList)中就只剩下可以走的块了

3.5然后我们遍历这些可移动的周围块,每个周围块执行两个判断:

(1)当这个周围块不存在开放列表中时,计算这个块的FGH值,并且将父节点赋值成当前节点

(2)当这个周围块存在开放列表中时,我们计算新的G值(计算方式:当前点的G值+当前点到这个周围块的节点所消耗的G值)是否小于周围块的原G值,如果小于就要更新这个周围块的GF值(为什么不更新H值呢?因为H值是块到终点的距离是不会变的。所以不用修改),并且修改这个周围块的父节点为当前节点。

3.6 最后判断一下开放列表(openList)中是否存在终点这个块,如果存在就跳出While循环

4.就这样我们根据终点块的父节点,一级一级的往后倒推就找到了我们想要的最短路径。

三、案例演示:

看案例图之前先了解一下每个颜色块的作用吧

(1)浅蓝色代表在开放列表中的块(存放已经找到并计算好FGH值的周围块)

右上角为F,左下角为G,右下角为H

(2)橙色代表找到的最小F值块

(3)箭头的指向表示块的上一级父节点

(4) 蓝色表示关闭列表(closeList)中的点(其中绿色起点和红色终点也都在关闭列表中,为了区别就没有标成蓝色)

好的接下来我们就开始根据上面的思路对照来看图了解:

1.开放列表中只有一个起点,将起点放入到关闭列表中并获取起点周围的8个周围块(上下左右,左上 左下 右上 右下),由于一开始这8个周围块都不在关闭列表)中,所以计算他们的FGH值并对父节点赋值,然后放入到开放列表中。如下图

2.再次执行循环,先找到开放列表中的最小F值块(当前橙色块),找寻当前块周围的8个点,剔除不可移动的周围块,遍历剩下的可移动周围块点进行判断,根据上图我们可以找到橙色块,根据橙色当前块来找周围可移动的块共有四个,并且这四个块都在开放列表中了(都有自己的FGH值和父节点了),所以我们要进行一个计算,从当前块(橙色)到这个周围块所花费的G值加橙色块的原G值是否小于周围块原来G值,小于就代表从当前块(橙色)这条路比你上一次走的路要近,就要重新计算这个周围块的FG值,并且修改这个周围块的父节点。这里呢这四个周围块原路径都比走当前块(橙色)近,所以不做修改,执行下一次循环

3.上一步橙色点不是已经走过了嘛,所以这里我就把走过的橙色块改成蓝色代表已经走过的块不能再走了,这一步还是和上一次一样从开放列表中找到最小F值点,这时候你会发现有两个最小值F为4.576492的块这时候怎么选呢?其实我们一开搜寻周围块的时候是有顺序的(上下左右,左上 左下 右上 右下),我们按照这个顺序将块存入到列表,所以当遇到多个最小F值的块相谁最先在列表中就选择那个块为当前块,所以这里程序选的是以起点为中心的右上角的块为当前块(橙色块)。然后就是遍历周围块(注意这里我设置了周围有墙不可以贴墙斜着走方法),没有在开放列表中的就计算FGH值,设置父节点,放到开放列表中即可,在开放列表中时,计算新路径G值与原路径G值谁大谁小,小就修改FG值,并替换父节点,否则就啥也不用动。

4.好的接下来继续找最小点,对周围点进行逻辑判断

5.继续

6.继续

7.继续

8.继续

9.继续

10. 下一步

11.下一步

12.下一步

13.下一步

14.下一步

15.下一步

16.下一步

17.下一步

18.最后一步,可以看到周围块包含了结束点,这证明我们已经找到了最优路线

19.最后找到的路线

程序运行效果:

四、代码实现

1.地图块脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;/// <summary>
/// 地图块
/// </summary>
public class PointTest : MonoBehaviour
{public Text ftxt;public Text gtxt;public Text htxt;//当A*计算出最近路径点时,用于记录上一节点public PointTest Parent { get; set; }public float F { get; set; }//总消耗 F=G+Hpublic float G { get; set; }//表示从起点移动到网格上指定方格的移动耗费 (可沿斜方向移动)public float H { get; set; }//当前点到终点的消耗//位置下标(具体位置要根据图片宽度进行计算)public Vector2 PosIndex { get; set; }//是否是障碍物public bool IsWall { get; set; }RectTransform m_rect;//记录自身Rect组件public void InitData(Vector2 origin, Vector2 space, Vector2 posIndex){Parent = null;       //创建地图 上一路径默认为空PosIndex = posIndex; //位置下标//当前rect为空 赋值if (m_rect == null){m_rect = GetComponent<RectTransform>();ftxt = m_rect.Find("F").GetComponent<Text>();gtxt = m_rect.Find("G").GetComponent<Text>();htxt = m_rect.Find("H").GetComponent<Text>();}//根据原点 间距 图片宽高计算位置m_rect.anchoredPosition = (posIndex * new Vector2(m_rect.sizeDelta.x, m_rect.sizeDelta.y)) + origin + PosIndex * space;}//修改图片颜色public void SetColor(Color color){GetComponent<Image>().color = color;}//更新父节点、g值计算总消耗F值public void UpdateParent(PointTest parent, float g){Parent = parent;G = g;F = G + H;gtxt.text = G.ToString();ftxt.text = F.ToString();}}

2.A*寻路脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class AStarTest : MonoBehaviour
{[Header("颜色")]public Color startColor = Color.green;  //起始点颜色public Color endColor = Color.red;      //结束点颜色public Color wallColor = Color.blue;    //障碍物颜色public Color pathColor = Color.gray;    //路径颜色public Color mapColor = Color.white;    //地图块颜色[Header("原点位置/间距")]public Vector2 origin = Vector2.zero;   //原点位置public Vector2 space = Vector2.zero;    //间距[Header("起点/终点")]public Vector2 start = Vector2.zero;   //起点public Vector2 end = Vector2.zero;     //终点[Header("障碍物")]public List<Vector2> wallList;         //障碍物列表[Header("地图宽高")]public Vector2 mapWidthAndHeight;      //地图宽高private PointTest[,] map;              //地图块容器//预设 容器public PointTest prefab;public RectTransform content;PointTest startPoint; //起点PointTest endPoint;   //终点List<PointTest> openList = new List<PointTest>(); //闭合地图块列表 不可移动 如:障碍物 起点/终点、已走过的路径List<PointTest> closeList = new List<PointTest>(); //闭合地图块列表 不可移动 如:障碍物 起点/终点、已走过的路径void Start(){//初始化地图块InitMap();//查找最近路径FindPath(startPoint, endPoint);//显示最优路线ShowPath(startPoint, endPoint);}#region 初始化地图 设置障碍物 起点终点PointTest obj;int pointName = 0;  //地图块名称private void InitMap(){map = new PointTest[(int)mapWidthAndHeight.x, (int)mapWidthAndHeight.y];for (int x = 0; x < (int)mapWidthAndHeight.x; x++){for (int y = 0; y < (int)mapWidthAndHeight.y; y++){//创建地图块并显示obj = Instantiate(prefab, content).GetComponent<PointTest>();obj.gameObject.SetActive(true);//数据初始化obj.InitData(origin, space, new Vector2(x, y));//将地图块添加到地图列表map[x, y] = obj;pointName++;obj.name = pointName.ToString();}}//起点 终点初始化 并将其添加到闭合列表中startPoint = map[(int)start.x, (int)start.y];endPoint = map[(int)end.x, (int)end.y];//障碍物创建并将其添加到闭合列表中for (int i = 0; i < wallList.Count; i++){PointTest point = map[(int)wallList[i].x, (int)wallList[i].y];point.IsWall = true;point.SetColor(wallColor);//添加到闭合列表中closeList.Add(point);}}#endregion#region 查找路径private void FindPath(PointTest start, PointTest end){openList.Add(start); //将开始路径添加到开放路径中//循环开启列表中的块while (openList.Count > 0){PointTest point = FindMinFOfPoint(openList);//找到总消耗值F最小的路径块,作为当前块//Debug.Log(point.name);//清除当前块 放入闭合列表中openList.Remove(point);closeList.Add(point);List<PointTest> surroundPointsList = GetSurroundPoints(point);//找寻围绕当前块四周可移动的地图块PointsFilter(surroundPointsList, closeList);//从四周可移动块中过滤已经走过的路径块//遍历四周点可移动点foreach (PointTest surroundPoint in surroundPointsList){//判断当前点在开放列表中时,要重新计算G值if (openList.IndexOf(surroundPoint) > -1){//重新计算G值float nowG = CalculateG(surroundPoint, point);//计算两点的G值 两点距离//如果当前G点距离小于原来G值if (nowG < surroundPoint.G){//更新父节点 G和F值surroundPoint.UpdateParent(point, nowG);}}else{//当前块没有在开放列表中时,初始化父块,FGH值 并将此块放入开放列表中surroundPoint.Parent = point;CalculateF(surroundPoint, end);openList.Add(surroundPoint);}}//判断一下是否到达了目标点if (openList.IndexOf(end) > -1){break;}}}#endregion#region 1.查找开启列表中的最小F值块private PointTest FindMinFOfPoint(List<PointTest> openList){float f = float.MaxValue;PointTest temp = null;//遍历开放路径,判断f值foreach (PointTest p in openList){if (p.F < f){temp = p;f = p.F;}}return temp;}#endregion#region 2.获取环绕可移动路径点private List<PointTest> GetSurroundPoints(PointTest point){PointTest up = null, down = null, left = null, right = null;//上下左右块PointTest lu = null, ld = null, ru = null, rd = null;//左上左下右上右下块//取得上下左右的Point (因为使用二维数组,所以高度范围在0~mapWidthAndHeight.y-1,同理宽度为0~mapWidthAndHeight.x-1)if (point.PosIndex.y < mapWidthAndHeight.y - 1){up = map[(int)point.PosIndex.x, (int)point.PosIndex.y + 1];}if (point.PosIndex.y > 0){down = map[(int)point.PosIndex.x, (int)point.PosIndex.y - 1];}if (point.PosIndex.x > 0){left = map[(int)point.PosIndex.x - 1, (int)point.PosIndex.y];}if (point.PosIndex.x < mapWidthAndHeight.x - 1){right = map[(int)point.PosIndex.x + 1, (int)point.PosIndex.y];}/*取得左上、左下、右上、右下的Point* 左上:左边和上边不为空 就有左上角,其他同理*/if (left != null && up != null){//以当前点为中间点,左上角在它(x-1,y+1)位置上lu = map[(int)point.PosIndex.x - 1, (int)point.PosIndex.y + 1];}if (left != null && down != null){//以当前点为中间点,左下角在它(x-1,y-1)位置上ld = map[(int)point.PosIndex.x - 1, (int)point.PosIndex.y - 1];}if (right != null && up != null){//以当前点为中间点,右上角在它(x+1,y+1)位置上ru = map[(int)point.PosIndex.x + 1, (int)point.PosIndex.y + 1];}if (right != null && down != null){//以当前点为中间点,右下角在它(x+1,y-1)位置上rd = map[(int)point.PosIndex.x + 1, (int)point.PosIndex.y - 1];}//筛选可以移动的块(筛选障碍物、起点、终点)List<PointTest> pointList = new List<PointTest>();//周围的Point(上下左右)如果不是墙就可以走if (up != null && up.IsWall == false){pointList.Add(up);}if (down != null && down.IsWall == false){pointList.Add(down);}if (left != null && left.IsWall == false){pointList.Add(left);}if (right != null && right.IsWall == false){pointList.Add(right);}//两边也不是墙才可以走(斜对角)if (lu != null && lu.IsWall == false && left.IsWall == false && up.IsWall == false){pointList.Add(lu);}if (ld != null && ld.IsWall == false && left.IsWall == false && down.IsWall == false){pointList.Add(ld);}if (ru != null && ru.IsWall == false && right.IsWall == false && up.IsWall == false){pointList.Add(ru);}if (rd != null && rd.IsWall == false && right.IsWall == false && down.IsWall == false){pointList.Add(rd);}return pointList;}#endregion#region 3.从四周可移动块中过滤已经走过的路径块/// <summary>/// 从四周可移动块中过滤已经走过的路径块/// </summary>/// <param name="src">四周可移动的地图块</param>/// <param name="closePoint">闭合路径块</param>private void PointsFilter(List<PointTest> src, List<PointTest> closePoint){//遍历闭合列表foreach (PointTest p in closePoint){/*判断是否找到* IndexOf(T):表示从列表0位置匹配到最后,直到找到第一个重复值并返回他的下标 没找到会返回-1* IndexOf(T, Int32):从第n个下标开始到最后* IndexOf(T, Int32,Int32):从第n个下标开始到m个下标结束的范围查找,如果只想查询一个可以是IndexOf(T, 1,1)从1~1的范围即只查找下标为1的数*/if (src.IndexOf(p) > -1){src.Remove(p);}}}#endregion#region 4.计算G的值 起点到当前点的消耗private float CalculateG(PointTest now, PointTest parent){return (Vector2.Distance(new Vector2(now.PosIndex.x, now.PosIndex.y), new Vector2(parent.PosIndex.x, parent.PosIndex.y)) + parent.G);}#endregion#region 5.计算F的值/// <summary>/// 计算F的值/// </summary>/// <param name="now">当前位置</param>/// <param name="end">目标位置</param>private void CalculateF(PointTest now, PointTest end){//当前位置到 终点位置//float h = Mathf.Abs(end.PosIndex.x - now.PosIndex.x) + Mathf.Abs(end.PosIndex.y - now.PosIndex.y);//绝对值float h = (Vector2.Distance(new Vector2(now.PosIndex.x, now.PosIndex.y), new Vector2(end.PosIndex.x, end.PosIndex.y)));//当前位置到上一点位置float g = Vector2.Distance(new Vector2(now.PosIndex.x, now.PosIndex.y), new Vector2(now.Parent.PosIndex.x, now.Parent.PosIndex.y)) + now.Parent.G;float f = g + h;now.F = f;now.G = g;now.H = h;now.ftxt.text = f.ToString();now.gtxt.text = g.ToString();now.htxt.text = h.ToString();}#endregion#region 显示路径坐标private void ShowPath(PointTest start, PointTest end){PointTest temp = end;while (true){Color color = pathColor;if (temp == start){color = startColor;}if (temp == end){color = endColor;}temp.SetColor(color);if (temp.Parent == null)break;temp = temp.Parent;}}#endregion}

unity之A*寻路相关推荐

  1. Unity 实现A* 寻路算法

    前言 A* 寻路算法是什么 游戏开发中往往有这样的需求,让玩家控制的角色自动寻路到目标地点,或是让 AI 角色移动到目标位置,实际的情况可能很复杂,比如地图上有无法通过的障碍或者需要付出代价(时间或其 ...

  2. unity NavMesh网格寻路

    前言 NavMeshs是Unity自带的一个寻路系统,即一个点到另一个点寻找最短有效路径 如何使用NavMesh? 先直接使用便于理解,然后再介绍参数属性 直接先给模型添加NavMeshAgent组件 ...

  3. 【Unity】游戏寻路系统—NavMesh入门(个人翻译)

    以下内容笔者个人翻译自:http://www.theknightsofunity.com/ 游戏中的许多角色经常需要绕过关卡种中的障碍物.正是因为这是一个游戏中非常常见的情形,Unity提供了内置的寻 ...

  4. 【Unity】 坦克寻路

    这是一篇残缺不全的记录-- Unity寻路所有的资料大概都是NavMesh吧.本来这一块跟我没啥关系,后来队友告诉我:NavMesh没法模拟坦克的转向. 我也不知道是怎样勇气打算试一下机器学习?反正最 ...

  5. Unity实现BStar寻路

    BStarFindPath 欢迎了解BStar寻路算法 想象空间 BStar寻路的优势 BStar寻路的劣势 路径优化的方案 空间优化的方案 欢迎了解BStar寻路算法 BStar算法归于人工智能或人 ...

  6. unity学习:寻路算法(AStar算法)与简单AI(势能场估价算法)

    项目地址:https://github.com/kotomineshiki/AIFindPath 视频地址:多重寻路 综合寻路--包括攻击考量的寻路算法 GamePlay 这是一个<文明> ...

  7. Unity A星寻路

    使用A星寻路要把地图网格化,这是为了简化地图,图中阴影是障碍物 一些关键概念 开放列表(open list):记下所有被考虑用来寻找最短路径的格子 封闭列表(close list):记下已经搜索过的格 ...

  8. unity Nav 分层寻路

    Navgation分层寻路 分层寻路应用场景 比如在王者荣耀中小兵按照上中下3条路运动到目标点,所以上路小兵不能往中路跑.就需要小兵按照指定的路径行动. 如何实现 我么在游戏中可以将Navigatio ...

  9. unity 敌人自动攻击和寻路_【A*Pathfinding】超级简单的Unity2D寻路

    哈喽~我是yumir. 写过俯视角射击和"元气骑士"地图生成之后,不做个敌人AI总觉得差点什么,所以又研究了一下Unity的2D寻路. 这次我用的是A*Pathfinding的免费 ...

最新文章

  1. 京东电商搜索中的语义检索与商品排序
  2. R语言使用xgboost构建回归模型:vtreat包为xgboost回归模型进行数据预处理(缺失值填充、缺失值标识、离散变量独热onehot编码)、构建出生体重的xgboost模型回归模型
  3. VMware CTO:未来VMware NSX与思科ACI将有更多整合
  4. linux shell install 命令简介
  5. vb一个使用URLDownloadToFile实现文件下载的类
  6. RIA and volta
  7. 如何在Mac上更改“终端”窗口光标?
  8. 定义标准的POJO类
  9. Python list列表groupby分组用法
  10. 游戏后端自增id选型
  11. 博客专家3月原创奖励
  12. win7与internet时间同步出错_win7系统时间不同步怎么办|win7系统时间同步出错的解决方法...
  13. Mit6.S081-xv6参考书翻译
  14. HCIE 面试资料-BFD/NSF/NSR/NTP
  15. matlab中的sym
  16. libreoffice安装+libreoffice转换pdf (linux)
  17. 记录第一个360浏览器翻译插件
  18. TDH 集群的许可证管理机制及TDH集群的卸载与安装
  19. 你好啊,Spring !!!
  20. 更新:MapReduce编程之自定义序列化类及自定义排序2

热门文章

  1. chrome字体发虚神马的
  2. 杂篇:理一理屏幕尺寸那些事
  3. 秦九韶算法如何应用到计算机,《秦九韶算法》说课稿——获奖说课稿
  4. 【数论】 秦九韶公式
  5. 你的数字货币安全吗?该怎么注意呢?
  6. 知识星球:ChatGPTAI 变现圈,正式上线!
  7. C语言与或非位运算 Java判断中 if条件
  8. android加载长图片的方法
  9. Go 2.0 该如何满足开发者的期待?
  10. 信息系统项目管理研究——项目管理流程