A星寻路算法流程详解

1.简易地图

A星寻路算法流程详解_第1张图片

如图所示简易地图,其中绿色方块的是起点,中间蓝色的障碍物,红色方块表示目的地,我们用一个二位数组来表示地图。

2.寻路步骤

A星寻路算法流程详解_第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.如何找回路径

A星寻路算法流程详解_第3张图片
除了起始方块, 每一个曾经或者现在还在 "开启列表" 里的方块, 它都有一个 "父方 块", 通过 "父方块" 可以索引到最初的 "起始方块", 这就是路径. 
具体我们用代码解释

4.代码分析

首先我们需要为地图的格子创建一个Model包含地图每一格的信息,
[csharp]  view plain  copy
 print ?
  1. using System.Collections;  
  2. using System.Collections.Generic;  
  3. using UnityEngine;  
  4.   
  5. public class Point {  
  6.     public Point Parent { getset; }  
  7.     public float H;  
  8.     public int X;  
  9.     public int Y;  
  10.     public float G;  
  11.     public float F;  
  12.     public bool IsWall;  
  13.     public Point(int x,int y,Point parent=null)  
  14.     {  
  15.         this.X = x;  
  16.         this.Y = y;  
  17.         this.Parent = parent;  
  18.     }  
  19.     public void UpdateParent(Point parent,float g)  
  20.     {  
  21.         this.Parent = parent;  
  22.         this.G = g;  
  23.         this.F = G+H;  
  24.     }  
  25. }  
然后重点就是寻路算法的代码
[csharp]  view plain  copy
 print ?
  1. ///   
  2. /// 查找最优路径  
  3. ///   
  4. ///   
  5. ///   
  6. private void FindPath(Point start, Point end)  
  7. {  
  8.     List openList = new List();  
  9.     List closeList = new List();  
  10.     openList.Add(start);    //将开始位置添加进Open列表  
  11.     while (openList.Count > 0)//查找退出条件  
  12.     {  
  13.         Point point = FindMinFOfPoint(openList);//查找Open列表中最小的f值  
  14.         //print(point.F+";"+point.X+","+point.Y);  
  15.         openList.Remove(point); closeList.Add(point);//不再考虑当前节点  
  16.   
  17.         List surroundPoints = GetSurroundPoints(point);//得到当前节点的四周8个节点  
  18.         PointsFilter(surroundPoints, closeList);//将周围节点中已经添加进Close列表中的节点移除  
  19.         foreach (Point surroundPoint in surroundPoints)  
  20.         {  
  21.             if (openList.IndexOf(surroundPoint) > -1)//如果周围节点在open列表中  
  22.             {  
  23.                 float nowG = CalcG(surroundPoint, surroundPoint.Parent);//计算经过的Open列表中最小f值到周围节点的G值  
  24.                 if (nowG < surroundPoint.G)  
  25.                 {  
  26.                     surroundPoint.UpdateParent(point, nowG);  
  27.                 }  
  28.             }  
  29.             else//周围节点不在Open列表中  
  30.             {  
  31.                 surroundPoint.Parent = point;//设置周围列表的父节点  
  32.                 CalcF(surroundPoint, end);//计算周围节点的F,G,H值  
  33.                 openList.Add(surroundPoint);//最后将周围节点添加进Open列表  
  34.             }  
  35.         }  
  36.         //判断一下退出条件  
  37.         if (openList.IndexOf(end) > -1)  
  38.         {  
  39.             break;  
  40.         }  
  41.     }  
  42.   
  43. }  
主要就是通过获取比较F的值,每次获取到open列表中最小F的节点,并将它添加进close列表中。

