unity行為樹簡介


目前在Unity3D游戲中一般復雜的AI都可以看到行為樹的身影,簡單的AI使用狀態機來實現就可以了,所以這里我也是簡單的學習下,為以后用到做准備。

 

行為樹的概念出現已經很多年了,總的來說,就是使用各種經典的控制節點+行為節點進行組合,從而實現復雜的AI。

       Behavior Designer插件里,主要有四種概念節點,都稱之為Task。包括:

       (1) Composites  組合節點,包括經典的:Sequence,Selector,Parallel

       (2) Decorator 裝飾節點,顧名思義,就是為僅有的一個子節點額外添加一些功能,比如讓子task一直運行直到其返回某個運行狀態值,或者將task的返回值取反等等

       (3) Actions     行為節點,行為節點是真正做事的節點,其為葉節點。Behavior Designer插件中自帶了不少Action節點,如果不夠用,也可以編寫自己的Action。一般來說都要編寫自己的Action,除非用戶是一個不懂腳本的美術或者策划,只想簡單地控制一些物件的屬性。

       (4) Conditinals 條件節點 ,用於判斷某條件是否成立。目前看來,是Behavior Designer為了貫徹職責單一的原則,將判斷專門作為一個節點獨立處理,比如判斷某目標是否在視野內,其實在攻擊的Action里面也可以寫,但是這樣Action就不單一了,不利於視野判斷處理的復用。一般條件節點出現在Sequence控制節點中,其后緊跟條件成立后的Action節點。

 

 

行為樹(Behavior Tree)具有如下的特性:
 
  它的4大類型的節點:1. Composite 2.Decorator 3.Condition 4. Action Node
  任何Node被執行后,必須向其Parent Node報告執行結果:成功 / 失敗。
  這簡單的成功 / 失敗匯報原則被很巧妙地用於控制整棵樹的決策方向。

一: Composite Node(組合節點)

 

Composite 節點按照復合性質還可以細分為3種:

  * Selector Node

    當執行本類型Node時,它將從begin到end迭代執行自己的Child Node:
    如遇到一個Child Node執行后返回True,那停止迭代,
    本Node向自己的Parent Node也返回True;否則所有Child Node都返回False,
    那本Node向自己的Parent Node返回False。

  * Sequence Node

    當執行本類型Node時,它將從begin到end迭代執行自己的Child Node:
    如遇到一個Child Node執行后返回False,那停止迭代,
    本Node向自己的Parent Node也返回False;否則所有Child Node都返回True,
    那本Node向自己的Parent Node返回True。

  * Parallel Node

    並發執行它的所有Child Node。
    而向Parent Node返回的值和Parallel Node所采取的具體策略相關:
    Parallel Selector Node: 一False則返回False,全True才返回True。
    Parallel Sequence Node: 一True則返回True,全False才返回False。
    Parallel Hybird Node: 指定數量的Child Node返回True或False后才決定結果。

 

    Parallel Node提供了並發,提高性能。
    不需要像Selector/Sequence那樣預判哪個Child Node應擺前,哪個應擺后,
    常見情況是:
    (1)用於並行多棵Action子樹。
    (2)在Parallel Node下掛一棵子樹,並掛上多個Condition Node,
       以提供實時性和性能。
    Parallel Node增加性能和方便性的同時,也增加實現和維護復雜度。


  PS:上面的Selector/Sequence准確來說是Liner Selector/Liner Sequence。
  AI術語中稱為strictly-order:按既定先后順序迭代。
 
  Selector和Sequence可以進一步提供非線性迭代的加權隨機變種。
  Weight Random Selector提供每次執行不同的First True Child Node的可能。
  Weight Random Sequence則提供每次不同的迭代順序。
  AI術語中稱為partial-order,能使AI避免總出現可預期的結果。

  二:Decorator Node(裝飾節點)

裝飾節點的功能正如它的字面意思:它將它的Child Node(孩子節點)執行后返回的結果值做額外處理(裝飾)后,再返回給它的Parent Node(父節點)。很有些AOP的味道。
 
  比如Decorator Not/Decorator FailUtil/Decorator Counter/Decorator Time…
  更geek的有Decorator Log/Decorator Ani/Decorator Nothing…

