这两天在做百度前端技术学院的题目,其中有涉及到寻路相关的,于是就找来相关博客进行阅读。

看了Create Chen写的理解A*寻路算法具体过程之后,我很快就理解A*算法的原理。不得不说作者写的很好,通熟易懂,图片也做的很好,可见作者在这上面是花了心思的。如果让我写,我是写不来这么好的。

唯一的不足就是,因为我学的是js,因此最后给我的源码我是用不了的......因此才有自己写一篇的打算,方面学习js人的学习。然而前面的描述我就借用他的了,因为如果然我的表达能力实在是太渣了。

简易地图

如图所示简易地图, 其中绿色方块的是起点 (用 A 表示), 中间蓝色的是障碍物, 红色的方块 (用 B 表示) 是目的地. 为了可以用一个二维数组来表示地图, 我们将地图划分成一个个的小方块.

二维数组在游戏中的应用是很多的, 比如贪吃蛇和俄罗斯方块基本原理就是移动方块而已. 而大型游戏的地图, 则是将各种"地貌"铺在这样的小方块上.

寻路步骤

1. 从起点A开始, 把它作为待处理的方格存入一个"开启列表", 开启列表就是一个等待检查方格的列表.

2. 寻找起点A周围可以到达的方格, 将它们放入"开启列表", 并设置它们的"父方格"为A.

3. 从"开启列表"中删除起点 A, 并将起点 A 加入"关闭列表", "关闭列表"中存放的都是不需要再次检查的方格

图中浅绿色描边的方块表示已经加入 "开启列表" 等待检查. 淡蓝色描边的起点 A 表示已经放入 "关闭列表" , 它不需要再执行检查.

从 "开启列表" 中找出相对最靠谱的方块, 什么是最靠谱? 它们通过公式 F=G+H 来计算.

        F = G + H

表示从起点 A 移动到网格上指定方格的移动耗费 (可沿斜方向移动).

表示从指定的方格移动到终点 B 的预计耗费 (H 有很多计算方法, 这里我们设定只可以上下左右移动).

我们假设横向移动一个格子的耗费为10, 为了便于计算, 沿斜方向移动一个格子耗费是14. 为了更直观的展示如何运算 FGH, 图中方块的左上角数字表示 F, 左下角表示 G, 右下角表示 H. 看看是否跟你心里想的结果一样?

从 "开启列表" 中选择 F 值最低的方格 C (绿色起始方块 A 右边的方块), 然后对它进行如下处理:

4. 把它从 "开启列表" 中删除, 并放到 "关闭列表" 中.

5. 检查它所有相邻并且可以到达 (障碍物和 "关闭列表" 的方格都不考虑) 的方格. 如果这些方格还不在 "开启列表" 里的话, 将它们加入 "开启列表", 计算这些方格的 G, H 和 F 值各是多少, 并设置它们的 "父方格" 为 C.

6. 如果某个相邻方格 D 已经在 "开启列表" 里了, 检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些, 如果新的G值更低, 那就把它的 "父方格" 改为目前选中的方格 C, 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做.

如图, 我们选中了 C 因为它的 F 值最小, 我们把它从 "开启列表" 中删除, 并把它加入 "关闭列表". 它右边上下三个都是墙, 所以不考虑它们. 它左边是起始方块, 已经加入到 "关闭列表" 了, 也不考虑. 所以它周围的候选方块就只剩下 4 个. 让我们来看看 C 下面的那个格子, 它目前的 G 是14, 如果通过 C 到达它的话, G将会是 10 + 10, 这比 14 要大, 因此我们什么也不做.

然后我们继续从 "开启列表" 中找出 F 值最小的, 但我们发现 C 上面的和下面的同时为 54, 这时怎么办呢? 这时随便取哪一个都行, 比如我们选择了 C 下面的那个方块 D.

D 右边已经右上方的都是墙, 所以不考虑, 但为什么右下角的没有被加进 "开启列表" 呢? 因为如果 C 下面的那块也不可以走, 想要到达 C 右下角的方块就需要从 "方块的角" 走了, 在程序中设置是否允许这样走. (图中的示例不允许这样走)

就这样, 我们从 "开启列表" 找出 F 值最小的, 将它从 "开启列表" 中移掉, 添加到 "关闭列表". 再继续找出它周围可以到达的方块, 如此循环下去...

那么什么时候停止呢? —— 当我们发现 "开始列表" 里出现了目标终点方块的时候, 说明路径已经被找到.

如何找回路径

如上图所示, 除了起始方块, 每一个曾经或者现在还在 "开启列表" 里的方块, 它都有一个 "父方块", 通过 "父方块" 可以索引到最初的 "起始方块", 这就是路径.

