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值最小的格子为终点或者“开启列表”中没有格子了,即为结束
几点注意点
- H(n)预计消耗的计算的准确度非常重要
由于G(n)是已经走过的路,所需的消耗是明确的,只有H(n)是预计的,预计的越准,我们就能越快的找到目标位置,减少计算量,也能更贴近最短路径 - 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; } //坐标X
public int Y {
get; set; } //坐标Y
public 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 (<

最低0.47元/天 解锁文章
3266

被折叠的 条评论
为什么被折叠?



