Behavior Designer 中文版教程

Behavior Designer 概述

Behavior Designer 是一個行為樹插件!是為了讓設計師,程序員,美術人員方便使用的可視化編輯器!Behavior Designer 提供了強大的 API 可以讓你輕松的創建 tasks(任務),配合 uScript 和 PlayMaker 這樣的插件,可以不費吹灰之力就能夠創建出強大的 AI 系統,而無需寫一行代碼!
本指南將介紹所有 Behavior Designer 的功能特性!如果你還不了解什么是行為樹(behavior trees)請查看"行為樹的概述"!依賴于 Behavior Designer你完全可以不用關心什么是行為樹(behavior trees)但是,如果你了解了一些 behavior trees!這將有助于你使用 Behavior Designer,包括一些常用組件,例如:tasks(任務),action(行為),composite(復合),conditional(條件),decorator(修飾符)!
當你第一次打開 Behavior Designer ,會出現下面的窗口:
這里一共分為四大部分:下圖中的第一個部分是主要操作區,用來創建你的行為樹!第二部分是面板屬性區,這里可以編輯一個行為樹的特定屬性,添加新任務,創建新的變量,或者編輯 tasks(任務)的參數。第三部分是工具欄,你可以添加/刪除行為樹,鎖定當前行為樹,查看所有行為樹,等等!第四部分是調試工具欄。你可以啟動/停止,逐步調試,暫停,查看錯誤!

第一部分是主要的設計工作的區域!在這個區域,你可以創建新的 Task(任務)和設計這些 Task(任務)的行為樹。第一步要做的就是創建一個行為樹(behavior tree),通過右鍵選擇"Add Behavior Tree"可以創建新的 behavior tree 行為樹,或者通過上方的工具欄 Lock(鎖定)旁邊的加號添加一個新的行為樹!
一旦行為樹創建完畢,你就可以開始添加 tasks(任務)了。添加一個 task 可以通過右鍵點擊空白區,或者左側 2 區中的 Tasks 標簽來創建!一旦你的task 創建完畢,你將看到類似于下面圖中的效果!

這里的 Entry task 是默認添加的,作為根節點進行后續的 Task 添加!這里能看到添加的 Sequence(隊列)節點有錯誤,這是因為這個 Sequence必須有后續子節點,只要添加了子節點,這個錯誤就會消失了!現在我們已經有了第一個 Task,接下來讓我們多添加幾個:

你可以創建 Sequence(序列)節點,或者 selector(選擇器)節點,他們都是 task(任務)。通過多個這種節點的組合,你可以創建很深的層次結構!
如果在創建過程中你不小心搞錯了什么,那么選擇有錯誤的節點,delete 刪除掉即可!
Behavior Designer 的執行順序是從左到右的順序執行,并且是深度優先。上圖中的執行順序如下:
SequenceA,SelectorA、SequenceB ActionA、ActionB ActionC,SelectorB,ActionD ActionE

現在我們已經有了一個基礎的行為樹,讓我們改改參數看看!選擇 ActionC 節點然后查看左側的屬性面板,選到 Inspector 視圖!你可以再這里重命名這個 Task(任務)名稱,設置參數,或者輸入一個注釋在(Comment)。
在左側的界面中有出了 Inspector 還有其他三個標簽(Behavior,Tasks,Variables)Variables 面板允許你創建共享變量,并改變變量值!關于這個面板的具體信息,在后面章節介紹!Tasks 面板列出了所有可用的 tasks(任務)這里的 tasks 和你在空白區右鍵彈出的菜單中的 tasks 是一樣的!這個列表可以通過上方的搜索框輸入,快速定位到你想要創建的 tasks 任務,包括 action(行為),composite(復合),conditional(條件),decorator(修飾符)!類型!最后一個面板,behavior 面板,顯示的是當前行為樹 Behavior Tree 組件的屬性,這個屬性也會出現在所捆綁的 Gameobject 的屬性面板上!各個面板具體介紹請看后續章節介紹!

