思路
1.起始节点存入open列表
2. 循环(如果open列表不为空并且目标节点不在其中)
open列表中找出离目标最近的节点做为当前节点
把当前节点放入close列表,
把当前节点从open列表删除,
找出当前节点周围离目标最近的节点,设为当前节点继续循环
(查看周围节点时,如果在close列表中就不用再查了,如果父级节点为空就设为当前节点,返回值可根据父级节点逐级获得路径)
结束循环
3. 循环结束后有两种情况,
open列表为空,此时能到达的节点都查看了,但没有目标,所以是无法到达
目标节点在open列表中,此时己查到目标节点,返回目标节点
4.目标节点逐级查找父级节点使用路径
算法类
public class Path
{
public Path(int h, int w)
{
H = h;
W = w;
allPosi = new Node[w, h];
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
allPosi[i, j]= new Node(i, j);
}
}
}
public Node[,] allPosi;
/// <summary>
/// 最大高度
/// </summary>
public int H { get; set; }
/// <summary>
/// 最大宽度
/// </summary>
public int W { get; set; }
/// <summary>
/// 获取路径
/// </summary>
/// <param name="startx"></param>
/// <param name="starty"></param>
/// <param name="endx"></param>
/// <param name="endy"></param>
/// <returns></returns>
public Node GetPaths(int startx,int starty,int endx,int endy)
{
if(startx>=0 && starty>=0 &&startx<W && starty < H
&&endx>=0&&endx<W&&endy>=0&&endy<H)
{
return GetPaths(allPosi[startx, starty], allPosi[endx, endy]);
}
return null;
}
/// <summary>
/// 获取路径
///
/// </summary>
/// <param name="start">开始位置</param>
/// <param name="end">结束位置</param>
/// <returns>链式结构 end.before->before.before->.....->start</returns>
public Node GetPaths(Node start, Node end)
{
//如果当前节点或目标节点不可到达,返回空
if (!start.CanMove || !end.CanMove)
{
return null;
}
//清空之前寻路记录
foreach (var item in allPosi)
{
item.before = null;
}
Node current= null;
var openList = new List<Node>() { start};
var closeList= new List<Node>();
//如果有可查看节点或目标节点未获得
while(openList.Count>0&& !openList.Contains(end))
{
//当前节点是可查看节点中离目标最近的
current = openList.MinBy(n => Math.Abs(end.X - n.X) + Math.Abs(end.Y - n.Y));
//查看过的节点存起来不用再查
closeList.Add(current);
openList.Remove(current);
//获取当前节点周围,离目标最近的,可用节点
var _node = GetNearPosi(current, end,openList, closeList);
//最近的节点设为当前节点,循环查看
current=_node;
}
return current;
}
/// <summary>
/// 当前格子周围最近的位置
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
Node GetNearPosi(Node start, Node end,List<Node> openList,List<Node> closeList)
{
//设置周围可查看范围,不要超出界限
int minx = start.X - 1;
int maxx= start.X + 1;
int miny= start.Y - 1;
int maxy=start.Y + 1;
if(minx<0)minx= 0;
if (maxx >= W) maxx = W - 1;
if(miny<0)miny= 0;
if(maxy >= H) maxy = H - 1;
//临时变量初始化
Node current = null;
int currentDis = 0;
for (int x = minx; x <= maxx; x++)
{
for (int y = miny; y <=maxy; y++)
{
//获取临时节点,如果不可到达或己查看过,进入下一个循环
var posi = allPosi[x, y];
if (!posi.CanMove||closeList.Contains(posi))
{
continue;
}
//存入可查看列表
openList.Add(posi);
//如果临时节点的上一个节点为空,设置成当前节点,方便查找路径
if (posi.before == null) posi.before = start;
//如果查到了目录节点,反回
if (current == end)
return end;
//临时节点与目标节点的距离,找出距离最近的临时节点
var dis=Math.Abs(end.X-x)+Math.Abs(end.Y-y);
if (currentDis == 0|| currentDis > dis)
{
currentDis = dis;
current = posi;
}
}
}
return current;
}
/// <summary>
/// 设置节点为无法通行
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="can"></param>
public void SetObs(int x, int y,bool can=false)
{
if(x>=0&&x<W&&y>=0&&y<H)
allPosi[x, y].CanMove = can;
}
/// <summary>
/// 获取节点
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public Node GetNode(int x,int y)
{
if (x >= 0 && x < W && y >= 0 && y < H)
return allPosi[x, y];
return null;
}
}
节点类
public class Node
{
public Node before = null;
public int X { get; set; }
public int Y { get; set; }
public bool CanMove { get; set; } = true;
public Node(int x,int y)
{
X = x;
Y = y;
}
}
使用
//初始化地块大小
var _path = new MyPath.Path(H,W);
//设置障碍物
_path.SetObs(0, 3);
_path.SetObs(1, 3);
_path.SetObs(1, 4);
//获取节点
var _start = _path.GetNode(start.X, start.Y);
var _end = _path.GetNode(obj.X, obj.Y);
//获取路径 链式结构 end->node->node->....->start
var node= _path.GetPaths(_start,_end);
//循环依次得到路径上的所有节点
while(node!=null)
{
node = node.before;
//移动....
}