三:Condition Node(條件節點)

條件節點很直白,它僅當滿足Condition節點的條件時返回True。

四:Action Node(行為節點)

行為節點是完成具體的一次(或一個step)的行為,視需求返回值。
而當行為需要分step/Node間進行時,可引入Blackboard進行簡單數據交互。

 

注意:

  整棵行為樹中,只有Condition Node和Action Node才能成為Leaf Node(葉子節點),而也只有Leaf Node才是需要特別定制的Node;Composite Node和Decorator Node均用於控制行為樹中的決策走向。(所以有些資料中也統稱Condition Node和ActionNode為Behavior Node,而Composite Node和Decorator Node為Decider Node。)
更強大的是可以加入Stimulus和Impulse,通過Precondition來判斷masks開關。
 
  通過上述的各種Nodes幾乎可以實現所有的決策控制:if, while, and, or, not, counter, time, random, weight random, util…

 

總的來說

行為樹具有如下幾種優點,確實是實現AI框架的利器,甚至是一種通用的可維護的復雜流程管理利器:

 

> 靜態性

  越復雜的功能越需要簡單的基礎,否則最后連自己都玩不過來。
  靜態是使用行為樹需要非常着重的一個要點:即使系統需要某些"動態"性。
 
  其實諸如Stimulus這類動態安插的Node看似強大,但卻破壞了本來易於理解的靜態性,弊大於利。
  Halo3相對於Halo2對BT AI的一個改進就是去除Stimulus的動態性。取而代之的做法是使用Behavior Masks,Encounter Attitude,Inhibitions。
  原則就是保持全部Node靜態,只是根據事件和環境來檢查是否啟用Node。
 
  靜態性直接帶來的好處就是整棵樹的規划無需再運行時動態調整,為很多優化和預編輯都帶來方便。

> 直觀性

  行為樹可以方便地把復雜的AI知識條目組織得非常直觀。默認的Composite Node的從begin往end的Child Node迭代方式就像是處理一個
  預設優先策略隊列,也非常符合人類的正常思考模式:先最優再次優。
 
  行為樹編輯器對優秀的程序員來說也是唾手可得。

 

 

> 復用性

  各種Node,包括Leaf Node,可復用性都極高。實現NPC AI的個性區別甚至可以通過在一棵共用的行為樹上不同的位置來安插Impulse來達到目的。當然,當NPC需要一個完全不同的大腦,比如70級大BOSS,與其絞盡腦汁在一棵公用BT安插Impulse,不如重頭設計一棵專屬BT。

 

 

> 擴展性

  雖然上述Node之間的組合和搭配使用幾乎覆蓋所有AI需求。
  但也可以容易地為項目量身定做新的Composite Node或Decorator Node。
  還可以積累一個項目相關的Node Lib,長遠來說非常有價值。

 

Conditional Abort 機制

Conditional Abort允許你的行為樹動態的響應變化,而不是被各種中斷/執行的任務搞的你的樹亂七八糟。這一特性跟虛幻4中的Abort很相似,很多行為樹,都會在每幀重新評估整顆樹。ConditionAbort是一個優化算法,讓你不必每次返回整顆樹。

如下圖:


非Abort模式:當這棵樹運行的時候,Conditional會返回Success,然后Sequence會執行下一個子節點,Wait。Wait節點會等待10秒鍾。

當Wait節點正在運行的時候,假定Condition發生了變化返回了Failure,Wait也不會停止,會一直等待10秒結束。
Abort模式:如果Sequence的Abort被激活,那么Conditional節點會發起一個abort,當Wait處於Running狀態時,如果Conditional狀態發生變化,會中斷wait的執行。Condtional Abort可以在任何的 組合節點獲取。

 

 

我的解讀:
Conditional Abort,只能由條件節點發起,這是因為只有條件節點會判斷當前條件,並且在條件變化時,發起中斷信息,終止一個正在Runing的行為節點。
【1】存在 Conditonal 節點
【2】存在Action節點,並且這個節點不是立刻返回,而是需要返回 Runing的。
【3】Action的節點的運行,依賴於Conditional節點的返回值。
【4】當條件變化時,Conditional節點返回一個abort請求
【5】那么誰來捕獲這個Conditional變化呢?可以Conditonal的父節點,或者是Conditional父節點的兄弟節點。(裝飾節點)
【6】當捕獲到Abort之后干什么呢?中斷當前running的節點,從新評估整顆樹。
【7】好處是什么呢?動態改變行為樹的執行順序,不必非等到Wait節點10秒鍾執行完畢,才重新檢索整棵樹,才檢測Conditional的返回值。