最后的是工具欄,工具欄提供了例如添加/刪除等基礎的行為樹操作的工具!上圖中 1 號位置的箭頭是用來預覽不同行為樹,如果在同一個 Gameobject上添加了多個行為樹的話!2 號位置用來顯示所有行為樹,并可以選擇某個行為樹,3 號位置是當前場景中擁有行為樹的 Gameobject,可以展開下拉菜單并查看和選擇!4 號位置也是用來選擇當前 Gameobject 上的不同行為樹,如果有多個的話,5 號是刪除當前行為樹,6 號增加一個行為樹,7 號鎖定當前行為樹試圖,不過因為在場景中點擊其他資源導致行為樹設計面板中的內容切換! 8號是保存當前行為樹,9 號是導出當前行為樹作為一個 Scriptable資源以便其他行為樹調用,10 號截圖,11 號偏好設置,包括了一些基礎設置!

什么是行為樹 Behavior Tree ?
行為樹在人工智能游戲中很受歡迎。像《光暈 2》就是一個使用行為樹并火起來的游戲!行為樹的有點就是很容易理解并且是可視化的編輯!

下面來了解一下行為樹:有四種不同類型的 task(任務): 包括 action(行為),composite(復合),conditional(條件),decorator(修飾符)!action(行為)可能很容易理解,因為他們在某種程度上改變游戲的狀態和結果。 
conditional(條件)用來判斷某些游戲屬性是否合適!例如:在上圖中的行為樹中,有兩個 conditional(條件)節點,兩個 action(行為)節點前兩個 conditional(條件)用來檢查是否需有敵人,并確保是否有足夠的子彈。如果這些條件都是真的,納悶這兩個 task(任務)將被執行,并執行后續任務,如果有 conditional(條件)不滿足,則不會執行后續操作,直接返回上層的 Sequence,并結束本次行為樹的執行!之后的是一個并行隊列(parallel),下面的兩個 action(行為)第一個負責計算設計傷害,第二個負責播放射擊動畫,他們是同事發生的!這里你完全可以把后面的兩個 action(行為)作為單獨的一個行為樹!以此類推,編輯出負責的,嵌套的行為樹!
composite(復合):從上圖中可以看出,Sequence 和 parallel 屬于 composite(復合)節點。一個是順序執行,一個是并列執行!
decorator(修飾符):這個類型的節點只能有一個子節點。它的功能是修改子任務的行為。在上面的例子中,我們沒有使用 decorator(修飾符),如果你需要類似于打斷操作的話會用得到這個 decorator(修飾符)類型!舉個例子:一個收集資源的操作,它可能有一個中斷節點,這個節點判斷是否被攻擊,如果被攻擊則中斷收集資源操作!decorator(修飾符)的另一個應用場合是重復執行子任務 X 次,或者執行子任務直到完成! 
行為樹還有一個重要話題,那就是返回狀態!有時候一個 task(任務)需要多幀才能完成。例如,大多數動畫不會在一幀開始并結束。此外有 conditional(條件)的任務需要一種方法來告訴他們的父任務條件是否正確,以便讓父節點確定子節點的執行順序。這兩個問題都可以使用 status(狀態)來解決。一個任務有三種不同狀態:運行,成功或者失敗。在第一個例子中,射擊動畫的 task 任務只有一個 status 狀態,而確定敵人的條件是在 Within Sight任務中返回的,如果返回失敗,也就是不在視野中則不會執行到后面的任務!

究竟該用行為樹還是有限狀態機?
Behavior Trees or Finite State Machines