将整个过程抽象

把起始格添加到 "开启列表" 
do 

       寻找开启列表中F值最低的格子, 我们称它为当前格. 
       把它切换到关闭列表. 
       对当前格相邻的8格中的每一个 
          if (它不可通过 || 已经在 "关闭列表" 中) 
          { 
                什么也不做. 
           } 
          if (它不在开启列表中) 
          { 
                把它添加进 "开启列表", 把当前格作为这一格的父节点, 计算这一格的 FGH 
          if (它已经在开启列表中) 
          { 
                if (用G值为参考检查新的路径是否更好, 更低的G值意味着更好的路径) 
                    { 
                            把这一格的父节点改成当前格, 并且重新计算这一格的 GF 值. 
                    } 
} while( 目标格已经在 "开启列表", 这时候路径被找到) 
如果开启列表已经空了, 说明路径不存在.

最后从目标格开始, 沿着每一格的父节点移动直到回到起始格, 这就是路径.

js代码:

//其中的MAP.arr是二维数组
function searchRoad(start_x,start_y,end_x,end_y){
var openList=[], //开启列表
closeList=[], //关闭列表
result=[], //结果数组
result_index; //结果数组在开启列表中的序号 openList.push({x:start_x,y:start_y,G:0});//把当前点加入到开启列表中,并且G是0 do{
var currentPoint = openList.pop();
closeList.push(currentPoint);
var surroundPoint=SurroundPoint(currentPoint);
for(var i in surroundPoint) {
var item = surroundPoint[i]; //获得周围的八个点
if (
item.x>=0 && //判断是否在地图上
item.y>=0 &&
item.x<MAP.rows &&
item.y<MAP.cols &&
MAP.arr[item.x][item.y] != 1 && //判断是否是障碍物
!existList(item, closeList) && //判断是否在关闭列表中
MAP.arr[item.x][currentPoint.y]!=1 && //判断之间是否有障碍物,如果有障碍物是过不去的
MAP.arr[currentPoint.x][item.y]!=1) {
//g 到父节点的位置
//如果是上下左右位置的则g等于10,斜对角的就是14
var g = currentPoint.G + ((currentPoint.x - item.x) * (currentPoint.y - item.y) == 0 ? 10 : 14);
if (!existList(item, openList)) { //如果不在开启列表中
//计算H,通过水平和垂直距离进行确定
item['H'] = Math.abs(end_x - item.x) * 10 + Math.abs(end_y - item.y) * 10;
item['G'] = g;
item['F'] = item.H + item.G;
item['parent'] = currentPoint;
openList.push(item);
}
else { //存在在开启列表中,比较目前的g值和之前的g的大小
var index = existList(item, openList);
//如果当前点的g更小
if (g < openList[index].G) {
openList[index].parent = currentPoint;
openList[index].G = g;
openList[index].F=g+openList[index].H;
} }
}
}
//如果开启列表空了,没有通路,结果为空
if(openList.length==0) {
break;
}
openList.sort(sortF);//这一步是为了循环回去的时候,找出 F 值最小的, 将它从 "开启列表" 中移掉
}while(!(result_index=existList({x:end_x,y:end_y},openList))); //判断结果列表是否为空
if(!result_index) {
result=[];
}
else {
var currentObj=openList[result_index];
do{
//把路劲节点添加到result当中
result.unshift({
x:currentObj.x,
y:currentObj.y
});
currentObj=currentObj.parent;
}while (currentObj.x!=start_x || currentObj.y!=start_y); }
return result; }
//用F值对数组排序
function sortF(a,b){
return b.F- a.F;
}
//获取周围八个点的值
function SurroundPoint(curPoint){
var x=curPoint.x,y=curPoint.y;
return [
{x:x-1,y:y-1},
{x:x,y:y-1},
{x:x+1,y:y-1},
{x:x+1,y:y},
{x:x+1,y:y+1},
{x:x,y:y+1},
{x:x-1,y:y+1},
{x:x-1,y:y}
]
}
//判断点是否存在在列表中,是的话返回的是序列号
function existList(point,list) {
for(var i in list) {
if(point.x==list[i].x && point.y==list[i].y) {
return i;
}
}
return false;
}

