行爲樹(Behavior Tree)實踐(2)– 進一步的討論

上次提到了一些行爲樹的基本概念,包括行爲節點,控制節點(選擇,序列,並行),此次來更多,更深刻的討論行爲樹的一些東西,若是對行爲樹不是很瞭解,請參看這裏程序員

一. 關於選擇節點的討論 數據結構

咱們說過選擇節點的定義是經過判斷子節點的前提條件來選擇一個節點執行,這就牽涉到判斷順序的問題,是自左向右,仍是隨機選擇,或者其餘的一些規則等等,這樣就延伸出各類各樣的選擇節點。 dom

 

 

  • 帶優先級的選擇節點(Priority Selector):這種選擇節點每次都是自左向右依次選擇,當發現找到一個可執行的子節點後就中止搜索後續子節點。這樣的選擇方式,就存在一個優先級的問題,也就是說最左邊的節點優先級最高,由於它是被最早判斷的。對於這種選擇節點來講,它的子節點的前提設定,必須是「從窄到寬」的方式,不然後續節點都會發生「餓死」的狀況,也就是說永遠不會被執行到,爲了更清楚的說明,看下面第一張圖,這三個子節點在一個帶優先級的選擇節點下,它們的前提會被依次判斷,能夠看到這個三個子節點的前提從左向右,一個比一個更嚴格,若是咱們如今a爲9,按照下圖的定義會執行第一個子節點,若是a爲7,則會執行第二個子節點,若是a=11,則會執行第三個子節點。下面的第二張圖演示了一種節點「餓死」(Starvation)的狀況,咱們看到第一個子節點的前提,比第二個子節點更寬泛,只要a<10,那自左向右判斷的話,永遠會進第一個節點,因此,若是要用到帶優先級的選擇節點,則必須檢查每個子節點的前提,以防止節點餓死的狀況.

 

bv-tree-priority-selector-1

bv-tree-priority-selector-2

  • 不帶優先級的選擇節點(Non-priority Selector):這種選擇節點的選擇順序是從上一個執行過的子節點開始選擇,若是前提知足,則繼續執行此節點,若是條件不知足,則今後節點開始,依次判斷每個子節點的前提,當找到一個知足條件的子節點後,則執行該節點。這種方式,是基於一種稱之爲「持續性」的假設,由於在遊戲中,一個行爲通常不會在一幀裏結束,而是會持續一段時間,因此有時爲了優化的目的,咱們能夠優先判斷上一個執行的節點,當其條件不知足時,再尋找下一個可執行的節點。這種尋找方式不存在哪一個節點優先判斷的問題,因此對於前提的設置的要求,就是要保證「互斥」(Exclusion)。若是咱們用上面第一張圖來講明,若是咱們把控制節點換成不帶優先級的選擇節點,能夠看到,當a=3時,第二個子節點會被執行,下一次當a變成9時,因爲不是從頭依次判斷前提的,因此,咱們仍是會選擇第二個節點,而不是咱們可能指望的第一個節點。正確的作法見下圖,注意每個子節點的前提是「互斥的」。因此對於不帶優先級的選擇節點,它子節點的排列順序就不是那麼重要了,能夠任意排列。

bv-tree-nonpriority-selector-1

  • 帶權值的選擇節點(Weighted Selector):對於這種選擇節點,咱們會預先爲每個分支標註一個「權值」(Weight Value),而後當咱們選擇的時候,採用隨機選擇的方式來選,隨機時會參考權值,而且保證已經被測試過的節點的不會再被測試,直到有一個節點的前提被知足,或者測試完全部的節點。帶權值的選擇節點對於子節點前提因爲隨機的存在,因此子節點的前提能夠任意,而不會發生「餓死」的狀況,通常來講,咱們一般會把因此子節點的前提設爲相同,以更好的表現出權值帶來的機率上的效果。當全部子節點的權值同樣時,這種選擇節點就成爲了隨機選擇節點(Random Selector)帶權值的選擇節點對於須要豐富AI行爲的地方,很是適用,好比養成類遊戲中,小狗表示開心的時候,可能會有各類各樣的表現,咱們就能夠用這種選擇節點,添加各類子節點行爲來實現。

bv-tree-weighted-selector-1