在什么情況下會需要用行為樹(BehaviorTree)而不用有限狀態機(FiniteStateMachines)例如 PlayMaker 這種插件?從更高級的邏輯層面來講,行為樹常常用于復雜的人工智能,而有限狀態機(FSMs)則用于一般的可視化變成。當然你也可以用有限狀態機(FSMs)來寫 AI,或者使用行為樹(BehaviorTree)來進行可視化編程,工具的使用因人而異!《人工智能開發》《AI Game Development》的作者 Alex J. Champandard 在 2007 年 12 月 28 日的一篇博客中提到:有限狀態機(FSMs)時代已經結束了的 10 大原因!原文地址:http://aigamedev.com/open/article/fsm-age-is-over/ 。(譯者:當然萬事無絕對,有限狀態機還是有他的用武之地的!因人而異,因游戲而異!)雖然行為樹不至于走到這一步,但是可以肯定行為樹在 AI 上比狀態機有著絕對的優勢!
行為樹比有限狀態的幾個優勢:行為樹提供了強大的靈活性,非常強大,并且很容易更改行為樹結構! 
讓我們先來看第一個優勢:靈活性!在使用狀態機 FSM 時,你要如何同時執行兩個狀態呢?你只能去創建兩個狀態機(FSM)!但是如果你使用行為樹的話,你只需要添加一個并行節點(Parallel)即可,所有子節點都將并行執行!使用 Behavior Designer,這些子節點可以是 PlayMaker 的 FSM,并且這些 FSMs 將被并行觸發! 
另一個關于靈活性的例子就是 guard task(監控任務)。比如你有兩個不同的 task(任務)一個播放聲音,一個播放特效。這兩個任務在行為樹里是兩個不同的分支,所以他們之間互相并不知道對方的狀態,有可能同一時間這兩個任務被同時執行!你可能不希望這種情況發生。在這種情況下,你可以添加一個 semaphore task(在 Behavior Designer 中被稱為 Task Guard 監控任務)這樣就可以在行為樹中保證當前要么播放音效,要么播放特效!只有當第一個播放完畢,才會播放第二個! 
行為樹另一個有點:行為樹的結構很健壯很清晰!這并不是說 FSM 結構并不夠健壯不夠清晰,只是他們的實現方式不同!在我看來,行為樹讓 AI 實現比有限制狀態機更加方便!行為樹能更好的去表達和實現復雜的 AI,而如果使用 FSM 去實現則會很復雜!為了達到同樣的效果的 FSM 連接線最終可能開上去就像面條! 
最后一個行為樹優點:方便修改!行為樹邊的如此受歡迎原因之一就是很容易創建可視化的編輯器!在 FSM 中你如果想改變執行順序,你必須在狀態之間進行切換操作,改變各種連線!而在行為樹中你不必這么麻煩!而且添加刪除節點也很方便! 

說了這么多,行為樹和 FSM 并不一定是互斥的!他們可以相互配合使用已達到更好的效果!

行為樹組件
Behavior Tree Component
如下圖:

這個組件記錄了你的行為樹的結構以及一些 BehaviorDesigner 配置信息!下面的 API 用來啟動和停止你的行為樹!
public void EnableBehavior();
public void DisableBehavior(bool pause = false);
你可以通過下面的這些方法查找行為樹中的相關節點 task 任務
TaskType FindTask< TaskType >();
List< TaskType > FindTasks< TaskType >();
Task FindTaskWithName(string taskName);
List< Task > FindTasksWithName(string taskName);
行為樹當前的執行狀態可以像下面這樣獲取:
behaviorTree.ExecutionStatus;
當行為樹運行結束后會有一個狀態被返回,返回的接口可能是 Success 成功或者是 Failure 失敗,這個結構依賴于行為樹中的各個
子節點 Task 任務的返回值!
你可以對行為樹監聽以下事件:
OnBehaviorStart
OnBehaviorRestart
OnBehaviorEnd
行為樹組件包含以下幾個屬性:
Behavior Name
行為樹的名稱
Behavior Description
行為樹的描述信息
External Behavior
一個外部行為樹的資源引用,行為樹可以被導出成外部序列化文件(ScriptableObject 文件)單獨存儲,并被其他行為樹引用,或
者作為子節點任務而使用!方便了行為樹的共用!
Group
行為樹的分組編號,用來將行為樹分組!可以用來方便的查找到特定的行為樹!
Start When Enabled
如果設置為 true,那么當這個行為樹組件 enabled 的時候,這個行為樹就會被執行!
Pause When Disabled
如果設置為 true,那么當這個行為樹組件 disabled 的時候,這個行為樹就會被暫停!
Restart When Complete
如果設置為 true,那么當這個行為樹組件執行結束的時候,這個行為樹就會被重新執行!
Reset Values On Restart
如果設置為 true,那么當這個行為樹組件 reset 的時候,這個行為樹就會被重新執行!
Log Task Changes
當設置為 true 是,這個行為樹下只要 task 流程發生變化就會打印一條 log 日志到控制臺中!

用腳本創建一個行為樹
在某些情況下,你可能想要通過腳本在運行時創建一個行為樹,而不是直接使用拖拽或者面板操作去創建!例如:如果你已經導出了一個外部行為樹,并想通過腳本創建它的話,可以如下這么做:

