A*尋路演算法 - 躲避障礙物 - 找到最短路徑

Juicyangxj發表於2017-11-28

你是否在做一款H5遊戲的時候想創造一些遊戲主角,讓它們移動到指定的位置,避開牆壁和障礙物呢?
下面的案例是由純js實現的,由canvas繪圖,並只考慮向上下左右四鄰域移動。

效果展示圖

有路可走
有路可走

無路可走
無路可走

核心思想

//第一步:畫棋盤,向外擴充套件一圈
    var $map = drawmap(me.opts, me.$element);
// 第二步:畫障礙點和邊界,初始化所有點,左上角為(0,0)
    drawpoint(me, $map);
//這裡預設把起點放入已檢測點
    var startnum = posToindex(me.opts.startY, me.opts.startX, me.opts.mapXnum);
    var endnum = posToindex(me.opts.endY, me.opts.endX, me.opts.mapXnum);
    me.closelist.push(me.wall[startnum]);
//第三步:從起點開始上下左右找擴充套件點出去,並記錄父子點對。如果該點status是0且從未放入過openlist,就放入openlist
//從openlist中找F值最小的點組,我這裡做了個優化,並選擇點組中最後加入openlist的點放入closelist
//從當前已檢測點再向外檢測,要麼檢測到終點就停止,要麼待檢測點全部檢測完還找不到終點就停止。
    searchroad(me, [me.opts.startY, me.opts.startX], startnum);
//第四步:畫檢測的所有點,用紅色細線標出,只需要把每個fatherson裡的父子連起來就好了(這一步可以不畫,跳過)
    drawroad(me);
//第五步:畫最終路線,黃色粗線標出。因為子元素的父元素只有一個,而一個父元素有多個子元素(比較難找),所以我們選擇從終點往起點找,把能連的線全部連起來就好
    drawFinalRoad(me.fatherson, startnum, endnum, me);複製程式碼

我是如下初始化引數的:

    var me = this;
    // 原型中的this不是指的原型物件,而是呼叫物件。
    //用於存地圖的長和寬
    me.opts = $.extend(true, {}, { //用於設彈窗預設值
        mapXnum: 10,
        mapYnum: 10,
        startX: 3,
        startY: 3,
        endX: 6,
        endY: 6,
        wallnum: 10,
        map_Per: 40//每個格子的長度
    }, options);
    me.wall = [];//整張圖資訊,村邊界和所有的障礙物的座標標1,起點標2,終點標3,其餘標0,距離左邊,距離右邊
    //index從上往下數數,從0開始,下次用二維陣列做試試。。。一開始咋先用了一維。。。
    me.openlist = [];//待檢測的點
    me.closelist = [];//已檢測的點
    me.fatherson = [];//檢測過程中記錄父子關係對,父節點向上下左右尋找子節點時記錄一下
    var num = 0;
    for (var i = 0; i < Number(me.opts.mapYnum) + 2; i++) {
        for (var j = 0; j < Number(me.opts.mapXnum) + 2; j++) {
            if (i == 0 || i == Number(me.opts.mapYnum) + 1 || j == 0 || j == Number(me.opts.mapXnum) + 1) {
                me.wall.push({
                    status: 1,
                    pos: [i, j],
                    f_score: 9999,
                    index: num++
                });
            } else if (i == me.opts.startY && j == me.opts.startX) {
                me.wall.push({
                    g_score: 0,
                    status: 2,
                    pos: [i, j],
                    index: num++
                });
            } else if (i == me.opts.endY && j == me.opts.endX) {
                me.wall.push({
                    g_score: 0,
                    h_score: 0,
                    f_score: 0,
                    status: 3,
                    pos: [i, j],
                    index: num++
                });
            } else {
                me.wall.push({
                    g_score: 0,
                    h_score: Math.abs(i - me.opts.endY) + Math.abs(j - me.opts.endX),
                    f_score: 0,
                    status: 0,
                    pos: [i, j],
                    index: num++,
                    inopen: 0
                });
            }
        }
    }複製程式碼

小結

我是這麼理解的,Dijkstra演算法就是從起點開始一圈一圈向外擴散,直到擴散到終點,這樣做肯定,但還是能找到一條最短路徑。貪婪演算法從直觀上理解就是用最快的方法來解決問題,主要目標是,從數學上來理解就是在做判斷時以當前最優解為基礎,每次取的都是當前位置距離終點最近的點,因為演算法是取的區域性最優解,沒有考慮以後的問題,所以整體上是很難找到最短路徑的。
貪婪是每個人都有的,但也要適可而止。我們的目標是要找到一條最短路徑的前提下,速度適當的要快一點。於是,A*演算法就應運而生了,既考慮了起點,又考慮了終點(要選取適合的啟發函式)。明白了它大概的思想後,童鞋們就可以開始摸索啦~~~嘻嘻

相關文章