梦幻之旅2

如何寻路

这个是我最困扰的,也是我想了好久才想出来的。游戏中,你肯定要判断当前的路是不是可以走,当然在没有障碍物的情况下,你是可以随便走的,但是我的这个游戏里面有地图,有地图肯定就得有障碍物,我的想法是这样先把一张图片(320*240),按照1*1的大小拆分下来,用一个矩阵表示当前坐标的状态,例如(20,30,1)这三个参数分别表示X坐标,Y坐标,最后一个参数0表示可以到达,1表示不可到达。这样经过矩阵的初始化以后,就可以在逻辑上知道,当前玩家是否可以行走了。

下来就是寻路了,寻路是游戏开发中非常重要的一个元素,如何找到一条最短的路径是程序需要设计的算法,由于自己在算法上也有点研究,自己也想了好久,终于想出了一种办法。

在介绍我的办法之前,我先介绍两种常见的搜索算法,分别是深度优先和广度优先,广度优先是从初始状态一层一层的向下找,知道找到结果目标为止,深度优先是按照一定的顺序先查找完一个分支再查找另一个分支,知道找到目标结果为止。这两种搜索方法有的很大缺陷是它们都是在一个给定的状态空间中穷举。这在状态空间不大的情况下是很适合的算法,但是当空间很大并且不可预测的情况下就不可取。这个时候这两种算法的效率太低甚至有时是无法完成,所以当地图大时候,他们就变得很无力。

我们将以下图作为地图来进行讲解,图中对每一个方格都进行了编号,其中绿色的方格代表起点,红色的方格代表终点,蓝色的方格代表障碍,我们将用这种算法来寻找一条从起点到终点最优路径,为了方便讲解,本地图规定只能走上下左右4个方向,当你理解了我的想法后,8个方向也自然明白

 

14

 

在地图中,每一个方格最基本也要具有两个属性值,一个是方格是通畅的还是障碍,另一个就是指向他父亲方格的指针(相当于双向链表结构中的父结点指针),我们假设方格值为0时为通畅,值为1时为障碍

算法中,有4个相当重要的元素,第一个就是指向父亲结点的指针,第二个就是一个OPEN表,第三个就是CLOSE表,这两张表的具体作用我们在后面边用边介绍,第四个就是每个结点的F值(F值相当于图结构中的权值)

而F = H + G;其中H值为从网格上当前方格移动到终点的预估移动耗费。可能会让你有点迷惑。这样叫的原因是因为它只是个猜测。我们没办法事先知道路径的长度,因为路上可能存在各种障碍。我们定义H值为 终点所在行减去当前格所在行的绝对值 与 终点所在列减去当前格所在列的绝对值之和,而G值为从当前格的父亲格移动到当前格的预估移动耗费,假如当前格的父亲的移动耗费为2,那个当前格的G值就为2+1=3,在这里我们设定一个基数10,每个H和G都要乘以10,这样方便观察

好了,我们开始对地图进行搜索

首先,我们将起点的父亲结点设置为NULL,然后将起点的G值设置为0,再装进open表里面,然后将起点作为父亲结点的周围4个点20,28,30,38(因为我们地图只能走4个方向,如果是8方向,则要加8个点进去)都加进open列表里面,并算去每个结点的H值,然后再将起点从open列表删除,放进close表中,我们将放进close表的所有方格都用浅蓝色线条进行框边处理,所以这次搜索以后,图片变为如下格式,其中箭头代表的是其父结点

15

其中每个格子的左下方为G值,右下方为H值,左上方为F值,我们拿28号格子为例来讲解一写F值的算法,首先因为终点33在4行7列,而28在4行2列,则行数相差为0,列数相差为5,总和为5,再乘以我们先前定的基数10,所以H值为50,又因为从28的父结点29移动到28,长度为1格,而29号为起点,G值为0,所以在父亲结点29的基础上移动到28所消耗的G值为(0 + 1) *10 = 10,0为父亲结点的G值,1为从29到28的消耗

当前OPEN表中的值: 20,28,30,38 当前CLOSE表中的值: 29