在這個例子中公共變量behaviorTree 包含你引用的外部行為樹。新創建的行為樹在創建時將自動加載所有子節點任務。通過設置 startWhenEnabled 為 false 來阻止行為樹在創建后立刻被執行!可以通過 bt.enabledBehavior()來開啟行為樹!

行為管理器
Behavior Manager

當運行一個行為樹的時候,會在場景中自動創建一個名稱為 BehaviorManager 的 GameObject,并添BehaviorManage.cs!
這個腳本用來管理所有場景中的行為樹!
你可以控制行為樹的更新類型,以及更新時間等等!"Every Frame"是每幀都更新行為樹!"Specify Seconds"定義個一個更新間隔時間!"Manual"是手動調用更新,選擇這個后需要通過腳本來調用行為樹的更新,例如下面這樣:
BehaviorManager.instance.Tick();
此外,如果你想讓不同的行為樹都有各自獨立的更新間隔的話,可以這樣:
BehaviorManager.instance.Tick(BehaviorTree);
Task Execution Type(任務執行類型)允許你指定行為樹行為樹的執行次數,默認是"No Duplicates"(不復制,不重復)像下圖中的這種循環操作
可以簡單的通過這類設置執行次數來實現!

Repeater Task(重復任務節點)設置成 5 次。如果 Task Execution Type(任務執行類型)被設置為"No Duplicates"(不復制,不重復),那么 Play Soundtask(播放音樂任務節點)則會被每幀執行一次。如果 Task Execution Type(任務執行類型)被設置為 5,那么那么 Play Sound task(播放音樂任務節點)會在每幀被執行 5 次!

Tasks(任務)
在整個任務樹的最高層的節點我們稱之為 Task(任務)。這些 task 任務擁有類似于 MonoBehavior 那樣的接口用于實現和擴展,如下:

// OnAwake is called once when the behavior tree is enabled. Think of it as a constructor
public virtual void OnAwake();
// OnStart is called immediately before execution. It is used to setup any variables that need to be reset from the previous run
public virtual void OnStart();
// OnUpdate runs the actual task
public virtual TaskStatus OnUpdate();
// OnEnd is called after execution on a success or failure.
public virtual void OnEnd();
// OnPause is called when the behavior is paused and resumed
public virtual void OnPause(bool paused);
// The priority select will need to know this tasks priority of running
public virtual float GetPriority();
// OnBehaviorComplete is called after the behavior tree finishes executing
public virtual void OnBehaviorComplete();
// OnReset is called by the inspector to reset the public properties
public virtual void OnReset();
// Allow OnDrawGizmos to be called from the tasks
public virtual void OnDrawGizmos();
// Keep a reference to the behavior that owns this task
public Behavior Owner;

task 任務有三個基礎的公共屬性:name, comment, instant(名稱,簡介,立刻)。這里的 instant 立刻,并不好容易理解!行為樹中,當一個 task 任務返回成功或者失敗后,行為樹會在同一幀中立刻移動到下一個 task 任務。如果你沒有選擇 instant 選項,那么在當前 task 任務執行完畢后,都會停留在當前節點中,直到收到了下一個 tick,才會移動到下一個 task 任務!
下面是執行的順序的流程圖:

父任務 Parent Tasks
behavior tree 行為樹中的父任務 task 包括:composite(復合),decorator(修飾符)!雖然 Monobehaviour 沒有類似的 API,但是并不難去理解這些功能:

// The maximum number of children a parent task can have. Will usually be 1 or int.MaxValue
public virtual int MaxChildren();
// Boolean value to determine if the current task is a parallel task
public virtual bool CanRunParallelChildren();
// The index of the currently active child
public virtual int CurrentChildIndex();
// Boolean value to determine if the current task can execute
public virtual bool CanExecute();
// Apply a decorator to the executed status
public virtual TaskStatus Decorate(TaskStatus status);
// Notifies the parent task that the child has been executed and has a status of childStatus
public virtual void OnChildExecuted(TaskStatus childStatus);
// Notifies the parent task that the child at index childIndex has been executed and has a status of childStatus
public virtual void OnChildExecuted(int childIndex, TaskStatus childStatus);
// Notifies the task that the child has started to run
public virtual void OnChildStarted();
// Notifies the parallel task that the child at index childIndex has started to run
public virtual void OnChildStarted(int childIndex);
// Some parent tasks need to be able to override the status, such as parallel tasks
public virtual TaskStatus OverrideStatus(TaskStatus status);
// The interrupt node will override the status if it has been interrupted.
public virtual TaskStatus OverrideStatus();
// Notifies the composite task that an conditional abort has been triggered and the child index should reset
public virtual void OnConditionalAbort(int childIndex);

