1.简易地图

如图所示简易地图,其中绿色方块的是起点,中间蓝色的障碍物,红色方块表示目的地,我们用一个二位数组来表示地图。
2.寻路步骤
1. 从起点 A 开始, 把它作为待处理的方格存入一个"开启列表", 开启列表就是一个等待检查方格 的列表.
2. 寻找起点 A 周围可以到达的方格, 将它们放入"开启列表", 并设置它们的"父方格"为 A.
3. 从"开启列表"中删除起点 A, 并将起点 A 加入"关闭列表", "关闭列表"中存放的都是不需要再次检查的方格
从 "开启列表" 中找出相对最靠谱的方块, 什么是最靠谱? 它们通过公式 F=G+H 来计算.
F = G + H
G 表示从起点 A 移动到网格上指定方格的移动耗费 (可沿斜方向移动).
H 表示从指定的方格移动到终点 B 的预计耗费 (H 有很多计算方法, 这里我们设定只可以 上下左右移动).
从 "开启列表" 中选择 F 值最低的方格 C (绿色起始方块 A 右边的方块), 然后对它进行如下处 理:
4. 把它从 "开启列表" 中删除, 并放到 "关闭列表" 中.
5. 检查它所有相邻并且可以到达 (障碍物和 "关闭列表" 的方格都不考虑) 的方格. 如果这些方格 还不在 "开启列表" 里的话, 将它们加入 "开启列表", 计算这些方格的 G, H 和 F 值各是多少, 并设置 它们的 "父方格" 为 C.
6. 如果某个相邻方格 D 已经在 "开启列表" 里了, 检查如果用新的路径 (就是经过 C 的路径) 到 达它的话, G 值是否会更低一些, 如果新的 G 值更低, 那就把它的 "父方格" 改为目前选中的方格 C, 然 后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择,
因为它需要更远的路, 这时我们什么也不做.
3.如何找回路径
除了起始方块, 每一个曾经或者现在还在 "开启列表" 里的方块, 它都有一个 "父方 块", 通过 "父方块" 可以索引到最初的 "起始方块", 这就是路径.
具体我们用代码解释
4.代码分析
首先我们需要为地图的格子创建一个Model包含地图每一格的信息,
-
using System.Collections;
-
using System.Collections.Generic;
-
using UnityEngine;
-
-
public class Point {
-
public Point Parent { get; set; }
-
public float H;
-
public int X;
-
public int Y;
-
public float G;
-
public float F;
-
public bool IsWall;
-
public Point(int x,int y,Point parent=null)
-
{
-
this.X = x;
-
this.Y = y;
-
this.Parent = parent;
-
}
-
public void UpdateParent(Point parent,float g)
-
{
-
this.Parent = parent;
-
this.G = g;
-
this.F = G+H;
-
}
-
}
然后重点就是寻路算法的代码
-
/// <summary>
-
/// 查找最优路径
-
/// </summary>
-
/// <param name="start"></param>
-
/// <param name="end"></param>
-
private void FindPath(Point start, Point end)
-
{
-
List<Point> openList = new List<Point>();
-
List<Point> closeList = new List<Point>();
-
openList.Add(start); //将开始位置添加进Open列表
-
while (openList.Count > 0)//查找退出条件
-
{
-
Point point = FindMinFOfPoint(openList);//查找Open列表中最小的f值
-
//print(point.F+";"+point.X+","+point.Y);
-
openList.Remove(point); closeList.Add(point);//不再考虑当前节点
-
-
List<Point> surroundPoints = GetSurroundPoints(point);//得到当前节点的四周8个节点
-
PointsFilter(surroundPoints, closeList);//将周围节点中已经添加进Close列表中的节点移除
-
foreach (Point surroundPoint in surroundPoints)
-
{
-
if (openList.IndexOf(surroundPoint) > -1)//如果周围节点在open列表中
-
{
-
float nowG = CalcG(surroundPoint, surroundPoint.Parent);//计算经过的Open列表中最小f值到周围节点的G值
-
if (nowG < surroundPoint.G)
-
{
-
surroundPoint.UpdateParent(point, nowG);
-
}
-
}
-
else//周围节点不在Open列表中
-
{
-
surroundPoint.Parent = point;//设置周围列表的父节点
-
CalcF(surroundPoint, end);//计算周围节点的F,G,H值
-
openList.Add(surroundPoint);//最后将周围节点添加进Open列表
-
}
-
}
-
//判断一下退出条件
-
if (openList.IndexOf(end) > -1)
-
{
-
break;
-
}
-
}
-
-
}
主要就是通过获取比较F的值,每次获取到open列表中最小F的节点,并将它添加进close列表中。
比如第一次获取到最小值为0,那么就获取到0周围的所有节点,并将他们的父节点设置为0,如果他是可以通过的就将他添加进open列表中。并将0添加进close列表中,以后不再考虑0这个节点
7.4,6,5.4
6,0,4
7.4,
6,5.4
第二次获取到最小值为4,重复第一步,由于4的周围没有新节点。不做操作。此时open列表中还剩7.4,6,5.4这三个值,0,4已经不再考虑,并且0没有父节点,4的父节点为0
7.4 ,6,5.4
6,,
7.4 ,6,5.4
第三次获取到最小值为5.4,这时有2个值同为5.4,没关系,我们接着往下运行,先走右上方5.4,open列表中添加7.4和8
9.4,
8,7.4,7.4
7.4 ,6,,
6,,,
7.4 ,6,5.4
这样依次往下运行,一步步向终点靠近,最先到达终点的点,他的一层层父节点就是我们得到的最短路径。这就是A星算法,每次和节点的四周比较获取最优点。
然后附上完整代码
-
using System.Collections;
-
using System.Collections.Generic;
-
using UnityEngine;
-
-
public class AStar : MonoBehaviour
-
{
-
private const int mapWith = 15;
-
private const int mapHeight = 15;
-
-
private Point[,] map = new Point[mapWith, mapHeight];
-
-
// Use this for initialization
-
void Start()
-
{
-
InitMap(); //初始化地图
-
Point start = map[2, 2];
-
Point end = map[6, 2];
-
FindPath(start, end);
-
ShowPath(start, end);
-
}
-
-
-
private void ShowPath(Point start, Point end)
-
{
-
int z = -1;
-
Point temp = end;
-
while (true)
-
{
-
//Debug.Log(temp.X + "," + temp.Y);
-
Color c = Color.gray;
-
if (temp == start)
-
{
-
c = Color.green;
-
}
-
else if (temp == end)
-
{
-
c = Color.red;
-
}
-
CreateCube(temp.X, temp.Y,z, c);
-
-
if (temp.Parent == null)
-
break;
-
temp = temp.Parent;
-
}
-
for (int x = 0; x < mapWith; x++)
-
{
-
for (int y = 0; y < mapHeight; y++)
-
{
-
if (map[x, y].IsWall)
-
{
-
CreateCube(x, y,z, Color.blue);
-
}
-
}
-
}
-
}
-
-
private void CreateCube(int x, int y,int z, Color color)
-
{
-
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
-
go.name = x+","+y;
-
go.transform.position = new Vector3(x, y, z);
-
go.GetComponent<Renderer>().material.color = color;
-
}
-
-
private void InitMap()
-
{
-
for (int x = 0; x < mapWith; x++)
-
{
-
for (int y = 0; y < mapHeight; y++)
-
{
-
map[x, y] = new Point(x, y);
-
CreateCube(x, y,0, Color.black);
-
}
-
}
-
map[4, 1].IsWall = true;
-
map[4, 2].IsWall = true;
-
map[4, 3].IsWall = true;
-
map[4, 4].IsWall = true;
-
map[4, 5].IsWall = true;
-
map[4, 6].IsWall = true;
-
}
-
/// <summary>
-
/// 查找最优路径
-
/// </summary>
-
/// <param name="start"></param>
-
/// <param name="end"></param>
-
private void FindPath(Point start, Point end)
-
{
-
List<Point> openList = new List<Point>();
-
List<Point> closeList = new List<Point>();
-
openList.Add(start); //将开始位置添加进Open列表
-
while (openList.Count > 0)//查找退出条件
-
{
-
Point point = FindMinFOfPoint(openList);//查找Open列表中最小的f值
-
print(point.F + ";" + point.X + "," + point.Y);
-
openList.Remove(point); closeList.Add(point);//不再考虑当前节点
-
-
List<Point> surroundPoints = GetSurroundPoints(point);//得到当前节点的四周8个节点
-
PointsFilter(surroundPoints, closeList);//将周围节点中已经添加进Close列表中的节点移除
-
foreach (Point surroundPoint in surroundPoints)
-
{
-
if (openList.IndexOf(surroundPoint) > -1)//如果周围节点在open列表中
-
{
-
float nowG = CalcG(surroundPoint, surroundPoint.Parent);//计算经过的Open列表中最小f值到周围节点的G值
-
if (nowG < surroundPoint.G)
-
{
-
print("123");
-
surroundPoint.UpdateParent(point, nowG);
-
}
-
}
-
else//周围节点不在Open列表中
-
{
-
surroundPoint.Parent = point;//设置周围列表的父节点
-
CalcF(surroundPoint, end);//计算周围节点的F,G,H值
-
openList.Add(surroundPoint);//最后将周围节点添加进Open列表
-
}
-
}
-
//判断一下
-
if (openList.IndexOf(end) > -1)
-
{
-
break;
-
}
-
}
-
-
}
-
-
private void PointsFilter(List<Point> src, List<Point> closeList)
-
{
-
foreach (Point p in closeList)
-
{
-
if (src.IndexOf(p) > -1)
-
{
-
src.Remove(p);
-
}
-
}
-
}
-
-
private List<Point> GetSurroundPoints(Point point)
-
{
-
Point up = null, down = null, left = null, right = null;
-
Point lu = null, ru = null, ld = null, rd = null;
-
if (point.Y < mapHeight - 1)
-
{
-
up = map[point.X, point.Y + 1];
-
}
-
if (point.Y > 0)
-
{
-
down = map[point.X, point.Y - 1];
-
}
-
if (point.X > 0)
-
{
-
left = map[point.X - 1, point.Y];
-
}
-
if (point.X < mapWith - 1)
-
{
-
right = map[point.X + 1, point.Y];
-
}
-
if (up != null && left != null)
-
{
-
lu = map[point.X - 1, point.Y + 1];
-
}
-
if (up != null && right != null)
-
{
-
ru = map[point.X + 1, point.Y + 1];
-
}
-
if (down != null && left != null)
-
{
-
ld = map[point.X - 1, point.Y - 1];
-
}
-
if (down != null && right != null)
-
{
-
rd = map[point.X + 1, point.Y - 1];
-
}
-
List<Point> list = new List<Point>();
-
if (down != null && down.IsWall == false)
-
{
-
list.Add(down);
-
}
-
if (up != null && up.IsWall == false)
-
{
-
list.Add(up);
-
}
-
if (left != null && left.IsWall == false)
-
{
-
list.Add(left);
-
}
-
if (right != null && right.IsWall == false)
-
{
-
list.Add(right);
-
}
-
if (lu != null && lu.IsWall == false && left.IsWall == false && up.IsWall == false)
-
{
-
list.Add(lu);
-
}
-
if (ld != null && ld.IsWall == false && left.IsWall == false && down.IsWall == false)
-
{
-
list.Add(ld);
-
}
-
if (ru != null && ru.IsWall == false && right.IsWall == false && up.IsWall == false)
-
{
-
list.Add(ru);
-
}
-
if (rd != null && rd.IsWall == false && right.IsWall == false && down.IsWall == false)
-
{
-
list.Add(rd);
-
}
-
return list;
-
}
-
-
private Point FindMinFOfPoint(List<Point> openList)
-
{
-
float f = float.MaxValue;
-
Point temp = null;
-
foreach (Point p in openList)
-
{
-
if (p.F < f)
-
{
-
temp = p;
-
f = p.F;
-
}
-
}
-
print("返回open列表中最小的f:"+temp.F);
-
return temp;
-
}
-
-
private float CalcG(Point now, Point parent)
-
{
-
return Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(parent.X, parent.Y)) + parent.G;
-
}
-
-
private void CalcF(Point now, Point end)
-
{
-
//F = G + H
-
float h = Mathf.Abs(end.X - now.X) + Mathf.Abs(end.Y - now.Y);
-
float g = 0;
-
if (now.Parent == null)
-
{
-
g = 0;
-
}
-
else
-
{
-
g = Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(now.Parent.X, now.Parent.Y)) + now.Parent.G;
-
}
-
float f = g + h;
-
now.F = f;
-
now.G = g;
-
now.H = h;
-
}
-
}
代码演示是在unity中实现的