很久沒寫博客了,最近在項目剛部署到測試服,需要進行壓測,老大相當專業的用了行為樹來組織壓測機器人的代碼,這段時間陸陸續續在網上看了不少關於行為樹的文章,其中有一篇我覺得寫得非常到位,它原文是英文,鏈接將在下面給出,在這之前看了很多國內的文章都是寫的糊里糊塗,后來看過這篇文章后,總算是對行為樹的概念入了門。在這里我會把它的大致意思給翻譯出來(注:由於原文有不少啰嗦的地方,所以沒有逐句的翻譯),以它的行為結構為標准,並會在譯文中穿插一些自己的理解來談談我眼中的行為樹,如果有什么不對的地方,歡迎大家指點。
一、介紹
盡管網上已經有大量關於行為樹的教程,但是在開發游戲中的 AI 時還是遇到了不少關於行為樹的問題。很多教程不關注於代碼的實現,而是給出一些非常籠統的節點圖,就像這樣:

這些節點看起來非常的抽象,這對於我了解行為樹的運作機制沒有什么幫助,因為這樣完全建立不起一個完整行為樹的概念。自然也就更不知道如何在代碼中進行行為樹的實現。
行為樹的概念見名知意,它是一棵具有層級結構的樹,主要用來控制 AI 的決策,樹的末端(注:葉子節點)就是 AI 實際要去做的事情。連接樹枝的是各種類型的節點(注:這些節點將在后面講解),這些節點決定了如何一個 AI 如何從根節點走到某一個葉子節點,並執行相應的命令操作。
二、樹的遍歷
行為樹的一個特點就是它會一層一層的對節點依次進行檢查,而每一層都要花費一個 Tick 的時間,所以一棵樹要花費很多 Tick 的時間才能完成遍歷,這和一般代碼的實現有很大的區別。這是一個很沒有效率的方式,尤其是當樹變得很深的時候。我認為行為樹的實現最好要在一次 Tick 里面完成整個行為樹的邏輯判斷,也就是說最好能在一次 Tick 時間里完成整棵樹的遍歷。
三、工作流
行為樹由多種不同類型的節點組成,這些節點都會返回三種狀態中的一種作為節點的運行結果。三種狀態分別是:
- 成功 - Success
- 失敗 - Failure
- 運行中 - Running
前兩個,正如它們的名字一樣,是用來向它們的父節點通知運行的成功或失敗。第三種是指還在運行中,結果還未確定,會在下一個 Tick 的時候再去檢查這個節點的運行結果。這個功能非常重要,它可以讓一個節點持續運行一段時間來維持某些行為。比如一個 Walk 節點會在計算尋路和讓角色保持行走的過程中持續返回 Running 來讓 AI 保持這一狀態。如果尋路因為某些原因失敗,或是除了某些狀況讓行走的行為不得不中止,那么這個節點會返回 Failure 來告訴它的父節點;如果這個角色走到了指定的目的地,那么節點返回 Success 來表示這個行走的指令已經成功完成。這些狀態可以用來決定行為樹的走向,確保 AI 可以按照我們預期的方式來以某些順序去執行行為樹里的行為。
上面是節點的 3 種狀態。說完了節點的 3 種狀態,下面來說一說行為樹中節點的類型。行為樹節點類型分為下面 3 種:
- 組合節點 - Composite
- 修飾節點 - Decorator
- 葉子結點 - Leaf
這些節點類型可以說是行為樹最精華的內容,下面將分別對這 3 種類型的節點做說明。
1、組合節點
我們先來看看行為樹中最常見的組合節點。組合節點通常可以擁有一個或更多的子節點。這些子節點會按照一定的次序或是隨機地執行,並會根據執行的結果向父節點返回 Success 、Failure,或是在未執行完畢時返回 Running 這樣的結果值。
- 次序節點
- 選擇節點
- 並行節點
(1)次序節點
(2)選擇節點
(3)並行節點
我們每個節點都會有一個運行狀態,來表示當前行為是否結束。對於組合節點來說,它的運行狀態就是其子節點的運行狀態,選擇節點和次序節點比較好處理,因為對於這兩種控制節點來說,每時刻,只會有一個子節點在運行,只要返回在運行的這個子節點的狀態即可。但對於並行節點來說,它同時刻會有多個子節點運行。
2、修飾節點
- 逆變節點
- 成功節點
- 重復節點
(1)逆變節點
Inverter(逆變節點)可以將子節點的結果倒轉,比如子節點返回了 Failure,則這個修飾節點會向上返回 Success,就像上面的例子里所說的一樣。
(2)成功節點
成功節點不管它的子節點向其返回的結果為何,它總是向它的父節點返回 Success 的結果。這個往往用在當你知道一個子節點一定會返回 Failure 的結果,而它的父節點是次序節點,會因為子節點的 Failure 而終止,那么你可以強行讓這個子節點返回 Success,來避免這一情況的發生。我們並不需要一個專門的失敗節點,因為一個逆變節點加上成功節點就可以達到這一效果。
(3)重復節點
重復節點會在它的子節點返回結果后反復繼續執行它。重復節點常常被用在一棵樹的最頂部來確保樹的持續運行。另外重復節點也可以被設定重復執行的次數。
