• [转]A星寻路算法


    本文为转载  下面的源码为AS3代码  原文地址

    最近正好在做寻路算法,最近看了很多资料,本来想自己写一篇的总结一下的,但是看了这个以后,感觉这个已经写的很通俗易懂了,所以偷了个懒,直接转载了。

    我最后用的不是A星,直接用的最短路径算法,代码只有50行左右,但是跟这个原理其实差不多,改天加了详细注释以后再把我自己的代码贴出来。


    无上天君与梦瑶的传说
    序章,仙界天山住着一位威震仙界的大神(无上天君),我们假设我们就是这位无上天君,我们有神识亿万分身术卫星定位系统(谁说神仙不会高科技),这可是我们寻路的资本!

    第一章,传说美丽的天外仙岛有一个仙女,她叫做梦瑶,她是所有程序神猿的终极梦想,但谁也不知道这座仙岛在什么地方,只知道在仙界之外。无上天君也一直喜欢着这位未知的仙女,以致于单身数万年。

    第二章,我们(无上天君)偶然得到了一张太古的天外地图,这里面记载着去天外仙道的路,但由于太古文字过于古老,我们也无法知道路在何方,但至少我们有了地图

    第三章,我们是谁?我们是拥有卫星定位系统的无上天君,这样通过地图加上系统,我们锁定了仙岛的位置和自己的位置。我们通过系统的雷达辅助,就可以实时计算我们所处位置到仙道的大致距离。

    第四章,一张地图、神识亿万分身术和卫星定位系统我们拥有这些,但路依然是未知的,那么重点来了,我们需要寻路!!!

    第五章,我们发明了一种算法叫做 F 算法(F = G + H),这种算法可以帮助我们确定自己和仙岛的距离,F 是我们从天山到仙岛的估算距离,G 是我们从天山到现在所处位置的实际距离,H 是我们从现在位置到仙岛的估算距离。

    第六章,天外一直是禁地,各种远古萌兽、死亡绝地等等不知吞噬了多少仙界大神的神格。但我们拥有神识亿万分身术,可以用分身不断的试路,但每一个分身的死亡都会减少我们的寿命(虽然我们拥有数以万年的生命,但终究不是不死之身)。

    第七章,我们按照地图将整个天界(包括天外)划分成为很多方格区域,这样方便我们寻路探索。

    第八章,我们从天山派出了四(八)个分身向四面(八方)出发,但由于神力有限只能一次控制一个分身去寻路探索。
    1. 如果当前分身无法到达某个地方,就会返回本体,之后我们控制其他最靠近仙岛的分身(通过F 算法);
    2. 如果分身遇到了远古萌兽或者其他危险,就会死亡,我们就知道了这个地方再也不用去了,之后我们控制其他最靠近仙岛的分身(通过F 算法);
    3. 每当分身到达一个新的方格区域,则会再次分身为四(八)个,再次向周围的区域探索,之后我们控制其他最靠近仙岛的分身(通过F 算法);
    4. 如果当前操控的分身遇到了其他分身,我们就用 F 算法 计算一下两个分身中那个分身到达这个方格区域更快一些(距离更短);
      4.1 如果当前操作的分身比遇到的其他分身到这个方格区域快,则将其他分身返回本体,用当前操作的分身代替他继续寻路,之后我们控制其他最靠近仙岛的分身(通过F 算法);
      4.2 如果是相反的,则将当前操作的分身返回本体,之后我们控制其他最靠近仙岛的分身(通过F 算法);

    第九章,无上天君从仙界消失了,谁也不知道他究竟是找到了仙岛,还是没有找到,但最终所有的程序神猿都将无上天君视为情敌,立誓要杀他千万遍!

    第十章(番外结局一),千辛万苦我们不断地这样分身寻找,分身寻找,最终我们找到了仙岛,见到了心意的仙女梦瑶,抱得美人归!

    第十章(番外结局二),千辛万苦我们不断地这样分身寻找,分身寻找,最终我们探索完了天界所有的区域(方格),都没有找到去仙岛的路,有些区域我们没有探索,是因为太古萌兽以及天外绝地将这些地方包围起来或者分隔,这些成为了我们永远的痛,永远的痛!


    代码界的程序神猿
    随着科技的发展,计算机来临了,程序神猿们发现,我们可以用计算机模拟寻路,他们看到了去天外仙岛的希望,他们看到了仙女梦瑶。

    下面就是他们通过计算机计算的方法(这是一篇网上的教程,我做了一些小小的改动与完善):

    一、搜索区域

    我们假设某个人要从A点到达B点,而一堵墙把这两个点隔开了,如下图所示,绿色部分代表起点A,红色部分代表终点B,蓝色方块部分代表之间的墙。


    你首先会注意到我们把这一块搜索区域分成了一个一个的方格,如此这般,使搜索区域简单化,正是寻找路径的第一步。这种方法将我们的搜索区域简化成了一个普通的二维数组。数组中的每一个元素表示对应的一个方格,该方格的状态被标记为可通过的和不可通过的。通过找出从A点到B点所经过的方格,就能得到AB之间的路径。当路径找出来以后,这个人就可以从一个格子中央移动到另一个格子中央,直到抵达目的地。

    这些格子的中点叫做节点。当你在其他地方看到有关寻找路径的东西时,你会经常发现人们在讨论节点。为什么不直接把它们称作方格呢?因为你不一定要把你的搜索区域分隔成方块,矩形、六边形或者其他任何形状都可以。况且节点还有可能位于这些形状内的任何一处呢?在中间、靠着边,或者什么的。我们就用这种设定,因为毕竟这是最简单的情况。

    二、开始搜索
    当我们把搜索区域简化成一些很容易操作的节点后,下一步就要构造一个搜索来寻找最短路径。在A*算法中,我们从A点开始,依次检查它的相邻节点,然后照此继续并向外扩展直到找到目的地。

    我们通过以下方法来开始搜索:

    1. 从A点开始,将A点加入一个专门存放待检验的方格的“开放列表”中。这个开放列表有点像一张购物清单。当前这个列表中只有一个元素,但一会儿将会有更多。列表中包含的方格可能会是你要途经的方格,也可能不是。总之,这是一个包含待检验方格的列表。

    2.检查起点A相邻的所有可达的或者可通过的方格,不用管墙啊,水啊,或者其他什么无效地形,把它们也都加到开放列表中。对于每一个相邻方格,将点A保存为它们的“父方格”。当我们要回溯路径的时候,父方格是一个很重要的元素。稍后我们将详细解释它。

    3. 从开放列表中去掉方格A,并把A加入到一个“封闭列表”中。封闭列表存放的是你现在不用再去考虑的方格。

    此时你将得到如下图所示的样子。在这张图中,中间深绿色的方格是你的起始方格,所有相邻方格目前都在开放列表中,并且以亮绿色描边。每个相邻方格有一个灰色的指针指向它们的父方格,即起始方格。


    <ignore_js_op>图片2.jpg

    2014-11-25 15:11:50 上传
    下载附件 (4.78 KB)
     



    接下来,我们在开放列表中选一个相邻方格并再重复几次如前所述的过程。但是我们该选哪一个方格呢?具有最小F值的那个。

    三、路径排序
    决定哪些方格会形成路径的关键是下面这个等式:

    F = G + H

    G=从起点A沿着已生成的路径到一个给定方格的移动开销。

    H=从给定方格到目的方格的估计移动开销。这种方式常叫做试探,有点困惑人吧。其实之所以叫做试探法是因为这只是一个猜测。在找到路径之前我们实际上并不知道实际的距离,因为任何东西都有可能出现在半路上(墙啊,水啊什么的)。本文中给出了一种计算H值的方法,网上还有很多其他文章介绍的不同方法。

    我们要的路径是通过反复遍历开放列表并选择具有最小F值的方格来生成的。本文稍后将详细讨论这个过程。我们先进一步看看如何计算那个等式。

    如前所述,G是从起点A沿着已生成的路径到一个给定方格的移动开销,在本例中,我们指定每一个水平或者垂直移动的开销为10,对角线移动的开销为14。因为对角线的实际距离是2的平方根(别吓到啦),或者说水平及垂直移动开销的1.414倍。为了简单起见我们用了10和14这两个值。比例大概对就好,我们还因此避免了平方根和小数的计算。这倒不是因为我们笨或者说不喜欢数学,而是因为对电脑来说,计算这样的数字也要快很多。不然的话你会发现寻找路径会非常慢。

    我们要沿特定路径计算给定方格的G值,办法就是找出该方格的父方格的G值,并根据与父方格的相对位置(斜角或非斜角方向)来给这个G值加上14或者10。在本例中这个方法将随着离起点方格越来越远计算的方格越来越多而用得越来越多。

    有很多方法可以用来估计H值。我们用的这个叫做曼哈顿(Manhattan)方法,即计算通过水平和垂直方向的平移到达目的地所经过的方格数乘以10来得到H值。之所以叫Manhattan方法是因为这就像计算从一个地方移动到另一个地方所经过的城市街区数一样,而通常你是不能斜着穿过街区的。重要的是,在计算H值时并不考虑任何障碍物。因为这是对剩余距离的估计值而不是实际值(通常是要保证估计值不大于实际值――译者注)。这就是为什么这个方式被叫做试探法的原因了。想要了解更多些吗?

    G和H相加就得到了F。第一步搜索所得到的结果如下图所示。每个方格里都标出了F、G和H值。如起点方格右侧的方格标出的,左上角显示的是F值,左下角是G值,右下角是H值。

    <ignore_js_op>图片3.jpg

    2014-11-25 15:13:35 上传
    下载附件 (12.85 KB)
     



    我们来看看这些方格吧。在有字母的方格中,G=10,这是因为它在水平方向上离起点只有一个方格远。起点紧挨着的上下左右都具有相同的G值10。对角线方向的方块G值都是14。

    H值通过估算到红色目标方格的曼哈顿距离而得出。用这种方法得出的起点右侧方格到红色方格有3个方格远,则该方格H值就是30。上面那个方格有4个方格远(注意只能水平和垂直移动),H就是40。你可以大概看看其他方格的H值是怎么计算出来的。

    每一个方格的F值,当然就不过是G和H值之和了。

    继续搜索
    为了继续搜索,我们简单的从开放列表中选择具有最小F值的方格,然后对选中的方格进行如下操作:

    将其从开放列表中移除,并加到封闭列表中。

    检验所有的相邻方格,忽略那些不可通过的或者已经在封闭列表里的方格。如果这个相邻方格不在开放列表中,就把它添加进去。并将当前选定方格设为新添方格的父方格。

    如果某个相邻方格已经在开放列表中了(意味着已经探测过,而且已经设置过父方格――译者),就看看有没有到达那个方格的更好的路径。也就是说,如果从当前选中方格到那个方格,会不会使那个方格的G值更小。如果不能,就不进行任何操作。相反的,如果新路径的G值更小,就将该相邻方格的父方格重设为当前选中方格。(在上图中是改变其指针的方向为指向选中方格。最后,重新计算那个相邻方格的F和G值。如果你看糊涂了,下面会有图解说明。

    好啦,咱们来看看具体点的例子。在初始时的9个方块中,当开始方格被加到封闭列表后,开放列表里还剩8个方格。在这八个方格当中,位于起点方格右边的那个方格具有最小的F值40。所以我们选择这个方格作为下一个中心方格。下图中它以高亮的蓝色表示。

    <ignore_js_op>图片4.jpg

    2014-11-25 15:16:05 上传
    下载附件 (12.65 KB)
     



    首先,我们将选中的方格从开放列表中移除,并加入到封闭列表中(所以用亮蓝色标记)。然后再检验它的相邻节点。那么在它紧邻的右边的方格都是墙,所以不管它们。左边挨着的是起始方格,而起始方格已经在封闭列表中了,所以我们也不管它。

    其他四个方格已经在开放列表中,那么我们就要检验一下如果路径经由当前选中方格到那些方格的话会不会更好,当然,是用G值作为参考。来看看选中方格右上角的那一个方格,它当前的G值是14,如果我们经由当前节点再到达那个方格的话,G值会是20(到当前方格的G值是10,然后向上移动一格就再加上10)。为20的G值比14大,因此这样的路径不会更好。你看看图就会容易理解些。显然从起始点沿斜角方向移动到那个方格比先水平移动一格再垂直移动一格更直接。

    当我们按如上过程依次检验开放列表中的所有四个方格后,会发现经由当前方格的话不会形成更好的路径,那我们就保持目前的状况不变。现在我们已经处理了所有相邻方格,准备到下一个方格吧。

    我们再遍历一下开放列表,目前只有7个方格了。我们挑个F值最小的吧。有趣的是,目前这种情况下,有两个F值为54的方格。那我们怎么选择呢?其实选哪个都没关系,要考虑到速度的话,选你最近加到开放列表中的那一个会更快些。当离目的地越来越近的时候越偏向于选最后发现的方格。实际上这个真的没关系(对待这个的不同造成了两个版本的A*算法得到等长的不同路径)。

    那我们选下面的那个好了,就是起始方格右边的,下图所示的那个。

    <ignore_js_op>图片5.jpg

    2014-11-25 15:16:58 上传
    下载附件 (13.51 KB)
     



    这一次,在我们检验相邻方格的时候发现右边紧挨的那个是墙,就不管它了。上面挨着的那个也同样忽略。还有右边墙下面那个方格我们也不管。为什么呢?因为你不可能切穿墙角直接到达那个格子。实际上你得先向下走然后再通过那个方格。这个过程中是绕着墙角走。(注意:穿过墙角的这个规则是可选的,取决于你的节点是如何放置的。)

    那么还剩下其他五个相邻方格。当前方格的下面那两个还不在开放列表中,那我们把它们加进去并且把当前方格作为它们的父方格。其他三个中有两个已经在封闭列表中了(两个已经在图中用亮蓝色标记了,起始方格,上面的方格),所以就不用管了。最后那个,当前方格左边挨着的,要检查一下经由当前节点到那里会不会降低它的G值。结果不行,所以我们又处理完毕了,然后去检验开放列表中的下一个格子。

    重复这个过程直到我们把目的方格加入到开放列表中了,那时候看起来会像下图这个样子。

    <ignore_js_op>图片6.jpg

    2014-11-25 15:17:56 上传
    下载附件 (27.35 KB)
     



    注意到没?起始方格下两格的位置,那里的格子已经和前一张图不一样了。之前它的G值是28并且指向右上方的那个方格。现在它的G值变成了20并且指向了正上方的方格。这个改变是在搜索过程中,它的G值被核查时发现在某个新路径下可以变得更小时发生的。然后它的父方格也被重设并且重新计算了G值和F值。在本例中这个改变看起来好像不是很重要,但是在很多种情况下这种改变会使到达目标的最佳路径变得非常不同。

    那么我们怎样来自动得出实际路径的呢?很简单,只要从红色目标方格开始沿着每一个方格的指针方向移动,依次到达它们的父方格,最终肯定会到达起始方格。那就是你的路径!如下图所示。从A方格到B方格的移动就差不多是沿着这个路径从每个方格中心(节点)移动到另一个方格中心,直到抵达终点。简单吧!

    <ignore_js_op>图片7.jpg

    2014-11-25 15:18:42 上传
    下载附件 (28.02 KB)
     




    A*算法总结
    1.将开始节点放入开放列表(开始节点的F和G值都视为0);

    2. 重复一下步骤
            i. 在开放列表中查找具有最小F值的节点,并把查找到的节点作为当前节点;
            ii. 把当前节点从开放列表删除, 加入到封闭列表;
    Iii. 对当前节点相邻的每一个节点依次执行以下步骤:
                    ① 如果该相邻节点不可通行或者该相邻节点已经在封闭列表中,则什么操作也不执行,继续检验下一个节点;
                    ② 如果该相邻节点没有探测过,则将该节点添加到开放列表中, 并将该相邻节点的父节点设为当前节点,同时保存该相邻节点的G和F值;
                    ③ 如果该相邻节点在开放列表中, 则判断若经由当前节点到达该相邻节点的G值是否小于原来保存的G值,若小于,则将该相邻节点的父节点设为当前节点,并重新设置该相邻节点的G和F值.
            Iv. 循环结束条件:
                    ① 当终点节点被加入到开放列表作为待检验节点时, 表示路径被找到,此时应终止循环;
                    ② 当开放列表为空,表明已无可以添加的新节点,而已检验的节点中没有终点节点则意味着路径无法被找到,此时也结束循环;

    3. 从终点节点开始沿父节点遍历, 并保存整个遍历到的节点坐标,遍历所得的节点就是最后得到的路径;


    核心寻路类(AStar):

    [Actionscript3] 纯文本查看 复制代码
    001
    002
    003
    004
    005
    006
    007
    008
    009
    010
    011
    012
    013
    014
    015
    016
    017
    018
    019
    020
    021
    022
    023
    024
    025
    026
    027
    028
    029
    030
    031
    032
    033
    034
    035
    036
    037
    038
    039
    040
    041
    042
    043
    044
    045
    046
    047
    048
    049
    050
    051
    052
    053
    054
    055
    056
    057
    058
    059
    060
    061
    062
    063
    064
    065
    066
    067
    068
    069
    070
    071
    072
    073
    074
    075
    076
    077
    078
    079
    080
    081
    082
    083
    084
    085
    086
    087
    088
    089
    090
    091
    092
    093
    094
    095
    096
    097
    098
    099
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    /*
     * 作  者:TKCB
     * 作者信息:身高(167cm+);体重(60kg±);年龄(90后);籍贯(陕西西安);星座(双鱼座);血型(O型);人生格言(The king come back.)。
     * 交流学习:加QQ群[AS3殿堂之路](96759336),群里有无数主城、架构、妹子、LOL战友,欢迎交流讨论。
     * 联系方式:QQ(2414268040);E-mail([url=mailto:tkcb@qq.com]tkcb@qq.com[/url]);手机(15029932353)。
     */
     
     
    package seekRoute
    {
            import flash.display.Sprite;
            import flash.display.Shape;
             
            /**
             * A*算法类
             */
            public class AStar extends Sprite
            {
                    //************************ ************************* 属  性 ******************** *********** *** **////
                    /** 地图可通行数组,用于存放地图的可通行信息。数组元素为 0 表示可通行,数组元素为 1 表示是障碍地形 */
                    private var mapPassableArr : Array;
                     
                    /** 地图节点数组数组,用于存放所有节点(包括普通节点、开始节点、结束节点、障碍节点等等) */
                    private var mapNodalPointArr : Array;
                     
                    /** 开放列表 */
                    private var openList : Array;
                     
                    /** 开始节点 */
                    private var startNodalPoint : NodalPoint;
                     
                    /** 结束节点 */
                    private var endNodalPoint : NodalPoint;
                     
                    /** 是否结束寻路 */
                    public var isEnd : Boolean = false;
                     
                    /** 是否寻找到路线(寻路结果),true为寻找到了,false为没有寻找到 */
                    public var isRoute : Boolean = false;
                     
                    /** 找到的路线节点数组 */
                    public var routeArr : Array;
                     
                    /** 是否开启八方向寻路 */
                    public var isEight : Boolean = true;
                     
                     
                    //************************ ************************* 构造函数 ******************** *********** *** **////
                    /**
                     * 构造函数
                     */
                    public function AStar ( arr : Array )
                    {
                            setSeekRouteMap ( arr );
                    }
                     
                     
                    //************************ ************************* 方  法 ******************** *********** *** **////
                    /**
                     * 设置新的寻路地图
                     */
                    public function setSeekRouteMap ( arr : Array ) : void
                    {
                            mapPassableArr = arr;
                            mapNodalPointArr = [];
                             
                            var nodalPoint : NodalPoint;
                            var i : int;
                            var ilen : int = mapPassableArr.length;
                            var j : int;
                            var jlen : int = mapPassableArr[ 0 ].length;
                             
                            for ( i = 0; i < ilen; i++ )
                            {
                                    for ( j = 0; j < jlen; j++ )
                                    {
                                            if ( mapNodalPointArr[ i ] == null )
                                            {
                                                    mapNodalPointArr[ i ] = [];
                                            }
                                            // 判断是否可以通行,并创建相应的节点
                                            if ( mapPassableArr[ i ][ j ] == 0 )
                                            {
                                                    nodalPoint = new NodalPoint();
                                                    nodalPoint.num1 = i;
                                                    nodalPoint.num2 = j;
                                                    mapNodalPointArr[ i ][ j ] = nodalPoint;
                                            }
                                            else
                                            {
                                                    nodalPoint = new NodalPoint();
                                                    nodalPoint.isPassable = false;
                                                    nodalPoint.num1 = i;
                                                    nodalPoint.num2 = j;
                                                    mapNodalPointArr[ i ][ j ] = nodalPoint;
                                            }
                                    }
                            }
                    }
                     
                    /**
                     * 寻找路线
                     * @param startNum1                开始节点,在地图中的竖向坐标(X轴)的索引值
                     * @param startNum2                开始节点,在地图中的横向坐标(Y轴)的索引值
                     * @param endNum1                结束节点,在地图中的竖向坐标(X轴)的索引值
                     * @param endNum2                结束节点,在地图中的横向坐标(Y轴)的索引值
                     */
                    public function seekRoute ( startNum1 : int, startNum2 : int, endNum1 : int, endNum2 : int ) : void
                    {
                            trace( "寻找路线!!!" );
                            isRoute = false;
                            routeArr = null;
                            openList = [];
                             
                            //// 将传入的开始点位置对应的节点设为开始节点,将传入的结束点位置对应的节点设为结束节点
                            startNodalPoint = mapNodalPointArr[ startNum1 ][ startNum2 ];
                            endNodalPoint = mapNodalPointArr[ endNum1 ][ endNum2 ];
                            startNodalPoint.isStart = true;
                            endNodalPoint.isEnd = true;
                             
                             
                            //// 核心步骤【1】:将开始节点加入开放列表
                            openList[ 0 ] = startNodalPoint;
                            openList[ 0 ].inOpenList = true;
                             
                            //// 核心步骤【2】:不断的检测开放列表中的节点
                            while ( isEnd == false )
                            {
                                    //——trace( "循环探测中……" );
                                     
                                    var i : int;
                                    var ilen : int;
                                    var j : int;
                                    var jlen : int;
                                    var currentNP : NodalPoint;                // 当前节点
                                    var currentNum : int;                        // 当前节点在开放列表中的索引位置
                                     
                                    //// 核心步骤【2.1】:在开放列表中查找具有最小 F 值的节点,并把查找到的节点作为当前节点
                                    ilen = openList.length;
                                    currentNP = openList[ 0 ];
                                    currentNum = 0;
                                    for ( i = 0; i < ilen; i++ )
                                    {
                                            if ( openList[ i ].F < currentNP.F )
                                            {
                                                    currentNP = openList[ i ];
                                                    currentNum = i;
                                            }       
                                    }
                                     
                                    //// 核心步骤【2.2】:把当前节点从开放列表删除, 加入到封闭列表
                                    currentNP.inOpenList = false;
                                    openList[ currentNum ] = openList[ openList.length - 1 ];
                                    openList.pop();
                                     
                                    //// 核心步骤【2.3】:对当前节点相邻的每一个节点依次执行以下步骤:
                                    //// 下面是一个九宫格循环,横向和竖向分别循环三次
                                    var currentDetectNP : NodalPoint;                // 当前要被探测的节点
                                    var num1 : int;                // 被探测节点的竖向坐标索引
                                    var num2 : int;                // 被探测节点的横向坐标索引
                                    var GNum : int;                // 用于计算 G 值
                                    var FNum : int;                // 用于计算 F 值
                                    ilen = 2;
                                    jlen = 2;
                                    for ( i = -1; i < ilen; i++ )
                                    {
                                            for ( j = -1; j < jlen; j++ )
                                            {
                                                    //// 如果关闭了八方向寻路(即为四方向寻路),则忽略掉左上、左下、右上、右下的四个节点的探路
                                                    if ( isEight == false )
                                                    {
                                                            // 左上角节点
                                                            if ( i == -1 && j == -1 ) continue;
                                                            // 右上角节点
                                                            if ( i == -1 && j == 1 ) continue;
                                                            // 左下角节点旁边障碍物判断
                                                            if ( i == 1 && j == -1 ) continue;
                                                            // 右下角节点旁边障碍物判断
                                                            if ( i == 1 && j == 1 ) continue;
                                                    }
                                                     
                                                    //// 核心步骤【2.3.1】:如果该相邻节点不可通行或者该相邻节点已经在封闭列表中,则什么操作也不执行,继续检验下一个节点
                                                    GNum = 10;                // 设置 G 值
                                                    num1 = currentNP.num1 + i;
                                                    num2 = currentNP.num2 + j;
                                                    //// 各种不需要检测的情况……
                                                    if ( i == 0 && j == 0 )
                                                    {
                                                            //——trace( "探测的点为当前节点!!!" );
                                                            continue;
                                                    }
                                                    if ( num1 < 0 )
                                                    {
                                                            //——trace( "探测的点超出上边!!!" );
                                                            continue;
                                                    }
                                                    if ( num1 == mapNodalPointArr.length )
                                                    {
                                                            //——trace( "探测的点超出下边!!!" );
                                                            continue;
                                                    }
                                                    if ( num2 < 0 )
                                                    {
                                                            //——trace( "探测的点超出左边!!!" );
                                                            continue;
                                                    }
                                                    if ( num2 == mapNodalPointArr[ 0 ].length )
                                                    {
                                                            //——trace( "探测的点超出右边!!!" );
                                                            continue;
                                                    }
                                                    //// 从节点数组中获取当前要探测的节点
                                                    currentDetectNP = mapNodalPointArr[ num1 ][ num2 ];
                                                    if ( currentDetectNP.isStart )
                                                    {
                                                            //——trace( "该节点为开始点!!!" );
                                                            continue;
                                                    }
                                                    if ( currentDetectNP.isPassable == false )
                                                    {
                                                            //——trace( "该节点不可通行!!!" );
                                                            continue;
                                                    }
                                                    if ( currentDetectNP.inCloseList )
                                                    {
                                                            //——trace( "该节点在封闭列表中!!!" );
                                                            continue;
                                                    }
                                                    // 左上角节点旁边障碍物判断
                                                    if ( i == -1 && j == -1 )
                                                    {
                                                            if ( mapNodalPointArr[ num1 + 1 ][ num2 ].isPassable == false )
                                                            {
                                                                    //——trace( "【左上角】该节点的下边有障碍物!!!" );
                                                                    continue;
                                                            }
                                                            if ( mapNodalPointArr[ num1 ][ num2 + 1 ].isPassable == false )
                                                            {
                                                                    //——trace( "【左上角】该节点的右边有障碍物!!!" );
                                                                    continue;
                                                            }
                                                            GNum = 14;                // 设置 G 值
                                                    }
                                                    // 右上角节点旁边障碍物判断
                                                    if ( i == -1 && j == 1 )
                                                    {
                                                            if ( mapNodalPointArr[ num1 + 1 ][ num2 ].isPassable == false )
                                                            {
                                                                    //——trace( "【右上角】该节点的下边有障碍物!!!" );
                                                                    continue;
                                                            }
                                                            if ( mapNodalPointArr[ num1 ][ num2 - 1 ].isPassable == false )
                                                            {
                                                                    //——trace( "【右上角】该节点的左边有障碍物!!!" );
                                                                    continue;
                                                            }
                                                            GNum = 14;                // 设置 G 值
                                                    }
                                                    // 左下角节点旁边障碍物判断
                                                    if ( i == 1 && j == -1 )
                                                    {
                                                            if ( mapNodalPointArr[ num1 - 1 ][ num2 ].isPassable == false )
                                                            {
                                                                    //——trace( "【左下角】该节点的上边有障碍物!!!" );
                                                                    continue;
                                                            }
                                                            if ( mapNodalPointArr[ num1 ][ num2 + 1 ].isPassable == false )
                                                            {
                                                                    //——trace( "【左下角】该节点的右边有障碍物!!!" );
                                                                    continue;
                                                            }
                                                            GNum = 14;                // 设置 G 值
                                                    }
                                                    // 右下角节点旁边障碍物判断
                                                    if ( i == 1 && j == 1 )
                                                    {
                                                            if ( mapNodalPointArr[ num1 - 1 ][ num2 ].isPassable == false )
                                                            {
                                                                    //——trace( "【右下角】该节点的上边有障碍物!!!" );
                                                                    continue;
                                                            }
                                                            if ( mapNodalPointArr[ num1 ][ num2 - 1 ].isPassable == false )
                                                            {
                                                                    //——trace( "【右下角】该节点的左边有障碍物!!!" );
                                                                    continue;
                                                            }
                                                            GNum = 14;                // 设置 G 值
                                                    }
                                                     
                                                    //// 核心步骤【2.3.2】:如果该相邻节点没有探测过,则将该节点添加到开放列表中, 并将该相邻节点的父节点设为当前节点,同时保存该相邻节点的G和F值
                                                    if ( currentDetectNP.isDetect == false )
                                                    {
                                                            //——trace( "普通节点" );
                                                            currentDetectNP.isDetect = true;                // 设置为被探测过
                                                            currentDetectNP.inOpenList = true;                // 加入开放列表
                                                            openList.push( currentDetectNP );                // 加入开放列表
                                                            currentDetectNP.parentNodalPoint = currentNP;                // 设置父节点
                                                            // 设置 G 值
                                                            currentDetectNP.G = currentNP.G + GNum;
                                                            // 设置 H 值
                                                            currentDetectNP.H = ( Math.abs( currentDetectNP.num1 - endNodalPoint.num1 ) + Math.abs( currentDetectNP.num2 - endNodalPoint.num2 ) ) * 10;
                                                            // 设置 F 值
                                                            currentDetectNP.F = currentDetectNP.G + currentDetectNP.H;
                                                    }
                                                     
                                                    //// 核心步骤【2.3.3】:如果该相邻节点在开放列表中, 则判断若经由当前节点到达该相邻节点的G值是否小于原来保存的G值,若小于,则将该相邻节点的父节点设为当前节点,并重新设置该相邻节点的G和F值
                                                    if ( currentDetectNP.inOpenList )
                                                    {
                                                            //——trace( "开放列表中的节点!!!" );
                                                            FNum = currentNP.G + GNum + ( ( Math.abs( currentDetectNP.num1 - endNodalPoint.num1 ) + Math.abs( currentDetectNP.num2 - endNodalPoint.num2 ) ) * 10 )
                                                            if ( FNum < currentDetectNP.F )
                                                            {
                                                                    currentDetectNP.parentNodalPoint = currentNP;                // 设置父节点
                                                                    // 设置 G 值
                                                                    currentDetectNP.G = currentNP.G + GNum;
                                                                    // 设置 F 值
                                                                    currentDetectNP.F = currentDetectNP.G + currentDetectNP.H;
                                                            }       
                                                    }
                                                    //// 核心步骤【2.4.1】:循环结束条件:当结束节点被加入到开放列表作为待检验节点时,表示路径被找到,此时应终止循环
                                                    if ( currentDetectNP.isEnd )
                                                    {
                                                            //——trace( "寻路结束,找到了路线!" );
                                                            isEnd = true;                // 寻路结束
                                                            isRoute = true;                // 找到了路线
                                                            routeArr = [];
                                                            routeArr.push( currentDetectNP );
                                                             
                                                            //// 核心步骤【3】:从终点节点开始沿父节点遍历, 并保存整个遍历到的节点坐标,遍历所得的节点就是最后得到的路径
                                                            while ( true )
                                                            {
                                                                    if ( currentDetectNP.parentNodalPoint.isStart == false )
                                                                    {
                                                                            routeArr.push( currentDetectNP.parentNodalPoint );
                                                                            currentDetectNP = currentDetectNP.parentNodalPoint;
                                                                    }
                                                                    else
                                                                    {
                                                                            break;
                                                                    }
                                                            }
                                                    }
                                            }
                                    }
                                     
                                    //// 核心步骤【2.4.2】:循环结束条件:开放列表为空,表明已无可以添加的新节点,而已检验的节点中没有终点节点则意味着路径无法被找到,此时也结束循环
                                    if ( openList.length == 0 )
                                    {
                                            //——trace( "寻路结束,没有找到路线!" );
                                            routeArr = [];
                                            isEnd = true;                        // 寻路结束
                                            isRoute = false;                // 没有找到路线
                                    }
                            }
                    }
                     
                     
            }
    }

    --------------------------------------------------------------------------我是分界线--------------------------------------------------------------------

  • 相关阅读:
    动态调用WCF服务
    矩阵的坐标变换(转)
    【.NET线程--进阶(一)】--线程方法详解
    [转] Location语法规则
    [转] 深入理解vue 一些底层原理
    [转] lodash常用方法
    [转] Vue 组件间通信六种方式(完整版)
    [转] vuejs组件通信精髓归纳
    [转] 浅谈移动端设备标识码:DeviceID、IMEI、IDFA、UDID和UUID
    [转] vue自定义组件中的v-model简单解释
  • 原文地址:https://www.cnblogs.com/Jenny-sider/p/6278650.html
Copyright © 2020-2023  润新知