比如第一次获取到最小值为0,那么就获取到0周围的所有节点,并将他们的父节点设置为0,如果他是可以通过的就将他添加进open列表中。并将0添加进close列表中,以后不再考虑0这个节点
7.46,5.4
6,0,4
7.4, 6,5.4
第二次获取到最小值为4,重复第一步,由于4的周围没有新节点。不做操作。此时open列表中还剩7.4,6,5.4这三个值,0,4已经不再考虑,并且0没有父节点,4的父节点为0
7.46,5.4
6,,
7.46,5.4
第三次获取到最小值为5.4,这时有2个值同为5.4,没关系,我们接着往下运行,先走右上方5.4,open列表中添加7.4和8
9.4, 8,7.4,7.4
7.46,,
6,,,
7.46,5.4
这样依次往下运行,一步步向终点靠近,最先到达终点的点,他的一层层父节点就是我们得到的最短路径。这就是A星算法,每次和节点的四周比较获取最优点。
然后附上完整代码
[csharp]  view plain  copy
 print ?
  1. using System.Collections;  
  2. using System.Collections.Generic;  
  3. using UnityEngine;  
  4.   
  5. public class AStar : MonoBehaviour  
  6. {  
  7.     private const int mapWith = 15;  
  8.     private const int mapHeight = 15;  
  9.   
  10.     private Point[,] map = new Point[mapWith, mapHeight];  
  11.   
  12.     // Use this for initialization  
  13.     void Start()  
  14.     {  
  15.         InitMap();  //初始化地图  
  16.         Point start = map[2, 2];  
  17.         Point end = map[6, 2];  
  18.         FindPath(start, end);  
  19.         ShowPath(start, end);  
  20.     }  
  21.   
  22.   
  23.     private void ShowPath(Point start, Point end)  
  24.     {  
  25.         int z = -1;  
  26.         Point temp = end;  
  27.         while (true)  
  28.         {  
  29.             //Debug.Log(temp.X + "," + temp.Y);  
  30.             Color c = Color.gray;  
  31.             if (temp == start)  
  32.             {  
  33.                 c = Color.green;  
  34.             }  
  35.             else if (temp == end)  
  36.             {  
  37.                 c = Color.red;  
  38.             }  
  39.             CreateCube(temp.X, temp.Y,z, c);  
  40.   
  41.             if (temp.Parent == null)  
  42.                 break;  
  43.             temp = temp.Parent;  
  44.         }  
  45.         for (int x = 0; x < mapWith; x++)  
  46.         {  
  47.             for (int y = 0; y < mapHeight; y++)  
  48.             {  
  49.                 if (map[x, y].IsWall)  
  50.                 {  
  51.                     CreateCube(x, y,z, Color.blue);  
  52.                 }  
  53.             }  
  54.         }  
  55.     }  
  56.   
  57.     private void CreateCube(int x, int y,int z, Color color)  
  58.     {  
  59.         GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);  
  60.         go.name = x+","+y;  
  61.         go.transform.position = new Vector3(x, y, z);  
  62.         go.GetComponent().material.color = color;  
  63.     }  
  64.   
  65.     private void InitMap()  
  66.     {  
  67.         for (int x = 0; x < mapWith; x++)  
  68.         {  
  69.             for (int y = 0; y < mapHeight; y++)  
  70.             {  
  71.                 map[x, y] = new Point(x, y);  
  72.                 CreateCube(x, y,0, Color.black);  
  73.             }  
  74.         }  
  75.         map[4, 1].IsWall = true;  
  76.         map[4, 2].IsWall = true;  
  77.         map[4, 3].IsWall = true;  
  78.         map[4, 4].IsWall = true;  
  79.         map[4, 5].IsWall = true;  
  80.         map[4, 6].IsWall = true;  
  81.     }  
  82.     ///   
  83.     /// 查找最优路径  
  84.     ///   
  85.     ///   
  86.     ///   
  87.     private void FindPath(Point start, Point end)  
  88.     {  
  89.         List openList = new List();  
  90.         List closeList = new List();  
  91.         openList.Add(start);    //将开始位置添加进Open列表  
  92.         while (openList.Count > 0)//查找退出条件  
  93.         {  
  94.             Point point = FindMinFOfPoint(openList);//查找Open列表中最小的f值  
  95.             print(point.F + ";" + point.X + "," + point.Y);  
  96.             openList.Remove(point); closeList.Add(point);//不再考虑当前节点  
  97.   
  98.             List surroundPoints = GetSurroundPoints(point);//得到当前节点的四周8个节点  
  99.             PointsFilter(surroundPoints, closeList);//将周围节点中已经添加进Close列表中的节点移除  
  100.             foreach (Point surroundPoint in surroundPoints)  
  101.             {  
  102.                 if (openList.IndexOf(surroundPoint) > -1)//如果周围节点在open列表中  
  103.                 {  
  104.                     float nowG = CalcG(surroundPoint, surroundPoint.Parent);//计算经过的Open列表中最小f值到周围节点的G值  
  105.                     if (nowG < surroundPoint.G)  
  106.                     {  
  107.                         print("123");  
  108.                         surroundPoint.UpdateParent(point, nowG);  
  109.                     }  
  110.                 }  
  111.                 else//周围节点不在Open列表中  
  112.                 {  
  113.                     surroundPoint.Parent = point;//设置周围列表的父节点  
  114.                     CalcF(surroundPoint, end);//计算周围节点的F,G,H值  
  115.                     openList.Add(surroundPoint);//最后将周围节点添加进Open列表  
  116.                 }  
  117.             }  
  118.             //判断一下  
  119.             if (openList.IndexOf(end) > -1)  
  120.             {  
  121.                 break;  
  122.             }  
  123.         }  
  124.   
  125.     }  
  126.   
  127.     private void PointsFilter(List src, List closeList)  
  128.     {  
  129.         foreach (Point p in closeList)  
  130.         {  
  131.             if (src.IndexOf(p) > -1)  
  132.             {  
  133.                 src.Remove(p);  
  134.             }  
  135.         }  
  136.     }  
  137.   
  138.     private List GetSurroundPoints(Point point)  
  139.     {  
  140.         Point up = null, down = null, left = null, right = null;  
  141.         Point lu = null, ru = null, ld = null, rd = null;  
  142.         if (point.Y < mapHeight - 1)  
  143.         {  
  144.             up = map[point.X, point.Y + 1];  
  145.         }  
  146.         if (point.Y > 0)  
  147.         {  
  148.             down = map[point.X, point.Y - 1];  
  149.         }  
  150.         if (point.X > 0)  
  151.         {  
  152.             left = map[point.X - 1, point.Y];  
  153.         }  
  154.         if (point.X < mapWith - 1)  
  155.         {  
  156.             right = map[point.X + 1, point.Y];  
  157.         }  
  158.         if (up != null && left != null)  
  159.         {  
  160.             lu = map[point.X - 1, point.Y + 1];  
  161.         }  
  162.         if (up != null && right != null)  
  163.         {  
  164.             ru = map[point.X + 1, point.Y + 1];  
  165.         }  
  166.         if (down != null && left != null)  
  167.         {  
  168.             ld = map[point.X - 1, point.Y - 1];  
  169.         }  
  170.         if (down != null && right != null)  
  171.         {  
  172.             rd = map[point.X + 1, point.Y - 1];  
  173.         }  
  174.         List list = new List();  
  175.         if (down != null && down.IsWall == false)  
  176.         {  
  177.             list.Add(down);  
  178.         }  
  179.         if (up != null && up.IsWall == false)  
  180.         {  
  181.             list.Add(up);  
  182.         }  
  183.         if (left != null && left.IsWall == false)  
  184.         {  
  185.             list.Add(left);  
  186.         }  
  187.         if (right != null && right.IsWall == false)  
  188.         {  
  189.             list.Add(right);  
  190.         }  
  191.         if (lu != null && lu.IsWall == false && left.IsWall == false && up.IsWall == false)  
  192.         {  
  193.             list.Add(lu);  
  194.         }  
  195.         if (ld != null && ld.IsWall == false && left.IsWall == false && down.IsWall == false)  
  196.         {  
  197.             list.Add(ld);  
  198.         }  
  199.         if (ru != null && ru.IsWall == false && right.IsWall == false && up.IsWall == false)  
  200.         {  
  201.             list.Add(ru);  
  202.         }  
  203.         if (rd != null && rd.IsWall == false && right.IsWall == false && down.IsWall == false)  
  204.         {  
  205.             list.Add(rd);  
  206.         }  
  207.         return list;  
  208.     }  
  209.   
  210.     private Point FindMinFOfPoint(List openList)  
  211.     {  
  212.         float f = float.MaxValue;  
  213.         Point temp = null;  
  214.         foreach (Point p in openList)  
  215.         {  
  216.             if (p.F < f)  
  217.             {  
  218.                 temp = p;  
  219.                 f = p.F;  
  220.             }  
  221.         }  
  222.         print("返回open列表中最小的f:"+temp.F);  
  223.         return temp;  
  224.     }  
  225.   
  226.     private float CalcG(Point now, Point parent)  
  227.     {  
  228.         return Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(parent.X, parent.Y)) + parent.G;  
  229.     }  
  230.   
  231.     private void CalcF(Point now, Point end)  
  232.     {  
  233.         //F = G + H  
  234.         float h = Mathf.Abs(end.X - now.X) + Mathf.Abs(end.Y - now.Y);  
  235.         float g = 0;  
  236.         if (now.Parent == null)  
  237.         {  
  238.             g = 0;  
  239.         }  
  240.         else  
  241.         {  
  242.             g = Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(now.Parent.X, now.Parent.Y)) + now.Parent.G;  
  243.         }  
  244.         float f = g + h;  
  245.         now.F = f;  
  246.         now.G = g;  
  247.         now.H = h;  
  248.     }  
  249. }  
代码演示是在unity中实现的

你可能感兴趣的:(Unity)