然后我們來看看有幾種中斷類型:


None:這是默認的行為,不會中斷,一旦Wait開始運行,他就必須傻了吧唧的運行完10秒鍾,哪怕期間條件變化了。

比如,你發現周圍沒人,決定休息10秒,然后敵人來了,你還在休息,敵人把毫無反抗的你給啪啪啪了。

Self:這是自身條件中斷,Conditional Abort只能中斷同級(擁有相同的父節點(裝飾節點)的Action節點。
比如,你剛決定休息,休息了不到1秒,敵人來了。你決定中斷休息這個行為,開始做點什么,而不是坐以待斃。

Lower Priority 代表中斷低優先級節點,
行為樹,越靠左邊,優先級越高,越靠右邊,優先級越低。
Lower Priority意義在於當一個高優先級裝飾節點A下的Condiontal子節點發生變化時,他只能中斷一個低優先級的Action節點(跟A節點同級,但在A節點右側的Action節點)。
假設一個AI包含有 回避 攻擊 休息三個模塊(大分支)
1-你正在攻擊,這時候有人朝你開槍了,回避分支發現了這個問題,要求你先保命,於是中斷了你的攻擊分支。
2-而如果你正在回避,這時候回避分支自身發生了變化——攻擊你的人死了——他不會中斷你當前的回避行為。

 

Both綜合Self和Lower Priority的中斷

 
1-你正在攻擊,這時候有人朝你開槍了,回避模塊發現了這個問題,要求你先保命,於是中斷了你的攻擊行為。
2-而如果你正在回避,這時候回避模塊發生了變化——攻擊你的人死了——那你還回避干屁,於是回避行為也終止了。

最后在總結一下干貨:
Conditional Abort由 Conditonal節點發起,由Composite節點截獲,並決定是否要abort。
如果決定Abort,會從發起Abort的節點開始,重新評估。
Self 是指:同根的節點
Lower 則是指:和發起Abort的父節點(Composite)相對而言,低優先級的節點。

如上圖,Sequence節點選擇了Lower Priority ,那么當子節點Action在Running狀態時,子節點的Conditional是中斷不了的,但是當右側的Sequence下的Action在Running狀態時,如果左側的Conditional條件發生了變化,是可以另樹的邏輯重新執行,執行自己的Action而中斷右側Sequence(低優先級裝飾節點)下的Action。

並且此時左側Sequence下的Conditional擁有一個帶有循環圖案的對號。


Conditional Abort條件是可以嵌套的,如下圖:


當能看見,和能聽見,任何一個節點返回True的時候,將執行Action。
一個恰當的例子,比如,一個守衛,當他看見敵人,或者聽到異常聲音的時候,他會開始巡邏一圈。

Selector被設置成LowerPriority,這樣,當“聽”和“看”條件變化時,可以中斷巡邏Action。
當敵人不見蹤影,也沒了聲音,守衛開始懷疑自己是不是幻聽幻視了,中斷巡邏。

Sequence被設置成LowerPriority,是希望Selector被重新評估時,他可以中斷比Sequence優先級低的其他節點上正在運行的行為。
比如,現在CanSee和CanHear都是返回False的,因此巡邏Action是不執行的。
假設Sequence的兄弟節點,有個抽煙的動作正在Runing。這時候風吹草動了,即CanSee和CanHear條件發生變化時,抽煙Action會中斷,而巡邏Action會被執行。

但是如果Sequence是Self而不是LowerPriority的話。即使風吹草動了,他也不會終止抽煙而開始巡邏的。
這里LowerPriority改成Both也是可以的,雖然這並沒有多大意義。

而Selector,如果他沒有被設置為Both或者LowerPriority,那么這兩個條件發生變化,這個Sequence根本不會被重新執行,也無法執行中斷巡邏Action,那么肯定也無法中斷Sequence的低優先級節點。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM