移動平臺上100個人複雜障礙物尋路的思考和實現(理論篇)

【轉載】移動平臺上100個人複雜障礙物尋路的思考和實現(理論篇)

ps:以下數據均以紅米1手機爲例。 
  
        去年我做了一個項目,當時就爲了十個人尋路的良好體驗做了多方嘗試,並最終通過改寫A*算法,而且寫了一篇文章:http://blog.csdn.net/yxriyin/article/details/40902063 
當時能夠做到良好的20人以下的多人尋路,在紅米上每一幀也只消耗5ms不到的時間。 
        但最近的一個項目是100人左右的在複雜的障礙物之間進行尋路,以前的方法在紅米手機上每一幀消耗超過100ms,這是無法接受的。 
        如果條件允許,我倒是想使用自帶的尋路系統,自帶的尋路用的是navmesh,本質上是節點尋路,效率比A*高1-2個數量級,但遺憾的是,遊戲的邏輯讓我必須使用A*才能實現對應的功能。 
         
A*是以單元格子作爲基本單位的,格子數越多,需要尋路時間越長,我們可以這麼來衡量一次A*尋路的性能,此次尋路總共找了200個格子。以coc爲例, 
他們的格子大概是100*100,也就是10000個。這個概念可以用紅米測試的時間來說明:假設一次尋路總共找了10000個格子,那麼標準A*的耗時 
是200ms左右(這裏是我自己拿了一個A*插件在手機上的測試),而一個流暢的遊戲一幀不會超過30ms。 
       爲了達到流暢的水平,那麼每一幀只能尋找500個格子左右,大概耗時10ms,(100個人同時渲染已經佔領了絕大多數的幀時間,即使實現這個目標,紅米上也只能在20幀左右徘徊,不過渲染的優化還有其他手段,我們這裏只考慮尋路優化先)。 
       
第一個有效手段是排隊,我們認爲尋路的ai有一秒延遲是合理的。也就是說,一個單位在原地思考1s以內不動,是一個合理行爲。那麼假設我們維持在24幀, 
那麼就是1s內可以有24個單位排隊尋路,每一個人能夠嘗試找500個格子。但是我們的目標是100個人,也就是說應該是每一幀要有5個人尋路,每一個人 
找100個格子就能找到終點,這樣就能實現目標。然而可想而知,在10000個格子內,用A*算法,想要用100個格子就找到目標,除非是毫無障礙物的情 
況。 
      於是我不得不開始思考爲何需要消耗如此的點才能找到終點,再看了大量的文章和以下圖片後: 

      




我得出一個結論,就是A*在有障礙物的情況下,實在是搜索了太多的無效的點了。而嘗試了大半個月的方案後,有一天我突然靈光一閃:我們走迷宮的時 
候,有一個策略,就是貼着牆壁一直往右走,那麼假設我修改A*找格子的策略,不再以啓發函數主導,而是饒牆主導,啓發函數輔助的形式,是不是是就會得出一 
個一直往前衝,碰到牆後繞,繞過去之後繼續往前衝到達終點。假設這個能成立,那麼尋找的格子將大大減少,因爲你和牆壁之間的那一大片都不用找了。 
當然即使行得通,找到的路徑也是很奇怪的,會饒牆而行,不過我以前寫過幾個路徑平滑算法,剛好有一個可以完美處理饒牆而行,讓路徑變成最短路徑。 
然後我就馬上開始執行這個方案,一邊執行一邊想,爲啥這麼牛逼的idea以前沒人想到過呢?然後我就發現了問題所在,這麼做會出現一個無法解決的情況,就是死衚衕, 
假設你剛好走進了一個只有一個格子長度的死衚衕,那麼你就再也找不到路了。因爲節點已經被Close了(寫過A*的人應該知道這個意思)。不過生活就是這樣,在我們的項目中是完全可以保證不存在這樣的死衚衕的。所以我就安安心心去做了。 
最終成功實現。策略如下: 
1.計算周圍的四個格子,哪一個離終點最近,得到這個格子後,Open之,下面稱它爲格子A。 
2.判斷格子A是否貼牆,如果貼牆,那麼就要Open周圍的三個格子,進入步驟3。如果不貼牆,就直接跳過周圍的其他三個格子,貪婪的跳轉到步驟1. 
3。進入A*正常的流程,用啓發函數判斷權值等等(具體參考標準A*) 


其實很簡單,只是通過1和2將大部分格子給pass了,這樣的問題也描述了,就是可能找不到路,但是對於我的項目來說,是不會出現的。 
這裏有幾個重點說明: 
1.在這種策略下4格子搜索比8格子更快。 
2.用這種方式找的路徑,必須用路徑平滑算法處理,不然走路會很奇怪。 
3.判斷是否貼牆要注意如果你在牆角邊緣,你要判斷周圍8個格子,不然可能會忽略一些正確的路徑導致繞遠路。 
4.如果障礙物不多,這種算法效率會比A*低。 
5.如果障礙物太多,這種算法和A*效率一樣,但是由於要路徑平滑,所以也會比A*效率低。 
哈哈,看了4和5,是不是坑爹啊。但大部分情況都是這樣做效率高。最終測試性能結果和預期還是有點差距,一幀300個格子,10ms,可以讓50個人一秒內從地圖的一端經過複雜的障礙物尋路到另一端,這是目前的極限。100個人的話,會有2s的思考延遲。 


目前項目就用這個來處理,體驗也馬馬虎虎,當然如果後續有bug我會再說明。雖然我也很喜歡分享源代碼,但這個代碼是整合在我們整個項目中,分離出來比較麻煩,所以目前只打算做理論分享,實戰分享希望以後有空專門寫一個demo。       

更多細節參考以前的文章:http://blog.csdn.net/yxriyin/article/category/6057606 

最近發現一個bug,那就是四個字走斜線會比直線要遠,這樣在我們的遊戲中,斜着的牆壁的消耗就比橫着的牆壁要大,導致尋路的結果老是斜着的牆壁優先,目前的方案是調整斜着牆壁的消耗值,讓他的權重降低,大致和直的相等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章