編寫自定義的條件任務節點
Writing a New Conditional Task

這個主題包含兩個部分。第一部分介紹如何編寫新的條件任務節點 conditional task,第二個部分介紹如何編寫行為任務 action taskconditional task(條件任務節點)用來判斷某些變量和條件,而 action task(行為任務節點)則負責執行某些具體的邏輯操作!下面舉例來寫一個判斷是否在視野距離中的條件任務節點(WithinSight)以及一個朝目標移動的(action task)(譯者:具體步驟略,直接上最終完整代碼)

編寫自定義行為任務節點
Writing a New Action Task

最終在編輯器中連接起來后是這個樣子!

調試

當行為樹在執行的過程中,你會看到類似上圖的效果,綠色的是真在執行的部分,灰色的是沒有執行或者執行過的部分!部分節點的右下角 ,

或者

表示這個節點的返回值是成功,還是失敗!任務運行時仍然可以通過屬性面板來改變數值并查看數值改變后的游戲表現!

通過鼠標右鍵點擊某個任務節點,可以給這個節點添加一個斷掉,這樣在運行到這個節點的時候會中斷,你可以查看節點的狀態和屬性等等!如上圖;

當你選中某個任務節點后,可以通過左側的 Inspector 面板來查看具體的變量,并通過變量掐面的按鈕,在設計區域查看變量具體的值!如上圖!

有時候你只希望執行行為樹的一部分而不是全部,那么你可以禁用某些節點極其子節點,只需選中某個節點然后選擇左上角的 X號即可!

另外通過打開 Behavior 的 LogTaskchanges 也可以打印行為樹的執行順序,類似于下面的輸出

GameObject - Behavior: Push task Sequence (index 0) at stack index 0
GameObject - Behavior: Push task Wait (index 1) at stack index 0
GameObject - Behavior: Pop task Wait (index 1) at stack index 0 with status Success
GameObject - Behavior: Push task Wait (index 2) at stack index 0
GameObject - Behavior: Pop task Wait (index 2) at stack index 0 with status Success
GameObject - Behavior: Pop task Sequence (index 0) at stack index 0 with status Success
Disabling GameObject – Behavior

這些消息可以分成以下部分:

{game object name } – {behavior name}: {task change} {task type} (index {task index}) at stack index {stack index} {optional status}

條件節點的終止 Conditional Aborts
Conditional aborts(條件 終止)允許你的行為樹動態的改變,而無需使用很多的類似于 打斷/執行打斷(Interrupt/Perform Interrupt) 等等類似的任務。這個特性類似于虛幻 4 中的觀察者中止。大多數的其他行為樹工具在處理類似問題的時候都需要重新遍歷一次行為樹。而這里的 Conditionalaborts (條件終止)可以避免這種重新遍歷的情況!以下圖為例來說明下它的用法:

當這個行為樹運行的時候,先執行 Conditional 判斷,如果返回正確,則到 Wait 節點等待,這里 Wait 節點等待 10 秒!假設在等待過程中 conditional 節點的判斷條件發生變化,返回 failure。如果 conditional 的 aborts(打斷)被開啟了的話,conditional 節點會觸發一個打斷操作并停止掉 Wait 節點的任務!conditional 節點任務會根據之前的邏輯重新評估并返回是否成功!conditional 節點的aborts 可以被任何 composite 復合節點(上圖中的 Sequence 節點)訪問到.如下圖這樣:如果第一個 Int 的判斷條件不滿足,則會被重新執行一遍 Sequence

一共有四種中斷類型的 abort types: None, Self, Lower Priority, and Both.
None

這種是默認的中斷類型!
Self

這是一種自包含中斷類型。也就是會檢測此節點下所有條件判斷節點,即便是被執行過的節點,如果判斷條件不滿足則打斷當前執行順序從新回到判斷節點判斷,并返回判斷結果!
Lower Priority

