zoukankan      html  css  js  c++  java
  • 路径查找基础知识-动画演示

    这是教程教你建立路径查找算法的第一步。

    路径查找就是在两点之间查找最短路径的算法,你可以在很多地方应用,例如:玩家控制角色时通过点击设置目的地时,就需要用到。

    在开始前,我们需要明确一点:路径查找是在终点给定的情况下才会工作。另外这只是系列教程中的第一步,因此我将说明一些最基本且效率不高的算法,然后我们会利用启发式方法一起研究出著名的A*算法。

    我不想发布另一个A*算法,按照下面的步骤你会掌握路径查找的基本知识而不只是拷贝代码。这也是我用动画演示的原因。

    让我们分析下一个简单基本路径查找算法。

    给定一个起始点并知道终点,我会检测所有邻近起始点的点看是否在路径上,然后挨个检测下面的三个值。

    G从起始点到检测点的成本。我一般用1.

    H:检测点到终点之间可能的距离。我说可能因为我只能稼穑当前点到终点的距离,因为它会随着墙或者移动的成本而改变。最简单计算可能距离的方法就是用曼哈坦距离。我会在例子中使用。如果想知道更多曼哈坦距离的知识,可以查看下《两点之间查找距离的最快方法》这篇博文。

    F:简单的G+H

    *一旦检查完所有相邻瓦块后,我选择F值最小的并设为已经访问过。

    *重新琢磨下后使用选择的瓦块作为起始点。

    几步后,我会遇到下面两种情况:

    *找到终点,过关喽

    *没有合法移动。这种情况我就必须回溯到一个有效的移动才能继续运行或者直接回溯到其实地方,当然那也意味着永远到不了终点。

    第一次没经过优化的算法用AS3写法如下:

    1. package {
    2. import flash.display.Sprite;
    3. import flash.geom.Point;
    4. import flash.events.MouseEvent;
    5. import flash.utils.Timer;
    6. import flash.events.TimerEvent;
    7. public class Main extends Sprite {
    8. private var canvas:Sprite;
    9. private var startPoint:Point;
    10. private var endPoint:Point;
    11. private var currPoint:Point;
    12. private var tileVector:Vector.<Object>;
    13. private var path:Vector.<Point>;
    14. private var timer:Timer;
    15. public function Main() {
    16. canvas=new Sprite();
    17. addChild(canvas);
    18. fieldSetup();
    19. drawGrid();
    20. findPath();
    21. stage.addEventListener(MouseEvent.CLICK,addWall);
    22. }
    23. // fieldSetup prepares the field. Each tile in the field is set to its default value:
    24. // * it's walkable
    25. // * it's not the start point
    26. // * it's not the end point
    27. // * it has not been visited
    28. private function fieldSetup():void {
    29. timer=new Timer(100);
    30. canvas.graphics.clear();
    31. tileVector=new Vector.<Object>();
    32. for (var i:Number=0; i<16; i++) {
    33. tileVector[i]=new Vector.<Object>();
    34. for (var j:Number=0; j<12; j++) {
    35. tileVector[i][j]=new Object();
    36. tileVector[i][j].walkable=true;
    37. tileVector[i][j].startPoint=false;
    38. tileVector[i][j].endPoint=false;
    39. tileVector[i][j].visited=false;
    40. }
    41. }
    42. // while the starting point is choosen absolutely random...
    43. startPoint=new Point(Math.floor(Math.random()*16),Math.floor(Math.random()*12));
    44. tileVector[startPoint.x][startPoint.y].startPoint=true;
    45. tileVector[startPoint.x][startPoint.y].visited=true;
    46. // ... we want the end point to be at least 10 tiles away from start point.
    47. // jsut to make things interesting
    48. do {
    49. endPoint=new Point(Math.floor(Math.random()*16),Math.floor(Math.random()*12));
    50. } while (manhattan(startPoint,endPoint)<10);
    51. tileVector[endPoint.x][endPoint.y].endPoint=true;
    52. }
    53. // findPath function initializes the field and sets the time listener to draw the path
    54. private function findPath():void {
    55. path=new Vector.<Point>();
    56. path.push(startPoint);
    57. for (var i:Number=0; i<16; i++) {
    58. for (var j:Number=0; j<12; j++) {
    59. tileVector[i][j].visited=false;
    60. }
    61. }
    62. currPoint=new Point(startPoint.x,startPoint.y);
    63. timer.addEventListener(TimerEvent.TIMER,step);
    64. timer.start();
    65. }
    66. // step is the core function. Let's explain it deeply
    67. private function step(e:TimerEvent):void {
    68. // f will be the variable which minimum value will decide which direction to take.
    69. // I created a minF variable with an high value to store the minimum f value found
    70. var minF:Number=10000;
    71. // initializing a temporary Point variable
    72. var savedPoint:Point;
    73. for (var i:Number=-1; i<=1; i++) {
    74. for (var j:Number=-1; j<=1; j++) {
    75. // these two for loops together with this if statement will scan for all four directions. No diagonals at the moment.
    76. if ((i!=0 && j==0)||(i==0 && j!=0)) {
    77. // we consider a tile only if:
    78. // * is inside the tile field 
    79. // * is walkable (not a wall)
    80. // * has not been already visited
    81. if (insideField(currPoint,i,j) && tileVector[currPoint.x+i][currPoint.y+j].walkable && !tileVector[currPoint.x+i][currPoint.y+j].visited) {
    82. // now, core of the loop: let's determine g, h and f
    83. // g represents the cost to move from the starting tile to the current tile. At the moment we aren't using this variable
    84. // so getG function will always return 1
    85. var g:Number=getG(i,j);
    86. // h is the presumable distance from the current tile and the ending tile. One of the quickest way to determine it is
    87. // using manhattan distance
    88. var h:Number=manhattan(new Point(currPoint.x+i,currPoint.y+j),endPoint);
    89. // f is just the sum of g and h
    90. var f:Number=g+h;
    91. // if the current f value is lower than the minimum f value found so far...
    92. if (f<minF) {
    93. // ... we update minF value and we save the current tile
    94. minF=f;
    95. savedPoint=new Point(currPoint.x+i,currPoint.y+j);
    96. }
    97. }

    98. }
    99. }
    100. }
    101. // once all neighbor tiles have been scanned, we can have two situations:
    102. // * we found a candidate (savedPoint) for the next tile, and we continue the process
    103. // * we did not found a candidate, so we are on a dead end and we must backtrack
    104. if (savedPoint) {
    105. // continue...
    106. if (savedPoint.x!=endPoint.x||savedPoint.y!=endPoint.y) {
    107. drawTile(savedPoint.x,savedPoint.y,0x0000ff);
    108. }
    109. tileVector[savedPoint.x][savedPoint.y].visited=true;
    110. currPoint=savedPoint;
    111. path.push(currPoint);
    112. if (path.length>2) {
    113. drawTile(path[path.length-2].x,path[path.length-2].y,0xcccccc);
    114. }
    115. if (currPoint.x==endPoint.x&&currPoint.y==endPoint.y) {
    116. // solved
    117. timer.removeEventListener(TimerEvent.TIMER,step);
    118. }
    119. }
    120. else {
    121. // backtrack
    122. if (path.length>1) {
    123. currPoint=path[path.length-2];
    124. drawTile(path[path.length-1].x,path[path.length-1].y,0xffffff);
    125. path.pop();
    126. }
    127. else {
    128. // can't be solved
    129. drawTile(currPoint.x,currPoint.y,0xff00ff);
    130. timer.removeEventListener(TimerEvent.TIMER,step);
    131. }
    132. }
    133. }
    134. // getG function will become really important during next steps, but at the moment just returns 1
    135. private function getG(n1:Number,n2:Number) {
    136. return 1;
    137. }
    138. // function to find manhattan distance between two points
    139. private function manhattan(p1:Point,p2:Point):Number {
    140. return Math.abs(p1.x-p2.x)+Math.abs(p1.y-p2.y);
    141. }
    142. // insideField checks if a given point inside the field will remain inside the field after adding a x and y offset
    143. private function insideField(p:Point,n1:Number,n2:Number):Boolean {
    144. if (p.x+n1>15||p.x+n1<0||p.y+n2>11||p.y+n2<0) {
    145. return false;
    146. }
    147. return true;
    148. }
    149. // function to add/remove a wall or generate another random grid
    150. private function addWall(e:MouseEvent):void {
    151. var row:Number=Math.floor(mouseY/40);
    152. var col:Number=Math.floor(mouseX/40);
    153. if (! tileVector[col][row].startPoint&&! tileVector[col][row].endPoint) {
    154. tileVector[col][row].walkable=! tileVector[col][row].walkable;
    155. }
    156. else {
    157. timer.removeEventListener(TimerEvent.TIMER,step);
    158. fieldSetup();
    159. }
    160. drawGrid();
    161. findPath();
    162. }
    163. // drawTile function just draws a tile in a given position with a given color
    164. private function drawTile(pX:Number,pY:Number,color:Number):void {
    165. canvas.graphics.beginFill(color,1);
    166. canvas.graphics.drawRect(pX*40,pY*40,40,40);
    167. canvas.graphics.endFill();
    168. }
    169. // function to draw the entire grid;
    170. private function drawGrid() {
    171. canvas.graphics.clear();
    172. canvas.graphics.lineStyle(1,0x999999);
    173. for (var i:Number=0; i<16; i++) {
    174. for (var j:Number=0; j<12; j++) {
    175. drawTile(i,j,0xffffff);
    176. if (tileVector[i][j].walkable==false) {
    177. drawTile(i,j,0x000000);
    178. }
    179. if (tileVector[i][j].startPoint==true) {
    180. drawTile(i,j,0x00ff00);
    181. }
    182. if (tileVector[i][j].endPoint==true) {
    183. drawTile(i,j,0xff0000);
    184. }
    185. }
    186. }
    187. }
    188. }
    189. }
    复制代码

    这是结果…有时候路径会比预期的长很多,但是还是会正常工作。

    想.jpg

     

    每个方块的含义:

    绿色方块:起始点

    红色方块:终点

    黑色方块:

    灰色方块:路径

    蓝色方块:当前位置

    紫色方块:放弃了,终点找不到哦

    来看下如何进行交互:

    点击空或者路径瓦块:添加墙

    点击黑色瓦块:消除墙

    点击开始或结束瓦块:随机生成另一个起始点

    今天到此为止,下次我们会进行优化。

    下载源码:

    http://www.emanueleferonato.com/wp-content/uploads/2012/11/astar.zip

    原文链接:http://www.emanueleferonato.com/2012/11/26/the-basics-of-pathfinding-animated-example/

  • 相关阅读:
    103.Binary Tree Zigzag Level Order Traversal
    6.ZigZag Conversion
    102.Binary Tree Level Order Traversal
    interrupted()和isInterrupted()比较+终止线程的正确方法+暂停线程
    117.Populating Next Right Pointers in Each Node II
    Thread.currentThread()与this的区别
    116.Populating Next Right Pointers in Each Node
    UNIX 技巧: UNIX 高手的另外 10 个习惯
    UNIX 高手的 10 个习惯
    关于CGI:Tomcat、PHP、Perl、Python和FastCGI之间的关系
  • 原文地址:https://www.cnblogs.com/gl5773477/p/3971143.html
Copyright © 2011-2022 走看看