這些就是經常使用的選擇節點類型,咱們能夠根據須要,定義更多的選擇節點的選擇行爲,其實咱們能夠看到,不一樣的選擇行爲對於子節點前提的要求會有略微的不一樣,這是在咱們搭建行爲樹的時候須要注意的地方。 編輯器

二. 關於並行節點結束條件的討論 工具

咱們每一個節點都會有一個運行狀態,來表示當前行爲是否結束。對於控制節點來講,它的運行狀態就是其子節點的運行狀態,選擇節點和序列節點比較好處理,由於對於這兩種控制節點來講,每時刻,只會有一個子節點在運行,只要返回在運行的這個子節點的狀態便可。但對於並行節點來講,它同時刻會有多個子節點運行,那咱們如何來處理並行節點的運行狀態問題呢?通常有兩種: 測試

  • 與:只有全部的子節點都運行結束,才返回結束。
  • 或:只要有一個子節點運行結束,就返回結束。

爲何要須要有節點的運行狀態呢? 優化

  • 序列控制節點中,須要用運行狀態來控制序列的執行
  • 外部世界須要瞭解行爲的運行狀態,來決定是否要更新決策(若是行爲樹在決策層)/請求(若是行爲樹在行爲層),關於AI分層,請參考這裏

對於第二點,能夠舉個例子,好比咱們有一個行爲是「走到A點」,假設這個行爲是不可被打斷的,那當咱們在走向A點的過程當中,行爲樹的運行狀態就是「正在執行」,當到達A點時,行爲樹就返回「已完成」,這樣,對外部來講,當咱們看到行爲樹是「正在執行」的時候,咱們就不須要作任何新的行爲(爲了優化,或者爲了行爲抖動等等),當看到「已完成」的時候,咱們就能夠作新的決策或者行爲了。這樣一個運行狀態還有助於咱們檢測行爲樹的狀態,幫助調試。 google

三.關於具體實現的討論 spa

行爲樹的實現能夠有多種多樣,我這邊提出一些建議,通常來講,行爲樹每一個節點須要有進入(Enter),離開(Exit),運行(Execute)等部分,須要有行爲節點(ActionNode),控制節點(ControlNode),前提(Precondition)等基類,而後,還須要定義行爲樹的輸入(InputParam)和輸出(OutputParam),通常來講,咱們但願行爲樹是一個黑盒,也就是說,它僅依賴於預約義的輸入。輸入能夠是黑板(Blackboard),工做池(Working Memory)等等數據結構,輸出能夠是請求(Request),或者其餘自定義的數據結構,以下圖: 插件

bv-tree-arch

代碼的話,就不寫了,由於blog沒有代碼插件,寫代碼效果不是很好,之後我會在TsiU裏面發佈一個行爲樹的庫的版本。

四.關於繪製和調試的討論

看到行爲樹的定義後,做爲程序員的直覺,咱們很天然的就會想到,這好像應該能作一個工具來輔助行爲樹的建立和調試,咱們能夠把預約義好的前提和節點,在一個可視化的編輯器裏搭建成行爲樹,而後再導出成數據給遊戲用。對於調試來講,咱們可讓工具和遊戲通訊,而後實時的檢測行爲樹的運行情況,好比當前在哪一個分支中等等。因爲行爲樹的邏輯是可見的,而且是靜態的,因此咱們看其選擇的路徑,咱們就能夠知道AI爲何會做出這樣的決策了。當我剛接觸到行爲樹的時候,就在想作這樣一個編輯器,但迫於項目壓力,一直沒有時間作(工做量仍是挺大的),有興趣,有時間的朋友,能夠考慮作一個。順便說一句,我如今對於行爲樹的搭建都是在代碼中完成的,雖然沒有數據驅動那麼「先進」,但經過宏定義,排版等方式,仍是能很是清晰的表示樹的總體結構。

關於行爲樹,我想這個系列就到這裏了。在使用行爲樹的過程當中,可能還會碰到這樣和那樣的問題,包括我本身在實踐中的一些經驗,我想就先不包括在這個系列裏了,之後再單獨拿出來聊,這個系列做爲行爲樹的入門,但願對你們有所幫助,歡迎指教和討論。

相關文章
相關標籤/搜索