當運行到后續節點時,本節點的判斷生效了的話則打斷當前執行順序,返回本節點執行!

Both

包含了 上面的兩個類型!

下面的示例將使用低優先級的中斷類型 Lower Priority:

在這個例子中左邊的 Sequence 的中斷類型為 Lower Priority,假設左側分支返回錯誤,行為樹將跳轉到右側分支!當右側分支運行時,第一個節點的判斷條件變成了 success。這時因為判斷結果發生變化并且設置了 Lower Priority,所以會打斷當前正在執行 Action 并返回去執行第一個 Action。 譯者:如果要自行測試上圖效果,建議把 Action 換成 wait 節點,我在測試的時候用的 log 結果 log 太快,這個 LowerPriority測試了很久也沒搞明白,原來是我的 Action 太快,導致行為樹結束);如下圖

條件在被檢測時會有一個圖標來標記,表示這個判斷節點當前被檢測中,當狀態變化后會根據打斷類型打斷行為樹當前的執行順序!上圖中左下角的 Int Comparison(整形數值判斷)節點,如果判斷返回 false 則會打斷 wait,并跳轉到后一個 Sequence 隊列,如果此時判斷有變成了有效值則又會跳回來執行第一個 Wait。這是因為第一個 Sequence 選擇了 Both 的打斷類型!另外有打斷的條件節點可以嵌套。例如下圖!

如果按下 Fire1(is Buttondown 監聽的是 Fire1),則會跳回到左側 Sequence 下的 Wait

Event 事件
Behavior Designer 中的 Event 事件系統可以讓你很容易的使用!你可以通過代碼觸發一個 event 事件,也可以通過行為樹的節點來觸發一個事件!
這些事件可以通過行為樹的 SendEvent 節點和 HasRecivedEvent 節點來觸發和監聽事件!當一個事件要被發送時使用 SendeEvnet 節點。HasRecivedEvent 節點是一個條件節點,當接收到注冊的事件后會返回 success。可以通過事件名稱的定義來觸發和監聽一個事件!

出了通過行為樹節點來觸發事件,還可以通過代碼來觸發事件!BehaviorTree.SendEvent 函數就是用來干這個的:

var behaviorTree = GetComponent< BehaviorTree >();
behaviorTree.SendEvent< object >("MyEvent", Vector3.zero);

上面這個例子就是通過代碼,將事件" MyEvent"發送到行為樹,并帶有參數( Vector3.zero),如果行為樹中有監聽器,則監聽器位置會返回 success!

Task 的引用,任務節點之間的引用!
在編寫一個 Task 任務節點的時候可能需要訪問另外一個 Task 任務。例如 TaskA 想訪問 TaskB 的某個屬性數值!例如下面這樣的 TaskA 和 TaskB

using UnityEngine;
using BehaviorDesigner.Runtime.Tasks;
public class TaskA : Action
{
public TaskB referencedTask;
public void OnAwake()
{
Debug.Log(referencedTask.SomeFloat);
}
}
TaskB 然后看起來像:
using UnityEngine;
using BehaviorDesigner.Runtime.Tasks;
public class TaskB : Action
{
public float SomeFloat;
}

將這兩個任務添加到行為樹編輯器中:

選中 TaskA,你會在 Inspector 面板中看到變量 referencedTask 他是個 Task 類型,這時你可以選擇 Select 按鈕進行選擇,最終像下圖這樣

你可以通過點擊"X"號來取消引用關系!這樣配置后,在執行 TaskA 的時候就會顯示 TaskB 的屬性,像下圖這樣

Task 的引用也可以是數組引用,像下面這樣!

public class TaskA : Action
{
public TaskB[] referencedTasks;
}

變量同步器 Variable Synchronizer

在 GameObject 上掛在腳本,并設置同步的源,以及同步目標,中間的箭頭表示同步方向,向右表示上面的變量同步給下面的,向左表示下面的變量同步給上面的!

Task 任務的可用屬性