现在我们开始寻找OPEN列表中F值最低的,得出结点30的F值最低,且为40,然后将结点30从OPEN表中删除,然后再加入到CLOSE表中,然后在判断结点30周围4个结点,因为结点31为障碍,结点29存在于CLOSE表中,我们将不处理这两点,只将21和39号结点加入OPEN表中,添加完后地图变为下图样式

当前OPEN表中的值: 20,28,38,21,39 当前CLOSE表中的值: 29,30

16

接着我们重复上面的过程,寻找OPEN表中F值为低的值,我们发现OPEN表中所有结点的F值都为60,我们随即取一个结点,这里我们直接取最后添加进OPEN表中的结点,这样方便访问(因为存在这样的情况,所有从一个点到另外一个点的最短路径可能不只一条),我们取结点39,将他从OPEN表中删除,并添加进CLOSE表中,然后观察39号结点周围的4个结点,因为40号结点为障碍,所以我们不管它,而30号结点已经存在与OPEN表中了,所以我们要比较下假设39号结点为30号结点的父结点,30号结点的G值会不会更小,如果更小的话我们将30结点的父结点改为39号,这里我们以39号结点为父结点,得出30号结点的新G值为20,而30号结点原来的G值为10,并不比原来的小,所以我们不对30号进行任何操作,同样的对38号结点进行上述操作后我们也不对它进行任何操作,接着我们把48号结点添加进OPEN表中,添加完后地图变为下图样式

当前OPEN表中的值: 20,28,38,21,48 当前CLOSE表中的值: 29,30,39

17

以后的过程中我们都重复这样的过程,一直到遍历到了最后终点,通过遍历父结点编号,我们能够得出一条最短路径,具体完整的推导过程我就不写出来了,因为和刚才那几步是一样的,这里我再讲出一个特例,然后基本算法就没问题了

上面的最后一推导中,我们在观察39号结点时,发现他周围已经有结点在OPEN表中了,我说"比较下假设39号结点为30号结点的父结点,30号结点的G值会不会更小,如果更小的话我们将30结点的父结点改为39号",但是刚才没有遇到G值更小的情况,所以这里我假设出一种G值更小的情况,然后让大家知道该怎么操作,假设以39号为父结点,我们得出的30号的新G值为5(只是假设),比30号的原G值10还要小,所以我们要修改路径,改变30号的箭头,本来他是指向29号结点的,我们现在让他指向39号结点,38号结点的操作也一样

好了,算法的大体思路就是这样了,对于8方向的地图来说,唯一的改变就是G值方面,在上下左右,我们的G值是加10,但是在斜方向我们要加14(为什么是14,你可以想想勾股定理)。

