寻路算法1:A星寻路和navmesh寻路的技巧和优化
一:客戶端navmesh 和 服務端A*算法的轉換
這是以前項目用到的C#版的navmesh。從那個開源的C++版本navmesh改過來的
timotimosky/-Navmesh_Csharp
public void Sync(){//判斷是否處于尋路//if (LocusPoints.Count < 2){return;}//獲取下一個路徑點//Vector3 nextPosition = LocusPoints[0];Vector3 nextPosition2 = LocusPoints[1];//如果兩者大于一個格子,則插點//if (Vector3.Distance(nextPosition ,nextPosition2) > 0.5f){// * 方向//Vector3 directionVector3 = nextPosition2 - nextPosition;//預測下一個路徑點//Vector3 targetVector3 = directionVector3.normalized * 0.5f + nextPosition;if (targetVector3.x == 0 && targetVector3.z == 0) LocusPoints.Insert(1, targetVector3);}//TODo:和服務端同步一次位置////記錄上一個出發點,用于平滑路徑NewLocusPointRotation = SelectedLightProjector.transform.rotation;NewLocusPointPosition = SelectedLightProjector.transform.position;NewLocusPointStartTime = Time.fixedTime;mFollowWayPointsState.begin = true;}上面這段代碼,實際上就是每隔一定距離,就對Navmesh的路徑點進行插點,然后轉換為A星的格子位置。
二: A*算法的障礙碰撞
一般的A*算法代碼是把所有障礙當做一個格子。但某些時候有的物體大,有的物體小,碰撞范圍大小不一,那么需要動態傳遞參數
public bool CanReach(int x, int y) {///加入自身碰撞器后,需要判斷該點周圍 全無阻擋///for (int i=-selfBlock; i<=selfBlock; i++){for(int j=-selfBlock; j<=selfBlock; j++){if(MazeArray[x+i, y+j]<=0)return false;}}return true; }三:客戶端navmesh 和 A*算法的混用
因為項目的某些需求,使用單一的尋路方式,性能或者效果可能不好,那么可以混用幾種尋路方式。
兩種思路:
1.使用navmesh檢測靜態障礙,在這段代碼內部,再使用A*專門檢測動態障礙,調整navmesh的路徑。
2. 因為一些特殊需求,比如尋找NPC,使用navmesh檢測長范圍的尋路,在路徑的最后一段,使用A*檢測小范圍的障礙。
三:優化特定需求的A*算法
1.平滑A型算法的路徑
2.A*算法的格子靜態合并、優化A*算法的尋路性能
3. A*尋路向navmesh尋路的演進(navmes設計思路)
4.A*工具化
平滑A型算法的路徑
如圖1
我使用一種簡單的情況來說明,假設在地圖上尋路,如果從A點(灰色格子)尋到X點,那么使用普通A*算法尋路出來可能是這樣的
現在格子很不規則,我們想使路徑平滑,一般采用的策略是數個角度差異較大的路徑點之間,通過插點,替換路徑點,來實現。
我們現在采用另一種方式,
步驟1:
先對所有路徑點循環,如果兩個頂點,比如B-C矢量為垂直/平行 于地圖起始的 1-2頂點矢量,則 B-C頂點合并。
圖片2
我們可以看到,B-C-D-E與1-2頂點矢量平行,所以可以合并為BCDE點,F-G點與1-2頂點矢量垂直,所以F-G合并為FG點。(這里要注意, ABCDE---F矢量 與ABCDE本身矢量不同,不能再合并。所以中斷合并,從F點重新判斷,F和G點合并了)
步驟2:
最后我們原本的路徑點為: A-B-C-D.....-Y-X 一共10多個路徑點, 合并后,為 ABCDE--FG---HOI---JKM---NOX 一共五個點,路徑點大大減少
雖說對行走效率有一定優化,但人物按這個路徑點來行走,路線并沒有平滑。
步驟3:
其實我們可以將ABCDE當做一個大方塊,同樣的道理,所以這個尋路路徑變成了5個大方塊的尋路。
我們先將我們理想中的平滑路徑畫出來,現在看圖3
其實我們可以看到黃色路徑比較接近我們的想法。
但是黃色點,并不在我們的尋路結果內,所以我們要將黃色點納入我們的五個大方塊里去,
于是成了圖3的樣子,我們用綠色格子將黃色部分納入。
我們可以發現,其實現在變成了3個更大的方塊(紅色圈內)的尋路。我們把三個圈命名為 a b c.
5個大方塊朝3個更大方塊的演進,實際上是將拐角磨平(比如FGH的拐角被納入第二個紅圈內了)
實際上,就是判斷,當矢量FG與下一個點H有拐角時,判斷FG與H點的斜上方所有點是否可走,如果可走,則FG與H點可以合并成一個大方塊。
則 我們計算把 a- b - c 三個長方形的公共點(如果有公共邊,則公共邊的中點為公共點,如果只有相鄰角,則角作為這個圈到下一個圈的路徑點)作為必經點
則新的路徑形成, a長方形終點 - b長方形起 ---- c起點---x(x屬于C內部)
其實,到這里為止,這已經是一個簡化版的動態navmesh尋路算法。
navmesh也是多個區域塊行走。但不是使用矩形,而是使用三角形。
navmesh的三角形有一個嚴格規定,每個三角形的三個頂點所形成的圓形內不能包含其他三角形的頂點(delaunay算法?http://baike.baidu.com/link?url=m1uEA28GLx-NjrhXlozvcJ03-hmXTIUMo03D99bf-4_nEF7Rsrd2h6F-Pq9HpVatIWlQNSOWfMmZs6jlLP5KkK)
這樣做有個好處,其實navmesh的實際路徑不是從一個三角型尋路到另一個三角型,本質上是從一個圓到另一個圓,圓與圓之間有且只有一個交點,這樣保證了在很多情況下路徑比矩形尋路更短更平滑(長方形無法形成一個不包含其他長方形頂點的圓,所以相鄰矩形的內部路徑之間的夾角更大)
所以如果我們把矩形拆分成三角型,更接近U3D自帶的navmesh算法。
U3D的navmesh烘焙,是讀取網格數據后,使用該delaunay算法直接生成三角型,剩余步驟相同。
A*算法的格子靜態合并
剛才是先進行尋路,再進行格子的動態合并,對效率影響很大,其實將上面的合并步驟拿出來,使用地圖格子之前,先對所有地圖格子進行合并,然后生成一個很多矩形的新地圖格子。
即可達成格子的靜態合并。 然后使用中,直接使用這些格子進行A*尋路,能大大提高效率。但是,靜態合并后,如果不單獨處理動態障礙,無法識別動態障礙,這也是NAVmesh的通病。
A*地圖格子生成工具化
簡單說,
1.將所有地圖上可行走區域設置為(walk層級),障礙的層級設置為nowalk
2.用攝像機對地圖循環掃描(每次掃描x坐標+1,下一次y+1,輪流加坐標),掃描到walk,xml上寫0,掃描到nowalk,xml上寫1.
(當然,還可以有減速行走的沼澤,則xml可以寫2,以此類推,可以無限多地形。順便提一句,草叢視野的實現,比如可以將草叢部分設置為3,人物中心點處于草叢格子,則判斷人物進入草叢。)
總結
以上是生活随笔為你收集整理的寻路算法1:A星寻路和navmesh寻路的技巧和优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 5G打通消费新空间,5G应用正在不断落地
- 下一篇: 计算机网口百兆改千兆,如何将电脑百兆网卡