HelpURL : web 連接
[HelpURL("http://www.opsive.com/assets/BehaviorDesigner/documentation.php?id=27")]
public class Parallel : Composite
{
//////////////////////////////////////////////////////////////////////////////
TaskIcon :任務的圖標
[TaskIcon("Assets/Path/To/{SkinColor}Icon.png")]
public class MyTask : Action
{
//////////////////////////////////////////////////////////////////////////////
TaskCategory:任務的顯示位置(在 Task 任務面板中的顯示位置)
[TaskCategory("Common")]
public class Seek : Action
{
[TaskCategory("RTS/Harvester")]
public class HarvestGold : Action
{
//////////////////////////////////////////////////////////////////////////////
TaskDescription:功能描述的文本內容,顯示在編輯器布局區域的左下角
[TaskDescription("The sequence task is similar to an \"and\" operation. ..."]
public class Sequence : Composite
{
//////////////////////////////////////////////////////////////////////////////
LinkedTask:應用其他的 Task 任務
[LinkedTask]
public TaskGuard[] linkedTaskGuards = null;
//////////////////////////////////////////////////////////////////////////////
InheritedField : 繼承屬性
[InheritedField]
public float moveSpeed;

Composites (復合)節點
Sequence(序列)節點

這個節點是一個"和"的關系,也就是他下面的子節點的執行順序是一個接著一個的!如果其中一個返回 false。那么后續的子節點不會被執行,這個序列節點返回 false。只有當所有子節點全部完成并返回 success 的時候,這個 Sequence(序列)節點才會返回 success;

Selector(選擇)節點

這個節點是"或"的關系,也就是他下面的子節點的執行順序是一個或另一個的!只有所有子節點返回 false 才會返回 false。只要有一個子節點返回 success,那么這個 Selector 節點就會返回 success,后續的節點不會被執行!

Parallel(并行)節點

這個節點類似于 Sequence(序列)節點。不同的是,Parallel(并行)節點會在同一時間執行所有子節點而不是一個一個的去執行!

如果子節點中有任意一個返回 false,則停掉所有子節點并返回 false。只有所有子節點全部返回 success 的時候,才會返回 success。

Parallel Selector(并行選擇)節點


類似于 Selector(選擇)節點,ParallelSelector(并行選擇)節點只要有一個子節點返回 success,那么他就會返回 success!不同于 Selector的一點就是 ParalleSelector(并行選擇)節點會在同一時間執行下面的所有子節點,如果有一個節點返回 success,則會停止掉其他所有子節點并返回 success。只有當所有子節點全部 false 的時候才會返回 false!

Priority Selector(優先選擇)節點

類似于 Selector(選擇)節點,PrioritySelector(并行選擇)節點只要有一個子節點返回 success,那么他就會返回 success!不同點在于,子節點的執行順序不是從左到右的,而是通過優先級來確定的執行順序!較高的優先級的子節點會被先執行!(譯者:優先級在哪里設置的,沒有搞清楚,目前測試結果同 Selector 節點,后來還是用我大 Google 搜索到的解決辦法!百度就是個垃圾站)需要在 Task 類中覆蓋函數,來設置不同的 Priority,原文地址:

http://forum.unity3d.com/threads/behavior-designer-behavior-trees-for-everyone.227497/page-4
// The priority select will need to know this tasks priority of running
public virtual float GetPriority();

Random Selector(隨機選擇)節點

這個節點的特點是:隨機的執行子節點,只要有一個子節點返回成功,它就會返回成功,不再執行后續節點。如果所有子節點都返回 false 則它也返回 false!在這個節點的屬性面板中有:seed(隨機種子)的設置,自行使用! 

Random Sequence(隨機序列)節點

類似于 Sequence(序列)節點,只是他的執行順序是隨機的!只要遇到一個子節點返回 false,RandomSequence(隨機序列)就返回錯誤,直到全部子節點都返回 success,它才會返回 success!在這個節點的屬性面板中有:seed(隨機種子)的設置,自行使用!

Selector Evaluator(重復判斷選擇)節點

這個節點每幀都會去重新評估子節點的執行狀態并選擇。它會執行子節點中優先級最低的子節點!每幀都會這么干!如果當前一個高優先級的節點在運行并且下一幀要執行的子節點優先級比較低,那么它會打斷高優先級的節點,去執行優先級低的子節點! Selector Evaluator(重復判斷選擇)節點會從低到高的去遍歷執行所有子節點,直到遇到一個返回 success 的!如果所有子節點都返回 false,那么它就返回 false!否則只要有一個返回 success,它就會返回 success!這個節點模擬了條件打斷功能,如果子節點沒有條件節點的話! 

Conditionals(條件判斷)節點
條件節點的任務是判斷游戲的一些屬性,比如玩家是否活著,怪物是否在視野距離內!

Random Probability (隨機概率)節點

通過設置 successProbability 屬性來控制返回 success 的幾率(默認 0.5,也就是 50%幾率)!另外還有 seed 隨機種子的設置等! 

Compare Field Value(字段比較)節點

比較指定的值的字段值。 返回成功如果值是相同的。

Has Received Event(是否接收到事件)

(譯者:還有很多條件節點,這里就忽略了!)

Decorators(修飾器)節點

這種節點的功能是用來包裝另一個節點!(只能有一個子節點)。Decorators(修飾節點)將改變節點的行為!例如:修飾節點可以再運行時控制子節點直到返回某個特定狀態(success 或者是 false)。后者是對子節點返回結果取反(即:success 返回 false,false,返回 success);下面來一一介 BehaviorDesigner 默認自帶的幾個 Decorator(裝飾器節點)

Conditional Evaluator (條件節點的評估) 裝飾節點

參數設置:
1:reevaluate :條件節點是否需要每幀都重新評估一次
2:conditionalTask:要被評估的條件節點,注意:這個節點本身就是個條件節點!
對設置的條件節點進行評估,如果條件節點返回 success,那么運行子節點并返回子節點的運行結果!如果條件節點沒有返回 success那么子節點不會被運行,并且立刻返回 failure!條件節點只會在開始運行的時候被評估一次!

Interrupt(打斷)裝飾節點

如果打斷節點被觸發,則打斷下面的所有子節點任務的執行!打斷命令可以被 Perform interruption(執行打斷)節點發起!打斷節點在收到打斷命令前,不會打斷他下面的子節點的執行狀態!如果子節點執行完畢還沒有收到打斷命令,則直接返回子節點的執行結果!例如下圖這樣:

Inverter(取反)裝飾節點

子節點的任務完成后返回值,在這個節點會被取反并傳遞到上一級中!

Repeater(重復/循環)裝飾節點

有三個屬性設置:執行次數,是否一直重復,運行直到返回錯誤!

Return Failure (返回失敗)裝飾節點

只要子節點當前的狀態不是 running,也就是子節點執行結果無論是 success 還是 failure,都返回 failure!如果子節點狀態是 running的話則返回 running!

Return Success(返回正確)裝飾節點

只要子節點當前的狀態不是 running,也就是子節點執行結果無論是 success 還是 failure,都返回 success!如果子節點狀態是 running的話則返回 running!

Task Guard(任務守衛)裝飾節點

類似于多線程互斥操作中使用的 Lock 標記,為了避免公共數據被多次引用!下圖以外部行為樹為例進行演示!這里使用并行觸發兩個外部行為樹,如果不加上 TaskGuard,那么兩邊都會去并行執行外部行為樹,現在加上 TaskGuard 后同一時間只能執行一個,而另一個要等待執行完畢才能執行

上圖 TaskGard 配置如下圖:

Until Failure(直到失敗)裝飾節點

直到子節點返回 failure,否則一直循環執行子節點, 如下圖:

Until Success(直到成功)裝飾節點
直到子節點返回 success,否則一直循環執行子節點!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容

  • 概述 本文就數種重要的Gameplay框架及插件,簡述它們的原理,介紹這些Gameplay框架的適用場合,并進行對...
    DonaldW閱讀 11,305評論 7 58
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 第二天一大早,當我和孫仲謀聯絡員剛一到村口停車,就看見伏村長站在小廟的門前柏樹下,我的心里一下子明白事情有了實質性...
    鄧文偉閱讀 444評論 0 2
  • 前幾日驚喜地瞥見玉蘭樹上開了一朵潔白的玉蘭,正是這一眼,讓我的心情也跟著好起來。我不由得多看了幾眼,我想這玉蘭真是...
    lulal閱讀 399評論 0 0
  • 生活中,總有那么多精心過日子的人,用心經營自己的生活,一點小心思,一些小裝飾,給生活增添了不少樂趣。 A,她說:經...
    Fanny讀書閱讀 370評論 4 2