下面上代码

 

 1  public class Stack
 2     {
 3         private Node node;
 4         private Stack next;
 5         public Node Node
 6         {
 7             get { return node; }
 8             set { node = value; }
 9         }
10         public Stack Next
11         {
12             get { return next; }
13             set { next = value; }
14         }
15         public Stack()
16         { }
17     }
Stack

 

 1  public class Node
 2     {
 3         private int f;
 4         private int h;
 5         private int g;
 6         private int x;
 7         private int y;
 8         private int index;//对应节点编号索引
 9         private Node parent;
10         private Node[] child;
11         private Node next;
12 
13         public int F
14         {
15             get { return f; }
16             set { f = value; }
17         }
18         public int H
19         {
20             get { return h; }
21             set { h = value; }
22         }
23         public int G
24         {
25             get { return g; }
26             set { g = value; }
27         }
28         public int X
29         {
30             get { return x; }
31             set { x = value; }
32         }
33         public int Y
34         {
35             get { return y; }
36             set { y = value; }
37         }
38         public int Index
39         {
40             get { return index; }
41             set { index = value; }
42         }
43         public Node Parent
44         {
45             get { return parent; }
46             set { parent = value; }
47         }
48         public Node[] Child
49         {
50             get { return child; }
51             set { child = value; }
52         }
53         public Node Next
54         {
55             get { return next; }
56             set { next = value; }
57         }
58 
59 
60         public Node(int x, int y)
61         {
62             this.x = x;
63             this.y = y;
64             this.child = new Node[8];
65         }
66         public Node()
67         {
68             this.child = new Node[8];
69         }
70     }
Node
 1  public class MapTerrain
 2     {
 3         private int _mapPositionX;
 4         public int MapPositionX
 5         {
 6             get
 7             {
 8                 return (this._mapPositionX);
 9             }
10             set
11             {
12                 this._mapPositionX = value;
13             }
14         }
15 
16         private int _mapPositionY;
17         public int MapPositionY
18         {
19             get
20             {
21                 return (this._mapPositionY);
22             }
23             set
24             {
25                 this._mapPositionY = value;
26             }
27         }
28 
29         private int _terrain;
30         public int Terrain
31         {
32             get
33             {
34                 return (this._terrain);
35             }
36             set
37             {
38                 this._terrain = value;
39             }
40         }
41 
42         public MapTerrain()
43         { }
44     }
MapTerrain
  1   public class AStart
  2     {
  3         private Node open;
  4         private Node close;
  5         private Stack stack;
  6         public int[] indexs = new int[30 * 30];
  7         public Point ScreenCenter;
  8         public AStart()
  9         {
 10             this.Inivate();
 11         }
 12 
 13         public void SetScreenCenter(Point Point)
 14         {
 15             ScreenCenter = Point;
 16         }
 17 
 18 
 19         public void SetBlock(List<MapTerrain> MapTerrainList, Point Point)
 20         {
 21             ScreenCenter = Point;
 22             List<MapTerrain> NowMapTerrainList = new List<MapTerrain>();
 23             foreach (MapTerrain MapTerrain in MapTerrainList)
 24             {
 25                 if (MapTerrain.MapPositionX >= Point.X / 320 * 10 - 10 && MapTerrain.MapPositionX < Point.X / 320 * 10 + 20
 26                     && MapTerrain.MapPositionY >= Point.Y / 240 * 10 - 10 && MapTerrain.MapPositionY < Point.Y / 240 * 10 + 20)
 27                 {
 28                     indexs[(MapTerrain.MapPositionY - Point.Y / 240 * 10 + 10) * 30 + MapTerrain.MapPositionX - Point.X / 320 * 10 + 10] = MapTerrain.Terrain;
 29                 }
 30             }
 31         }
 32 
 33         /// <summary>
 34         /// 初始化,默认为0,表示边界不可行走
 35         /// </summary>
 36         private void Inivate()
 37         {
 38             int c, index = 0;//设置边界
 39             for (c = 0; c < 30; c++)
 40             {
 41                 indexs[c] = 0;//初始化 正方形的上边 30个
 42             }
 43             for (c = 1; c < 29; c++)
 44             {
 45                 index += 30;//换行 加等30 即换行
 46                 indexs[index] = 0;//初始化 正方形的左边 28个
 47                 indexs[index + 29] = 0; //初始化 正方形的右边 28个
 48             }
 49             index += 29;
 50             for (c = index; c < 30 * 30; c++)
 51             {
 52                 indexs[c] = 0; //初始化 正方形的下边 30个
 53             }
 54 
 55             open = new Node();
 56             close = new Node();
 57             stack = new Stack();
 58         }
 59 
 60         public List<Node> FindPath(Point StarPoint, Point EndPoint)
 61         {
 62             Inivate();//初始化,设置边界
 63             StarPoint = new Point(StarPoint.X - StarPoint.X % 32, StarPoint.Y - StarPoint.Y % 24);
 64             EndPoint = new Point(EndPoint.X - EndPoint.X % 32, EndPoint.Y - EndPoint.Y % 24);
 65             List<Node> path = new List<Node>();
 66             if (indexs[GetIndex(EndPoint.X, EndPoint.Y)] == 0)
 67                 return path;
 68             Node node, bestnode = null;
 69             int bestIndex = this.GetIndex(EndPoint.X, EndPoint.Y);
 70             node = new Node(StarPoint.X, StarPoint.Y);
 71             node.G = 0;
 72             node.H = (StarPoint.X - EndPoint.X) * (StarPoint.X - EndPoint.X) + (StarPoint.Y - EndPoint.Y) * (StarPoint.Y - EndPoint.Y);//如果开平方就不精确了
 73             node.F = node.H + node.G;
 74             node.Index = this.GetIndex(StarPoint.X, StarPoint.Y);
 75             open.Next = node;
 76             while (true)
 77             {
 78                 bestnode = this.GetBestNode();
 79                 if (bestnode != null)
 80                 {
 81                     if (bestnode.Index.Equals(bestIndex))
 82                         break;
 83                     this.GenerateSuccessors(bestnode, EndPoint);
 84                 }
 85                 else
 86                     break;
 87             }
 88             this.ShowPath(path, bestnode);
 89             return path;
 90         }
 91 
 92         private void ShowPath(List<Node> list, Node bestnode)
 93         {
 94             if (bestnode != null)
 95             {
 96                 if (bestnode.Parent != null)
 97                 {
 98                     this.ShowPath(list, bestnode.Parent);
 99                 }
100                 list.Add(bestnode);
101             }
102         }
103 
104         private void GenerateSuccessors(Node bestnode, Point EndPoint)
105         {
106             int x, y = 0;
107             for (int i = 0; i < 8; i++)
108             {
109                 switch (i)
110                 {
111                     case 0://
112                         x = bestnode.X + 32;
113                         y = bestnode.Y;
114                         if (HasIndex(x, y))
115                             this.GenerateSucc(bestnode, x, y, EndPoint, 10);
116                         break;
117                     case 1://
118                         x = bestnode.X;
119                         y = bestnode.Y + 24;
120                         if (HasIndex(x, y))
121                             this.GenerateSucc(bestnode, x, y, EndPoint, 10);
122                         break;
123                     case 2://西
124                         x = bestnode.X - 32;
125                         y = bestnode.Y;
126                         if (HasIndex(x, y))
127                             this.GenerateSucc(bestnode, x, y, EndPoint, 10);
128                         break;
129                     case 3://
130                         x = bestnode.X;
131                         y = bestnode.Y - 24;
132                         if (HasIndex(x, y))
133                             this.GenerateSucc(bestnode, x, y, EndPoint, 10);
134                         break;
135                     case 4://东北
136                         x = bestnode.X + 32;
137                         y = bestnode.Y - 24;
138                         if (HasIndex(x, y))
139                             this.GenerateSucc(bestnode, x, y, EndPoint, 14);
140                         break;
141                     case 5://东南
142                         x = bestnode.X + 32;
143                         y = bestnode.Y + 24;
144                         if (HasIndex(x, y))
145                             this.GenerateSucc(bestnode, x, y, EndPoint, 14);
146                         break;
147                     case 6://西南
148                         x = bestnode.X - 32;
149                         y = bestnode.Y + 24;
150                         if (HasIndex(x, y))
151                             this.GenerateSucc(bestnode, x, y, EndPoint, 14);
152                         break;
153                     case 7://西北
154                         x = bestnode.X - 32;
155                         y = bestnode.Y - 24;
156                         if (HasIndex(x, y))
157                             this.GenerateSucc(bestnode, x, y, EndPoint, 14);
158                         break;
159                 }
160             }
161         }
162 
163         private void GenerateSucc(Node bestnode, int x, int y, Point EndPoint, int step)
164         {
165             int g, index, c = 0;
166             g = bestnode.G + step;
167             index = this.GetIndex(x, y);
168             Node old, successor = null;
169             if ((old = CheckOpen(index)) != null)
170             {
171                 for (c = 0; c < 8; c++)
172                     if (bestnode.Child[c] == null)
173                         break;
174                 bestnode.Child[c] = old;
175                 if (g < old.G)
176                 {
177                     old.Parent = bestnode; ;
178                     old.G = g;
179                     old.F = g + old.H;
180                 }
181             }
182             else if ((old = CheckClose(index)) != null)
183             {
184                 for (c = 0; c < 8; c++)
185                     if (bestnode.Child[c] == null)
186                         break;
187                 bestnode.Child[c] = old;
188                 if (g < old.G)
189                 {
190                     old.Parent = bestnode;
191                     old.G = g;
192                     old.F = g + old.H;
193                     this.PropagateDown(old, step);
194                 }
195             }
196             else
197             {
198                 successor = new Node(x, y);
199                 successor.Parent = bestnode;
200                 successor.G = g;
201                 successor.H = (x - EndPoint.X) * (x - EndPoint.X) + (y - EndPoint.Y) * (y - EndPoint.Y);
202                 successor.F = g + successor.H;
203                 successor.Index = index;
204                 this.Insert(successor);
205                 for (c = 0; c < 8; c++)
206                     if (bestnode.Child[c] == null)
207                         break;
208                 bestnode.Child[c] = successor;
209             }
210         }
211 
212         private void PropagateDown(Node old, int step)
213         {
214             int c, g = 0;
215             Node child, father = null;
216             g = old.G;
217             for (c = 0; c < 8; c++)
218             {
219                 if ((child = old.Child[c]) == null)
220                     break;
221                 if (g + step < child.G)
222                 {
223                     child.G = g + step;
224                     child.F = child.G + child.H;
225                     child.Parent = old;
226                     this.Push(child);
227                 }
228             }
229             while (stack.Next != null)
230             {
231                 father = this.Pop();
232                 for (c = 0; c < 8; c++)
233                 {
234                     if ((child = father.Child[c]) == null)
235                         break;
236                     if (father.G + step < child.G)
237                     {
238                         child.G = father.G + step;
239                         child.F = child.G + child.H;
240                         child.Parent = father;
241                         this.Push(child);
242                     }
243                 }
244             }
245         }
246 
247         private void Insert(Node successor)
248         {
249             Node tmp1, tmp2 = null;
250             int f = 0;
251             if (open.Next == null)
252             {
253                 open.Next = successor;
254                 return;
255             }
256             f = successor.F;
257             tmp1 = open;
258             tmp2 = open.Next;
259             while ((tmp2 != null) && (tmp2.F < f))
260             {
261                 tmp1 = tmp2;
262                 tmp2 = tmp2.Next;
263             }
264             successor.Next = tmp2;
265             tmp1.Next = successor;
266         }
267 
268         private Node CheckOpen(int index)
269         {
270             Node temp = null;
271             temp = open.Next;
272             while (temp != null)
273             {
274                 if (temp.Index.Equals(index))
275                     return temp;
276                 else
277                     temp = temp.Next;
278             }
279             return temp;
280         }
281 
282         private Node CheckClose(int index)
283         {
284             Node temp = null;
285             temp = close.Next;
286             while (temp != null)
287             {
288                 if (temp.Index.Equals(index))
289                     return temp;
290                 else
291                     temp = temp.Next;
292             }
293             return temp;
294         }
295 
296         private bool HasIndex(int x, int y)
297         {
298             try
299             {
300                 if (!indexs[GetIndex(x, y)].Equals(0))//该节点不是障碍物
301                     return true;
302                 else
303                     return false;
304             }
305             catch (Exception)
306             {
307 
308                 return false;
309             }
310         }
311         /// <summary>
312         /// 寻找最优点
313         /// </summary>
314         /// <returns></returns>
315         private Node GetBestNode()
316         {
317             Node temp = null;
318             if (open.Next != null)
319             {
320                 temp = open.Next;
321                 open.Next = temp.Next;
322                 temp.Next = close.Next;
323                 close.Next = temp;
324             }
325             return temp;
326         }
327 
328         public int GetIndex(int x, int y)
329         {
330             return (y - ScreenCenter.Y + ScreenCenter.Y % 240 + 240) / 24 * 30 + (x - ScreenCenter.X + ScreenCenter.X % 320 + 320) / 32;
331         }
332 
333 
334         private void Push(Node node)
335         {
336             Stack temp = new Stack();
337             temp.Node = node;
338             temp.Next = stack.Next;
339             stack.Next = temp;
340         }
341 
342         private Node Pop()
343         {
344             Node tempNode = null;
345             Stack tempStack = null;
346             tempStack = stack.Next;
347             tempNode = tempStack.Node;
348             stack.Next = tempStack.Next;
349             return tempNode;
350         }
351     }
AStart

 

原文地址:https://www.cnblogs.com/jack-Star/p/3218778.html