js实现A*寻路算法的更多相关文章

  1. JS算法之A*(A星)寻路算法

    今天写一个连连看的游戏的时候,接触到了一些寻路算法,我就大概讲讲其中的A*算法. 这个是我学习后的一点个人理解,有错误欢迎各位看官指正. 寻路模式主要有三种:广度游戏搜索.深度优先搜索和启发式搜索. ...

  2. 算法:Astar寻路算法改进,双向A*寻路算法

    早前写了一篇关于A*算法的文章:<算法:Astar寻路算法改进> 最近在写个js的UI框架,顺便实现了一个js版本的A*算法,与之前不同的是,该A*算法是个双向A*. 双向A*有什么好处呢 ...

  3. javascript的Astar版 寻路算法

    去年做一个模仿保卫萝卜的塔防游戏的时候,自己写的,游戏框架用的是coco2d-html5 实现原理可以参考 http://www.cnblogs.com/technology/archive/2011 ...

  4. 5 X 5 方阵引出的寻路算法 之 路径遍历(完结)

      此篇文章源自对一个有趣问题的思考,在我的另一篇博文<一个有趣的 5 X 5 方阵一笔画问题>中有详细介绍.在已知其结论的情况下,作为程序员的我,还是想利用该问题当做出发点,写一个可以遍 ...

  5. A星寻路算法介绍

    你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢? 如果是的话,请看这篇教程,我们会展示如何使用A星寻路算法来实现它! 在网上已经有很多篇关于A星寻路算法 ...

  6. A*寻路算法探究

    A*寻路算法探究 A*算法常用在游戏的寻路,是一种静态网路中求解最短路径的搜索方法,也是解决很多搜索问题的算法.相对于Dijkstra,BFS这些算法在复杂的搜索更有效率.本文在U3D中进行代码的测试 ...

  7. A*寻路算法

    对于初学者而言,A*寻路已经是个比较复杂的算法了,为了便于理解,本文降低了A*算法的难度,规定只能横竖(四方向)寻路,而无法直接走对角线,使得整个算法更好理解. 简而言之,A*寻路就是计算从起点经过该 ...

  8. 算法:Astar寻路算法改进

    早前写了一篇<RCP:gef智能寻路算法(A star)> 出现了一点问题. 在AStar算法中,默认寻路起点和终点都是N x N的方格,但如果用在路由上,就会出现问题. 如果,需要连线的 ...

  9. 用简单直白的方式讲解A星寻路算法原理

    很多游戏特别是rts,rpg类游戏,都需要用到寻路.寻路算法有深度优先搜索(DFS),广度优先搜索(BFS),A星算法等,而A星算法是一种具备启发性策略的算法,效率是几种算法中最高的,因此也成为游戏中 ...

随机推荐

  1. Oracle字符分隔函数(split)

    为了让 PL/SQL 函数返回数据的多个行,必须通过返回一个 REF CURSOR 或一个数据集合来完成.REF CURSOR 的这种情况局限于可以从查询中选择的数据,而整个集合在可以返回前,必须进行 ...

  2. 学习C++.Primer.Plus 6 分支语句和逻辑操作符

    ||. &&操作符是一个顺序点 < 操作符从左向右结合 ; < age < )//17<age为true, = 1,肯定 < 27.所以为整个条件为tru ...

  3. jboss:跟踪所有sql语句及sql参数

    默认情况下,hibernate/JPA 在server.log中记录的SQL语句,参数都是用?代替的,这样不太方便. 网上留传的p6spy在最新的jboss上(EAP 6.0+版本)貌似已经不起作用了 ...

  4. 教你如何反编译Android安装文件apk来偷窥源代码

    本文章首发于浩瀚先森博客,地址:http://www.guohao1206.com/2016/08/23/970.html 1. 准备 - java环境 安装java并配置环境 => JAVA环 ...

  5. 关于Node.js的httpClieint请求报错ECONNRESET的原因和解决措施

    背景说明 最近在工作项目中有下面一个场景: 使用Node.js的express框架实现了一个文件系统服务器端,其中有个API用于客户端上传文件.客户端使用Node.js的HttpClient来调用服务 ...

  6. MVC 中的 ispostback

    总之呢就是在MVC中试下 ispostback那种效果, 环境就是:登录验证loinger, if (Request.HttpMethod == "POST"){} 没理解透彻 源 ...

  7. SWIFT--文档地址

    APPLE官方: https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift ...

  8. redis在window环境下的安装

    1.下载客户端文件 地址:https://github.com/dmajkic/redis/downloads 客户端文件目录说明: 2.启动redis服务端 1.在客户端文件目录下新建一个bat文件 ...

  9. Spring 向页面传值以及接受页面传过来的参数的方式

    来源于:http://www.cnblogs.com/liuhongfeng/p/4802013.html 一.从页面接收参数 Spring MVC接收请求提交的参数值的几种方法: 使用HttpSer ...

  10. SQL 常用函数及示例

    --SQL 基础-->常用函数 --================================== /* 一.函数的分类 SQL函数一般分为两种 单行函数 基于单行的处理,一行产生一个结果 ...