a*(a-star)演算法是一種靜態路網中求解最短路徑最有效的直接搜尋方法,也是解決許多搜尋問題的有效演算法。演算法中的距離估算值與實際值越接近,最終搜尋速度越快。說白了,就是一種搜尋演算法。
在學習a* 演算法之前,我們要對這個演算法的很多重要名詞做乙個解釋。要學習這個演算法,這些名詞是必須要理解的。
1.結點
為了方便理解,我將稱以下所有的結點為格仔。這麼稱呼會更形象,當然它不一定是格仔,也可能是乙個座標,乙個方格,乙個六邊形格仔,甚至其他的東西。這也是a*演算法的乙個基本單位。
2.列表
開啟列表(openlist):儲存待檢查的格仔的列表。
關閉列表(closelist):儲存檢查完的格仔的列表。
3.f=g+h
這是整個演算法中的核心公式,可以說是整個演算法的靈魂。對於整個地圖來說,每乙個格仔,都在不同時候有不同的f、g、h。
專業術語版:
g:在狀態空間中從初始狀態到狀態n的實際代價。
h:從狀態n到目標狀態的最佳路徑的估計代價。
f:從初始狀態經由狀態n到目標狀態的代價估計。
簡單理解版:
g:起始格仔到該格仔走過的距離
h:該格仔到目標格仔估計的距離
f:g+h
查詢了很多資料或者教學之後,多數的a* 演算法都是這麼算的:規定斜著一格距離為14,直著距離為10。首先原因是因為計算機處理浮點數效率遠比整數低。因此首先這個數值一定要盡可能取整。如果設定兩個格仔的距離為1,那斜向根據勾股定理會取到1.414,因此多數人都把這個值乘以10。取10和14。
4.父節點
就是當前格仔是從哪個格仔走過來的。畢竟尋路是要返回路徑的,而不是單純判斷能不能去。
其實a*演算法的思想並不難。只不過我當時學習的時候被那一大堆專業術語搞得糊塗了,才讓我好久都無法理解。
1.確定起點和終點(當然提前要檢查這兩個點是不是可到達的)
2.把起點新增到開啟列表
3.把起點周圍所有可到達的點以父節點為起點加入開啟列表
4.把起點放入關閉列表
5.計算每乙個開啟列表裡結點的f、g、h
6.找尋開啟列表裡f最低的乙個格仔。把當前格仔設為這個格仔。
7.將當前格仔周圍所有可到達的格仔加入開啟列表
8.如果遇到已經加入過的格仔,需計算從當前格仔到這個加入過的格仔的新g。如果g比之前要低,就把這個加入過的格仔的父節點設為當前格仔。然後賦新的f、g、h。
9.如果是沒加入過的,直接把父節點設為當前格仔
10.把當前格仔放到關閉列表。然後再開啟列表裡尋找f最低的格仔
7-10迴圈往復
最後一直迴圈之後會發現某個格仔的周圍格仔正好是終點。這時將終點的父節點設定為這個格仔,然後一直尋找父節點,就返回了一條尋路路徑了。
不必擔心什麼計算量特別大或者什麼這樣真的會找到終點之類的問題嗎。我第一次學的時候也是這麼想的,最後發現真的可以。我之前還擔心他的計算會影響遊戲執行效率,後來試著做乙個6* 6終點的遍歷a* 尋路,發現也是沒有卡頓的感覺就計算完了。是我太低估了cpu。
我當時寫這段**時參考了乙個大佬的a*演算法教程,但是我找不到這篇教程了。所以原大佬如果看到這篇教程感覺這個**結構很像你的,別糾結,可能就是你的(捂臉)。當然我在這裡吸收精華之後有修改了一些自己的理解。因此來學習的也不必擔心我並不是講乙個不屬於自己的**。
首先定義了乙個grid類。這個類是每乙個結點的類
public
class
grid
public
void
changeparent
(grid parent,
int g)
//修改父節點
}
接下來是主類的定義部分我只留了乙個地圖,和乙個單例。
public
static
astar instance;
//單例
public
map map;
//地圖
然後是演算法的核心函式
public list
findpath
(grid start,
grid end)
}return
null
;}
接下來是其他函式
public
point
findminfofgrid
(list openlist)
}return temp;
//最後返回temp
}
public list
getsurroundgrid
(grid grid)
if(grid.y >0)
if(grid.x >0)
if(grid.x < map.horizontal -1)
list list =
newlist
<
grid
>()
;//臨時變數儲存所有周圍的格仔
if(down !=
null
&& down.cantwalk ==
false)if
(up !=
null
&& up.cantwalk ==
false)if
(left !=
null
&& left.cantwalk ==
false)if
(right !=
null
&& right.cantwalk ==
false
)return list;
}
這裡需要注意的是,因為我這段**用到是時戰棋遊戲。多數戰棋遊戲都是沒有斜向移動的。因此我只選擇了上下左右四個方向。如果你需要8方向的尋路移動的時候,額外再加上另外四個方向即可。
public
void
gridfilter
(list src, list closelist)
}}
public
void
changeparentinsurround
(list surroundgrid,
grid gird, list openlist)
}else
//如果開放列表裡面沒有
}}
至於計算g和f這兩個函式,不同的思路有不同的演算法。我就不詳細說了,也當作乙個個人思考。我這裡提供乙個思路,計算g就是計算兩個點之間方向。斜向計算14,縱橫計算10,再加上父節點的g值。f的計算可以算出終點和當前節點的距離,再加上g即可。
最後return的path函式我也不寫在這裡了。思路就是從終點一路尋找父節點尋回起點,然後把這些節點都記錄到乙個list裡傳回去。
最後呼叫的時候只需要astar.instance.findpath(start,end);即可。返回值就是乙個list < grid >(沒有空格這個我不加空格打不出來……)型別的路徑。
我就拿我自己的demo做例子。為了保證小於5m的只能壓縮質量了……
當然這種遊戲其實也不一定非要做a*。這些短距離尋路用一些深度廣度優先演算法也可以實現。只不過我就是為了a*演算法才做的這個遊戲23333.
演算法之跳躍遊戲
給定乙個非負整數陣列,你最初位於陣列的第乙個位置。陣列中的每個元素代表你在該位置可以跳躍的最大長度。你的目標是使用最少的跳躍次數到達陣列的最後乙個位置。示例 輸入 2,3,1,1,4 輸出 2 解釋 跳到最後乙個位置的最小跳躍數是 2。從下標為 0 跳到下標為 1 的位置,跳 1 步,然後跳 3 步...
演算法之跳躍遊戲
給定乙個非負整數陣列,你最初位於陣列的第乙個位置。陣列中的每個元素代表你在該位置可以跳躍的最大長度。判斷你是否能夠到達最後乙個位置。示例 1 輸入 2,3,1,1,4 輸出 true 解釋 從位置 0 到 1 跳 1 步,然後跳 3 步到達最後乙個位置。示例 2 輸入 3,2,1,0,4 輸出 fa...
遊戲開發之隨機概率的選擇演算法
實現 超簡單,具體實現gihiwxxwyt方法如下 gihiwxxwyt 有時候當我們的遊戲人物遇敵時,我們需我怪物隨機根據概率選擇處理方式,如下 1 50 的機會友好的問候 2 25 的機率走開 3 20 的機會立即攻擊 4 5 的機會提供金錢作為禮物 下面的這個演算法就是跟據概率陣列,返回選擇的...