Behavior Designer 中文版教程
Behavior Designer 概述
中文版PDF下載地址:http://download.csdn.net/download/mango9126/9840488
Behavior Designer 是一個(gè)行為樹(shù)插件!是為了讓設(shè)計(jì)師,程序員,美術(shù)人員方便使用的可視化編輯器!Behavior Designer 提供了強(qiáng)大的 API 可以讓你輕松的創(chuàng)建 tasks(任務(wù)),配合 uScript 和 PlayMaker 這樣的插件,可以不費(fèi)吹灰之力就能夠創(chuàng)建出強(qiáng)大的 AI 系統(tǒng),而無(wú)需寫(xiě)一行代碼! 本指南將介紹所有 Behavior Designer 的功能特性!如果你還不了解什么是行為樹(shù)(behavior trees)請(qǐng)查看"行為樹(shù)的概述"!依賴(lài)于 Behavior Designer你完全可以不用關(guān)心什么是行為樹(shù)(behavior trees)但是,如果你了解了一些 behavior trees!這將有助于你使用 Behavior Designer,包括一些常用組件,例如:tasks(任務(wù)),action(行為),composite(復(fù)合),conditional(條件),decorator(修飾符)! 當(dāng)你第一次打開(kāi) Behavior Designer ,會(huì)出現(xiàn)下面的窗口:這里一共分為四大部分:下圖中的第一個(gè)部分是主要操作區(qū),用來(lái)創(chuàng)建你的行為樹(shù)!第二部分是面板屬性區(qū),這里可以編輯一個(gè)行為樹(shù)的特定屬性,添加新任務(wù),創(chuàng)建新的變量,或者編輯 tasks(任務(wù))的參數(shù)。第三部分是工具欄,你可以添加/刪除行為樹(shù),鎖定當(dāng)前行為樹(shù),查看所有行為樹(shù),等等!第四部分是調(diào)試工具欄。你可以啟動(dòng)/停止,逐步調(diào)試,暫停,查看錯(cuò)誤!
第一部分是主要的設(shè)計(jì)工作的區(qū)域!在這個(gè)區(qū)域,你可以創(chuàng)建新的 Task(任務(wù))和設(shè)計(jì)這些 Task(任務(wù))的行為樹(shù)。第一步要做的就是創(chuàng)建一個(gè)行為樹(shù)(behavior tree),通過(guò)右鍵選擇"Add Behavior Tree"可以創(chuàng)建新的 behavior tree 行為樹(shù),或者通過(guò)上方的工具欄 Lock(鎖定)旁邊的加號(hào)添加一個(gè)新的行為樹(shù)!
一旦行為樹(shù)創(chuàng)建完畢,你就可以開(kāi)始添加 tasks(任務(wù))了。添加一個(gè) task 可以通過(guò)右鍵點(diǎn)擊空白區(qū),或者左側(cè) 2 區(qū)中的 Tasks 標(biāo)簽來(lái)創(chuàng)建!一旦你的task 創(chuàng)建完畢,你將看到類(lèi)似于下面圖中的效果!
這里的 Entry task 是默認(rèn)添加的,作為根節(jié)點(diǎn)進(jìn)行后續(xù)的 Task 添加!這里能看到添加的 Sequence(隊(duì)列)節(jié)點(diǎn)有錯(cuò)誤,這是因?yàn)檫@個(gè) Sequence必須有后續(xù)子節(jié)點(diǎn),只要添加了子節(jié)點(diǎn),這個(gè)錯(cuò)誤就會(huì)消失了!現(xiàn)在我們已經(jīng)有了第一個(gè) Task,接下來(lái)讓我們多添加幾個(gè):
你可以創(chuàng)建 Sequence(序列)節(jié)點(diǎn),或者 selector(選擇器)節(jié)點(diǎn),他們都是 task(任務(wù))。通過(guò)多個(gè)這種節(jié)點(diǎn)的組合,你可以創(chuàng)建很深的層次結(jié)構(gòu)!
如果在創(chuàng)建過(guò)程中你不小心搞錯(cuò)了什么,那么選擇有錯(cuò)誤的節(jié)點(diǎn),delete 刪除掉即可!
Behavior Designer 的執(zhí)行順序是從左到右的順序執(zhí)行,并且是深度優(yōu)先。上圖中的執(zhí)行順序如下:
SequenceA,SelectorA、SequenceB ActionA、ActionB ActionC,SelectorB,ActionD ActionE
現(xiàn)在我們已經(jīng)有了一個(gè)基礎(chǔ)的行為樹(shù),讓我們改改參數(shù)看看!選擇 ActionC 節(jié)點(diǎn)然后查看左側(cè)的屬性面板,選到 Inspector 視圖!你可以再這里重命名這個(gè) Task(任務(wù))名稱(chēng),設(shè)置參數(shù),或者輸入一個(gè)注釋在(Comment)。
在左側(cè)的界面中有出了 Inspector 還有其他三個(gè)標(biāo)簽(Behavior,Tasks,Variables)Variables 面板允許你創(chuàng)建共享變量,并改變變量值!關(guān)于這個(gè)面板的具體信息,在后面章節(jié)介紹!Tasks 面板列出了所有可用的 tasks(任務(wù))這里的 tasks 和你在空白區(qū)右鍵彈出的菜單中的 tasks 是一樣的!這個(gè)列表可以通過(guò)上方的搜索框輸入,快速定位到你想要?jiǎng)?chuàng)建的 tasks 任務(wù),包括 action(行為),composite(復(fù)合),conditional(條件),decorator(修飾符)!類(lèi)型!最后一個(gè)面板,behavior 面板,顯示的是當(dāng)前行為樹(shù) Behavior Tree 組件的屬性,這個(gè)屬性也會(huì)出現(xiàn)在所捆綁的 Gameobject 的屬性面板上!各個(gè)面板具體介紹請(qǐng)看后續(xù)章節(jié)介紹!
最后的是工具欄,工具欄提供了例如添加/刪除等基礎(chǔ)的行為樹(shù)操作的工具!上圖中 1 號(hào)位置的箭頭是用來(lái)預(yù)覽不同行為樹(shù),如果在同一個(gè) Gameobject上添加了多個(gè)行為樹(shù)的話!2 號(hào)位置用來(lái)顯示所有行為樹(shù),并可以選擇某個(gè)行為樹(shù),3 號(hào)位置是當(dāng)前場(chǎng)景中擁有行為樹(shù)的 Gameobject,可以展開(kāi)下拉菜單并查看和選擇!4 號(hào)位置也是用來(lái)選擇當(dāng)前 Gameobject 上的不同行為樹(shù),如果有多個(gè)的話,5 號(hào)是刪除當(dāng)前行為樹(shù),6 號(hào)增加一個(gè)行為樹(shù),7 號(hào)鎖定當(dāng)前行為樹(shù)試圖,不過(guò)因?yàn)樵趫?chǎng)景中點(diǎn)擊其他資源導(dǎo)致行為樹(shù)設(shè)計(jì)面板中的內(nèi)容切換! 8號(hào)是保存當(dāng)前行為樹(shù),9 號(hào)是導(dǎo)出當(dāng)前行為樹(shù)作為一個(gè) Scriptable資源以便其他行為樹(shù)調(diào)用,10 號(hào)截圖,11 號(hào)偏好設(shè)置,包括了一些基礎(chǔ)設(shè)置!
什么是行為樹(shù) Behavior Tree ?
行為樹(shù)在人工智能游戲中很受歡迎。像《光暈 2》就是一個(gè)使用行為樹(shù)并火起來(lái)的游戲!行為樹(shù)的有點(diǎn)就是很容易理解并且是可視化的編輯!
下面來(lái)了解一下行為樹(shù):有四種不同類(lèi)型的 task(任務(wù)): 包括 action(行為),composite(復(fù)合),conditional(條件),decorator(修飾符)!action(行為)可能很容易理解,因?yàn)樗麄冊(cè)谀撤N程度上改變游戲的狀態(tài)和結(jié)果。 conditional(條件)用來(lái)判斷某些游戲?qū)傩允欠窈线m!例如:在上圖中的行為樹(shù)中,有兩個(gè) conditional(條件)節(jié)點(diǎn),兩個(gè) action(行為)節(jié)點(diǎn)前兩個(gè) conditional(條件)用來(lái)檢查是否需有敵人,并確保是否有足夠的子彈。如果這些條件都是真的,納悶這兩個(gè) task(任務(wù))將被執(zhí)行,并執(zhí)行后續(xù)任務(wù),如果有 conditional(條件)不滿足,則不會(huì)執(zhí)行后續(xù)操作,直接返回上層的 Sequence,并結(jié)束本次行為樹(shù)的執(zhí)行!之后的是一個(gè)并行隊(duì)列(parallel),下面的兩個(gè) action(行為)第一個(gè)負(fù)責(zé)計(jì)算設(shè)計(jì)傷害,第二個(gè)負(fù)責(zé)播放射擊動(dòng)畫(huà),他們是同事發(fā)生的!這里你完全可以把后面的兩個(gè) action(行為)作為單獨(dú)的一個(gè)行為樹(shù)!以此類(lèi)推,編輯出負(fù)責(zé)的,嵌套的行為樹(shù)! composite(復(fù)合):從上圖中可以看出,Sequence 和 parallel 屬于 composite(復(fù)合)節(jié)點(diǎn)。一個(gè)是順序執(zhí)行,一個(gè)是并列執(zhí)行! decorator(修飾符):這個(gè)類(lèi)型的節(jié)點(diǎn)只能有一個(gè)子節(jié)點(diǎn)。它的功能是修改子任務(wù)的行為。在上面的例子中,我們沒(méi)有使用 decorator(修飾符),如果你需要類(lèi)似于打斷操作的話會(huì)用得到這個(gè) decorator(修飾符)類(lèi)型!舉個(gè)例子:一個(gè)收集資源的操作,它可能有一個(gè)中斷節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)判斷是否被攻擊,如果被攻擊則中斷收集資源操作!decorator(修飾符)的另一個(gè)應(yīng)用場(chǎng)合是重復(fù)執(zhí)行子任務(wù) X 次,或者執(zhí)行子任務(wù)直到完成! 行為樹(shù)還有一個(gè)重要話題,那就是返回狀態(tài)!有時(shí)候一個(gè) task(任務(wù))需要多幀才能完成。例如,大多數(shù)動(dòng)畫(huà)不會(huì)在一幀開(kāi)始并結(jié)束。此外有 conditional(條件)的任務(wù)需要一種方法來(lái)告訴他們的父任務(wù)條件是否正確,以便讓父節(jié)點(diǎn)確定子節(jié)點(diǎn)的執(zhí)行順序。這兩個(gè)問(wèn)題都可以使用 status(狀態(tài))來(lái)解決。一個(gè)任務(wù)有三種不同狀態(tài):運(yùn)行,成功或者失敗。在第一個(gè)例子中,射擊動(dòng)畫(huà)的 task 任務(wù)只有一個(gè) status 狀態(tài),而確定敵人的條件是在 Within Sight任務(wù)中返回的,如果返回失敗,也就是不在視野中則不會(huì)執(zhí)行到后面的任務(wù)!
究竟該用行為樹(shù)還是有限狀態(tài)機(jī)?
Behavior Trees or Finite State Machines
行為樹(shù)比有限狀態(tài)的幾個(gè)優(yōu)勢(shì):行為樹(shù)提供了強(qiáng)大的靈活性,非常強(qiáng)大,并且很容易更改行為樹(shù)結(jié)構(gòu)! 讓我們先來(lái)看第一個(gè)優(yōu)勢(shì):靈活性!在使用狀態(tài)機(jī) FSM 時(shí),你要如何同時(shí)執(zhí)行兩個(gè)狀態(tài)呢?你只能去創(chuàng)建兩個(gè)狀態(tài)機(jī)(FSM)!但是如果你使用行為樹(shù)的話,你只需要添加一個(gè)并行節(jié)點(diǎn)(Parallel)即可,所有子節(jié)點(diǎn)都將并行執(zhí)行!使用 Behavior Designer,這些子節(jié)點(diǎn)可以是 PlayMaker 的 FSM,并且這些 FSMs 將被并行觸發(fā)! 另一個(gè)關(guān)于靈活性的例子就是 guard task(監(jiān)控任務(wù))。比如你有兩個(gè)不同的 task(任務(wù))一個(gè)播放聲音,一個(gè)播放特效。這兩個(gè)任務(wù)在行為樹(shù)里是兩個(gè)不同的分支,所以他們之間互相并不知道對(duì)方的狀態(tài),有可能同一時(shí)間這兩個(gè)任務(wù)被同時(shí)執(zhí)行!你可能不希望這種情況發(fā)生。在這種情況下,你可以添加一個(gè) semaphore task(在 Behavior Designer 中被稱(chēng)為 Task Guard 監(jiān)控任務(wù))這樣就可以在行為樹(shù)中保證當(dāng)前要么播放音效,要么播放特效!只有當(dāng)?shù)谝粋€(gè)播放完畢,才會(huì)播放第二個(gè)! 行為樹(shù)另一個(gè)有點(diǎn):行為樹(shù)的結(jié)構(gòu)很健壯很清晰!這并不是說(shuō) FSM 結(jié)構(gòu)并不夠健壯不夠清晰,只是他們的實(shí)現(xiàn)方式不同!在我看來(lái),行為樹(shù)讓 AI 實(shí)現(xiàn)比有限制狀態(tài)機(jī)更加方便!行為樹(shù)能更好的去表達(dá)和實(shí)現(xiàn)復(fù)雜的 AI,而如果使用 FSM 去實(shí)現(xiàn)則會(huì)很復(fù)雜!為了達(dá)到同樣的效果的 FSM 連接線最終可能開(kāi)上去就像面條! 最后一個(gè)行為樹(shù)優(yōu)點(diǎn):方便修改!行為樹(shù)邊的如此受歡迎原因之一就是很容易創(chuàng)建可視化的編輯器!在 FSM 中你如果想改變執(zhí)行順序,你必須在狀態(tài)之間進(jìn)行切換操作,改變各種連線!而在行為樹(shù)中你不必這么麻煩!而且添加刪除節(jié)點(diǎn)也很方便!
說(shuō)了這么多,行為樹(shù)和 FSM 并不一定是互斥的!他們可以相互配合使用已達(dá)到更好的效果!
行為樹(shù)組件
Behavior Tree Component
如下圖:
這個(gè)組件記錄了你的行為樹(shù)的結(jié)構(gòu)以及一些 BehaviorDesigner 配置信息!下面的 API 用來(lái)啟動(dòng)和停止你的行為樹(shù)!
public void EnableBehavior();
public void DisableBehavior(bool pause = false);
你可以通過(guò)下面的這些方法查找行為樹(shù)中的相關(guān)節(jié)點(diǎn) task 任務(wù)
TaskType FindTask< TaskType >();
List< TaskType > FindTasks< TaskType >();
Task FindTaskWithName(string taskName);
List< Task > FindTasksWithName(string taskName);
行為樹(shù)當(dāng)前的執(zhí)行狀態(tài)可以像下面這樣獲取:
behaviorTree.ExecutionStatus;
當(dāng)行為樹(shù)運(yùn)行結(jié)束后會(huì)有一個(gè)狀態(tài)被返回,返回的接口可能是 Success 成功或者是 Failure 失敗,這個(gè)結(jié)構(gòu)依賴(lài)于行為樹(shù)中的各個(gè)
子節(jié)點(diǎn) Task 任務(wù)的返回值!
你可以對(duì)行為樹(shù)監(jiān)聽(tīng)以下事件:
OnBehaviorStart
OnBehaviorRestart
OnBehaviorEnd
行為樹(shù)組件包含以下幾個(gè)屬性:
Behavior Name
行為樹(shù)的名稱(chēng)
Behavior Description
行為樹(shù)的描述信息
External Behavior
一個(gè)外部行為樹(shù)的資源引用,行為樹(shù)可以被導(dǎo)出成外部序列化文件(ScriptableObject 文件)單獨(dú)存儲(chǔ),并被其他行為樹(shù)引用,或
者作為子節(jié)點(diǎn)任務(wù)而使用!方便了行為樹(shù)的共用!
Group
行為樹(shù)的分組編號(hào),用來(lái)將行為樹(shù)分組!可以用來(lái)方便的查找到特定的行為樹(shù)!
Start When Enabled
如果設(shè)置為 true,那么當(dāng)這個(gè)行為樹(shù)組件 enabled 的時(shí)候,這個(gè)行為樹(shù)就會(huì)被執(zhí)行!
Pause When Disabled
如果設(shè)置為 true,那么當(dāng)這個(gè)行為樹(shù)組件 disabled 的時(shí)候,這個(gè)行為樹(shù)就會(huì)被暫停!
Restart When Complete
如果設(shè)置為 true,那么當(dāng)這個(gè)行為樹(shù)組件執(zhí)行結(jié)束的時(shí)候,這個(gè)行為樹(shù)就會(huì)被重新執(zhí)行!
Reset Values On Restart
如果設(shè)置為 true,那么當(dāng)這個(gè)行為樹(shù)組件 reset 的時(shí)候,這個(gè)行為樹(shù)就會(huì)被重新執(zhí)行!
Log Task Changes
當(dāng)設(shè)置為 true 是,這個(gè)行為樹(shù)下只要 task 流程發(fā)生變化就會(huì)打印一條 log 日志到控制臺(tái)中!
用腳本創(chuàng)建一個(gè)行為樹(shù)
在某些情況下,你可能想要通過(guò)腳本在運(yùn)行時(shí)創(chuàng)建一個(gè)行為樹(shù),而不是直接使用拖拽或者面板操作去創(chuàng)建!例如:如果你已經(jīng)導(dǎo)出了一個(gè)外部行為樹(shù),并想通過(guò)腳本創(chuàng)建它的話,可以如下這么做:
在這個(gè)例子中公共變量behaviorTree 包含你引用的外部行為樹(shù)。新創(chuàng)建的行為樹(shù)在創(chuàng)建時(shí)將自動(dòng)加載所有子節(jié)點(diǎn)任務(wù)。通過(guò)設(shè)置 startWhenEnabled 為 false 來(lái)阻止行為樹(shù)在創(chuàng)建后立刻被執(zhí)行!可以通過(guò) bt.enabledBehavior()來(lái)開(kāi)啟行為樹(shù)!
行為管理器
Behavior Manager
當(dāng)運(yùn)行一個(gè)行為樹(shù)的時(shí)候,會(huì)在場(chǎng)景中自動(dòng)創(chuàng)建一個(gè)名稱(chēng)為 BehaviorManager 的 GameObject,并添BehaviorManage.cs!
這個(gè)腳本用來(lái)管理所有場(chǎng)景中的行為樹(shù)!
你可以控制行為樹(shù)的更新類(lèi)型,以及更新時(shí)間等等!"Every Frame"是每幀都更新行為樹(shù)!"Specify Seconds"定義個(gè)一個(gè)更新間隔時(shí)間!"Manual"是手動(dòng)調(diào)用更新,選擇這個(gè)后需要通過(guò)腳本來(lái)調(diào)用行為樹(shù)的更新,例如下面這樣:
BehaviorManager.instance.Tick();
此外,如果你想讓不同的行為樹(shù)都有各自獨(dú)立的更新間隔的話,可以這樣:
BehaviorManager.instance.Tick(BehaviorTree);
Task Execution Type(任務(wù)執(zhí)行類(lèi)型)允許你指定行為樹(shù)行為樹(shù)的執(zhí)行次數(shù),默認(rèn)是"No Duplicates"(不復(fù)制,不重復(fù))像下圖中的這種循環(huán)操作
可以簡(jiǎn)單的通過(guò)這類(lèi)設(shè)置執(zhí)行次數(shù)來(lái)實(shí)現(xiàn)!
Repeater Task(重復(fù)任務(wù)節(jié)點(diǎn))設(shè)置成 5 次。如果 Task Execution Type(任務(wù)執(zhí)行類(lèi)型)被設(shè)置為"No Duplicates"(不復(fù)制,不重復(fù)),那么 Play Soundtask(播放音樂(lè)任務(wù)節(jié)點(diǎn))則會(huì)被每幀執(zhí)行一次。如果 Task Execution Type(任務(wù)執(zhí)行類(lèi)型)被設(shè)置為 5,那么那么 Play Sound task(播放音樂(lè)任務(wù)節(jié)點(diǎn))會(huì)在每幀被執(zhí)行 5 次!
Tasks(任務(wù))
在整個(gè)任務(wù)樹(shù)的最高層的節(jié)點(diǎn)我們稱(chēng)之為 Task(任務(wù))。這些 task 任務(wù)擁有類(lèi)似于 MonoBehavior 那樣的接口用于實(shí)現(xiàn)和擴(kuò)展,如下:
task 任務(wù)有三個(gè)基礎(chǔ)的公共屬性:name, comment, instant(名稱(chēng),簡(jiǎn)介,立刻)。這里的 instant 立刻,并不好容易理解!行為樹(shù)中,當(dāng)一個(gè) task 任務(wù)返回成功或者失敗后,行為樹(shù)會(huì)在同一幀中立刻移動(dòng)到下一個(gè) task 任務(wù)。如果你沒(méi)有選擇 instant 選項(xiàng),那么在當(dāng)前 task 任務(wù)執(zhí)行完畢后,都會(huì)停留在當(dāng)前節(jié)點(diǎn)中,直到收到了下一個(gè) tick,才會(huì)移動(dòng)到下一個(gè) task 任務(wù)!
下面是執(zhí)行的順序的流程圖:
父任務(wù) Parent Tasks
behavior tree 行為樹(shù)中的父任務(wù) task 包括:composite(復(fù)合),decorator(修飾符)!雖然 Monobehaviour 沒(méi)有類(lèi)似的 API,但是并不難去理解這些功能:
編寫(xiě)自定義的條件任務(wù)節(jié)點(diǎn)
Writing a New Conditional Task
這個(gè)主題包含兩個(gè)部分。第一部分介紹如何編寫(xiě)新的條件任務(wù)節(jié)點(diǎn) conditional task,第二個(gè)部分介紹如何編寫(xiě)行為任務(wù) action taskconditional task(條件任務(wù)節(jié)點(diǎn))用來(lái)判斷某些變量和條件,而 action task(行為任務(wù)節(jié)點(diǎn))則負(fù)責(zé)執(zhí)行某些具體的邏輯操作!下面舉例來(lái)寫(xiě)一個(gè)判斷是否在視野距離中的條件任務(wù)節(jié)點(diǎn)(WithinSight)以及一個(gè)朝目標(biāo)移動(dòng)的(action task)(譯者:具體步驟略,直接上最終完整代碼)
編寫(xiě)自定義行為任務(wù)節(jié)點(diǎn)
Writing a New Action Task
最終在編輯器中連接起來(lái)后是這個(gè)樣子!
調(diào)試
當(dāng)行為樹(shù)在執(zhí)行的過(guò)程中,你會(huì)看到類(lèi)似上圖的效果,綠色的是真在執(zhí)行的部分,灰色的是沒(méi)有執(zhí)行或者執(zhí)行過(guò)的部分!部分節(jié)點(diǎn)的右下角 ,
或者
表示這個(gè)節(jié)點(diǎn)的返回值是成功,還是失敗!任務(wù)運(yùn)行時(shí)仍然可以通過(guò)屬性面板來(lái)改變數(shù)值并查看數(shù)值改變后的游戲表現(xiàn)!
通過(guò)鼠標(biāo)右鍵點(diǎn)擊某個(gè)任務(wù)節(jié)點(diǎn),可以給這個(gè)節(jié)點(diǎn)添加一個(gè)斷掉,這樣在運(yùn)行到這個(gè)節(jié)點(diǎn)的時(shí)候會(huì)中斷,你可以查看節(jié)點(diǎn)的狀態(tài)和屬性等等!如上圖;
當(dāng)你選中某個(gè)任務(wù)節(jié)點(diǎn)后,可以通過(guò)左側(cè)的 Inspector 面板來(lái)查看具體的變量,并通過(guò)變量掐面的按鈕,在設(shè)計(jì)區(qū)域查看變量具體的值!如上圖!
有時(shí)候你只希望執(zhí)行行為樹(shù)的一部分而不是全部,那么你可以禁用某些節(jié)點(diǎn)極其子節(jié)點(diǎn),只需選中某個(gè)節(jié)點(diǎn)然后選擇左上角的 X號(hào)即可!
另外通過(guò)打開(kāi) Behavior 的 LogTaskchanges 也可以打印行為樹(shù)的執(zhí)行順序,類(lèi)似于下面的輸出
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} 條件節(jié)點(diǎn)的終止 Conditional Aborts
Conditional aborts(條件 終止)允許你的行為樹(shù)動(dòng)態(tài)的改變,而無(wú)需使用很多的類(lèi)似于 打斷/執(zhí)行打斷(Interrupt/Perform Interrupt) 等等類(lèi)似的任務(wù)。這個(gè)特性類(lèi)似于虛幻 4 中的觀察者中止。大多數(shù)的其他行為樹(shù)工具在處理類(lèi)似問(wèn)題的時(shí)候都需要重新遍歷一次行為樹(shù)。而這里的 Conditionalaborts (條件終止)可以避免這種重新遍歷的情況!以下圖為例來(lái)說(shuō)明下它的用法:
當(dāng)這個(gè)行為樹(shù)運(yùn)行的時(shí)候,先執(zhí)行 Conditional 判斷,如果返回正確,則到 Wait 節(jié)點(diǎn)等待,這里 Wait 節(jié)點(diǎn)等待 10 秒!假設(shè)在等待過(guò)程中 conditional 節(jié)點(diǎn)的判斷條件發(fā)生變化,返回 failure。如果 conditional 的 aborts(打斷)被開(kāi)啟了的話,conditional 節(jié)點(diǎn)會(huì)觸發(fā)一個(gè)打斷操作并停止掉 Wait 節(jié)點(diǎn)的任務(wù)!conditional 節(jié)點(diǎn)任務(wù)會(huì)根據(jù)之前的邏輯重新評(píng)估并返回是否成功!conditional 節(jié)點(diǎn)的aborts 可以被任何 composite 復(fù)合節(jié)點(diǎn)(上圖中的 Sequence 節(jié)點(diǎn))訪問(wèn)到.如下圖這樣:如果第一個(gè) Int 的判斷條件不滿足,則會(huì)被重新執(zhí)行一遍 Sequence
一共有四種中斷類(lèi)型的 abort types: None, Self, Lower Priority, and Both.
None
這種是默認(rèn)的中斷類(lèi)型!
Self
這是一種自包含中斷類(lèi)型。也就是會(huì)檢測(cè)此節(jié)點(diǎn)下所有條件判斷節(jié)點(diǎn),即便是被執(zhí)行過(guò)的節(jié)點(diǎn),如果判斷條件不滿足則打斷當(dāng)前執(zhí)行順序從新回到判斷節(jié)點(diǎn)判斷,并返回判斷結(jié)果!
Lower Priority
當(dāng)運(yùn)行到后續(xù)節(jié)點(diǎn)時(shí),本節(jié)點(diǎn)的判斷生效了的話則打斷當(dāng)前執(zhí)行順序,返回本節(jié)點(diǎn)執(zhí)行!
Both
包含了 上面的兩個(gè)類(lèi)型!
下面的示例將使用低優(yōu)先級(jí)的中斷類(lèi)型 Lower Priority:
在這個(gè)例子中左邊的 Sequence 的中斷類(lèi)型為 Lower Priority,假設(shè)左側(cè)分支返回錯(cuò)誤,行為樹(shù)將跳轉(zhuǎn)到右側(cè)分支!當(dāng)右側(cè)分支運(yùn)行時(shí),第一個(gè)節(jié)點(diǎn)的判斷條件變成了 success。這時(shí)因?yàn)榕袛嘟Y(jié)果發(fā)生變化并且設(shè)置了 Lower Priority,所以會(huì)打斷當(dāng)前正在執(zhí)行 Action 并返回去執(zhí)行第一個(gè) Action。 譯者:如果要自行測(cè)試上圖效果,建議把 Action 換成 wait 節(jié)點(diǎn),我在測(cè)試的時(shí)候用的 log 結(jié)果 log 太快,這個(gè) LowerPriority測(cè)試了很久也沒(méi)搞明白,原來(lái)是我的 Action 太快,導(dǎo)致行為樹(shù)結(jié)束);如下圖
條件在被檢測(cè)時(shí)會(huì)有一個(gè)圖標(biāo)來(lái)標(biāo)記,表示這個(gè)判斷節(jié)點(diǎn)當(dāng)前被檢測(cè)中,當(dāng)狀態(tài)變化后會(huì)根據(jù)打斷類(lèi)型打斷行為樹(shù)當(dāng)前的執(zhí)行順序!上圖中左下角的 Int Comparison(整形數(shù)值判斷)節(jié)點(diǎn),如果判斷返回 false 則會(huì)打斷 wait,并跳轉(zhuǎn)到后一個(gè) Sequence 隊(duì)列,如果此時(shí)判斷有變成了有效值則又會(huì)跳回來(lái)執(zhí)行第一個(gè) Wait。這是因?yàn)榈谝粋€(gè) Sequence 選擇了 Both 的打斷類(lèi)型!另外有打斷的條件節(jié)點(diǎn)可以嵌套。例如下圖!
如果按下 Fire1(is Buttondown 監(jiān)聽(tīng)的是 Fire1),則會(huì)跳回到左側(cè) Sequence 下的 Wait
Event 事件
Behavior Designer 中的 Event 事件系統(tǒng)可以讓你很容易的使用!你可以通過(guò)代碼觸發(fā)一個(gè) event 事件,也可以通過(guò)行為樹(shù)的節(jié)點(diǎn)來(lái)觸發(fā)一個(gè)事件!
這些事件可以通過(guò)行為樹(shù)的 SendEvent 節(jié)點(diǎn)和 HasRecivedEvent 節(jié)點(diǎn)來(lái)觸發(fā)和監(jiān)聽(tīng)事件!當(dāng)一個(gè)事件要被發(fā)送時(shí)使用 SendeEvnet 節(jié)點(diǎn)。HasRecivedEvent 節(jié)點(diǎn)是一個(gè)條件節(jié)點(diǎn),當(dāng)接收到注冊(cè)的事件后會(huì)返回 success。可以通過(guò)事件名稱(chēng)的定義來(lái)觸發(fā)和監(jiān)聽(tīng)一個(gè)事件!
出了通過(guò)行為樹(shù)節(jié)點(diǎn)來(lái)觸發(fā)事件,還可以通過(guò)代碼來(lái)觸發(fā)事件!BehaviorTree.SendEvent 函數(shù)就是用來(lái)干這個(gè)的:
var behaviorTree = GetComponent< BehaviorTree >(); behaviorTree.SendEvent< object >("MyEvent", Vector3.zero);上面這個(gè)例子就是通過(guò)代碼,將事件" MyEvent"發(fā)送到行為樹(shù),并帶有參數(shù)( Vector3.zero),如果行為樹(shù)中有監(jiān)聽(tīng)器,則監(jiān)聽(tīng)器位置會(huì)返回 success!
Task 的引用,任務(wù)節(jié)點(diǎn)之間的引用!
在編寫(xiě)一個(gè) Task 任務(wù)節(jié)點(diǎn)的時(shí)候可能需要訪問(wèn)另外一個(gè) Task 任務(wù)。例如 TaskA 想訪問(wèn) TaskB 的某個(gè)屬性數(shù)值!例如下面這樣的 TaskA 和 TaskB
將這兩個(gè)任務(wù)添加到行為樹(shù)編輯器中:
選中 TaskA,你會(huì)在 Inspector 面板中看到變量 referencedTask 他是個(gè) Task 類(lèi)型,這時(shí)你可以選擇 Select 按鈕進(jìn)行選擇,最終像下圖這樣
你可以通過(guò)點(diǎn)擊"X"號(hào)來(lái)取消引用關(guān)系!這樣配置后,在執(zhí)行 TaskA 的時(shí)候就會(huì)顯示 TaskB 的屬性,像下圖這樣
Task 的引用也可以是數(shù)組引用,像下面這樣!
public class TaskA : Action { public TaskB[] referencedTasks; }變量同步器 Variable Synchronizer
在 GameObject 上掛在腳本,并設(shè)置同步的源,以及同步目標(biāo),中間的箭頭表示同步方向,向右表示上面的變量同步給下面的,向左表示下面的變量同步給上面的!
Task 任務(wù)的可用屬性
HelpURL : web 連接 [HelpURL("http://www.opsive.com/assets/BehaviorDesigner/documentation.php?id=27")] public class Parallel : Composite { ////////////////////////////////////////////////////////////////////////////// TaskIcon :任務(wù)的圖標(biāo) [TaskIcon("Assets/Path/To/{SkinColor}Icon.png")] public class MyTask : Action { ////////////////////////////////////////////////////////////////////////////// TaskCategory:任務(wù)的顯示位置(在 Task 任務(wù)面板中的顯示位置) [TaskCategory("Common")] public class Seek : Action { [TaskCategory("RTS/Harvester")] public class HarvestGold : Action { ////////////////////////////////////////////////////////////////////////////// TaskDescription:功能描述的文本內(nèi)容,顯示在編輯器布局區(qū)域的左下角 [TaskDescription("The sequence task is similar to an \"and\" operation. ..."] public class Sequence : Composite { ////////////////////////////////////////////////////////////////////////////// LinkedTask:應(yīng)用其他的 Task 任務(wù) [LinkedTask] public TaskGuard[] linkedTaskGuards = null; ////////////////////////////////////////////////////////////////////////////// InheritedField : 繼承屬性 [InheritedField] public float moveSpeed; Composites (復(fù)合)節(jié)點(diǎn)
Sequence(序列)節(jié)點(diǎn)
這個(gè)節(jié)點(diǎn)是一個(gè)"和"的關(guān)系,也就是他下面的子節(jié)點(diǎn)的執(zhí)行順序是一個(gè)接著一個(gè)的!如果其中一個(gè)返回 false。那么后續(xù)的子節(jié)點(diǎn)不會(huì)被執(zhí)行,這個(gè)序列節(jié)點(diǎn)返回 false。只有當(dāng)所有子節(jié)點(diǎn)全部完成并返回 success 的時(shí)候,這個(gè) Sequence(序列)節(jié)點(diǎn)才會(huì)返回 success;
Selector(選擇)節(jié)點(diǎn)
這個(gè)節(jié)點(diǎn)是"或"的關(guān)系,也就是他下面的子節(jié)點(diǎn)的執(zhí)行順序是一個(gè)或另一個(gè)的!只有所有子節(jié)點(diǎn)返回 false 才會(huì)返回 false。只要有一個(gè)子節(jié)點(diǎn)返回 success,那么這個(gè) Selector 節(jié)點(diǎn)就會(huì)返回 success,后續(xù)的節(jié)點(diǎn)不會(huì)被執(zhí)行!
Parallel(并行)節(jié)點(diǎn)
這個(gè)節(jié)點(diǎn)類(lèi)似于 Sequence(序列)節(jié)點(diǎn)。不同的是,Parallel(并行)節(jié)點(diǎn)會(huì)在同一時(shí)間執(zhí)行所有子節(jié)點(diǎn)而不是一個(gè)一個(gè)的去執(zhí)行!
如果子節(jié)點(diǎn)中有任意一個(gè)返回 false,則停掉所有子節(jié)點(diǎn)并返回 false。只有所有子節(jié)點(diǎn)全部返回 success 的時(shí)候,才會(huì)返回 success。
Parallel Selector(并行選擇)節(jié)點(diǎn)
類(lèi)似于 Selector(選擇)節(jié)點(diǎn),ParallelSelector(并行選擇)節(jié)點(diǎn)只要有一個(gè)子節(jié)點(diǎn)返回 success,那么他就會(huì)返回 success!不同于 Selector的一點(diǎn)就是 ParalleSelector(并行選擇)節(jié)點(diǎn)會(huì)在同一時(shí)間執(zhí)行下面的所有子節(jié)點(diǎn),如果有一個(gè)節(jié)點(diǎn)返回 success,則會(huì)停止掉其他所有子節(jié)點(diǎn)并返回 success。只有當(dāng)所有子節(jié)點(diǎn)全部 false 的時(shí)候才會(huì)返回 false!
Priority Selector(優(yōu)先選擇)節(jié)點(diǎn)
類(lèi)似于 Selector(選擇)節(jié)點(diǎn),PrioritySelector(并行選擇)節(jié)點(diǎn)只要有一個(gè)子節(jié)點(diǎn)返回 success,那么他就會(huì)返回 success!不同點(diǎn)在于,子節(jié)點(diǎn)的執(zhí)行順序不是從左到右的,而是通過(guò)優(yōu)先級(jí)來(lái)確定的執(zhí)行順序!較高的優(yōu)先級(jí)的子節(jié)點(diǎn)會(huì)被先執(zhí)行!(譯者:優(yōu)先級(jí)在哪里設(shè)置的,沒(méi)有搞清楚,目前測(cè)試結(jié)果同 Selector 節(jié)點(diǎn),后來(lái)還是用我大 Google 搜索到的解決辦法!百度就是個(gè)垃圾站)需要在 Task 類(lèi)中覆蓋函數(shù),來(lái)設(shè)置不同的 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(隨機(jī)選擇)節(jié)點(diǎn)
這個(gè)節(jié)點(diǎn)的特點(diǎn)是:隨機(jī)的執(zhí)行子節(jié)點(diǎn),只要有一個(gè)子節(jié)點(diǎn)返回成功,它就會(huì)返回成功,不再執(zhí)行后續(xù)節(jié)點(diǎn)。如果所有子節(jié)點(diǎn)都返回 false 則它也返回 false!在這個(gè)節(jié)點(diǎn)的屬性面板中有:seed(隨機(jī)種子)的設(shè)置,自行使用!
Random Sequence(隨機(jī)序列)節(jié)點(diǎn)
類(lèi)似于 Sequence(序列)節(jié)點(diǎn),只是他的執(zhí)行順序是隨機(jī)的!只要遇到一個(gè)子節(jié)點(diǎn)返回 false,RandomSequence(隨機(jī)序列)就返回錯(cuò)誤,直到全部子節(jié)點(diǎn)都返回 success,它才會(huì)返回 success!在這個(gè)節(jié)點(diǎn)的屬性面板中有:seed(隨機(jī)種子)的設(shè)置,自行使用!
Selector Evaluator(重復(fù)判斷選擇)節(jié)點(diǎn)
這個(gè)節(jié)點(diǎn)每幀都會(huì)去重新評(píng)估子節(jié)點(diǎn)的執(zhí)行狀態(tài)并選擇。它會(huì)執(zhí)行子節(jié)點(diǎn)中優(yōu)先級(jí)最低的子節(jié)點(diǎn)!每幀都會(huì)這么干!如果當(dāng)前一個(gè)高優(yōu)先級(jí)的節(jié)點(diǎn)在運(yùn)行并且下一幀要執(zhí)行的子節(jié)點(diǎn)優(yōu)先級(jí)比較低,那么它會(huì)打斷高優(yōu)先級(jí)的節(jié)點(diǎn),去執(zhí)行優(yōu)先級(jí)低的子節(jié)點(diǎn)! Selector Evaluator(重復(fù)判斷選擇)節(jié)點(diǎn)會(huì)從低到高的去遍歷執(zhí)行所有子節(jié)點(diǎn),直到遇到一個(gè)返回 success 的!如果所有子節(jié)點(diǎn)都返回 false,那么它就返回 false!否則只要有一個(gè)返回 success,它就會(huì)返回 success!這個(gè)節(jié)點(diǎn)模擬了條件打斷功能,如果子節(jié)點(diǎn)沒(méi)有條件節(jié)點(diǎn)的話!
Conditionals(條件判斷)節(jié)點(diǎn)
條件節(jié)點(diǎn)的任務(wù)是判斷游戲的一些屬性,比如玩家是否活著,怪物是否在視野距離內(nèi)!
Random Probability (隨機(jī)概率)節(jié)點(diǎn)
通過(guò)設(shè)置 successProbability 屬性來(lái)控制返回 success 的幾率(默認(rèn) 0.5,也就是 50%幾率)!另外還有 seed 隨機(jī)種子的設(shè)置等!
Compare Field Value(字段比較)節(jié)點(diǎn)
比較指定的值的字段值。 返回成功如果值是相同的。
Has Received Event(是否接收到事件)
(譯者:還有很多條件節(jié)點(diǎn),這里就忽略了!)
Decorators(修飾器)節(jié)點(diǎn)
這種節(jié)點(diǎn)的功能是用來(lái)包裝另一個(gè)節(jié)點(diǎn)!(只能有一個(gè)子節(jié)點(diǎn))。Decorators(修飾節(jié)點(diǎn))將改變節(jié)點(diǎn)的行為!例如:修飾節(jié)點(diǎn)可以再運(yùn)行時(shí)控制子節(jié)點(diǎn)直到返回某個(gè)特定狀態(tài)(success 或者是 false)。后者是對(duì)子節(jié)點(diǎn)返回結(jié)果取反(即:success 返回 false,false,返回 success);下面來(lái)一一介 BehaviorDesigner 默認(rèn)自帶的幾個(gè) Decorator(裝飾器節(jié)點(diǎn))
Conditional Evaluator (條件節(jié)點(diǎn)的評(píng)估) 裝飾節(jié)點(diǎn)
參數(shù)設(shè)置:
1:reevaluate :條件節(jié)點(diǎn)是否需要每幀都重新評(píng)估一次
2:conditionalTask:要被評(píng)估的條件節(jié)點(diǎn),注意:這個(gè)節(jié)點(diǎn)本身就是個(gè)條件節(jié)點(diǎn)!
對(duì)設(shè)置的條件節(jié)點(diǎn)進(jìn)行評(píng)估,如果條件節(jié)點(diǎn)返回 success,那么運(yùn)行子節(jié)點(diǎn)并返回子節(jié)點(diǎn)的運(yùn)行結(jié)果!如果條件節(jié)點(diǎn)沒(méi)有返回 success那么子節(jié)點(diǎn)不會(huì)被運(yùn)行,并且立刻返回 failure!條件節(jié)點(diǎn)只會(huì)在開(kāi)始運(yùn)行的時(shí)候被評(píng)估一次!
Interrupt(打斷)裝飾節(jié)點(diǎn)
如果打斷節(jié)點(diǎn)被觸發(fā),則打斷下面的所有子節(jié)點(diǎn)任務(wù)的執(zhí)行!打斷命令可以被 Perform interruption(執(zhí)行打斷)節(jié)點(diǎn)發(fā)起!打斷節(jié)點(diǎn)在收到打斷命令前,不會(huì)打斷他下面的子節(jié)點(diǎn)的執(zhí)行狀態(tài)!如果子節(jié)點(diǎn)執(zhí)行完畢還沒(méi)有收到打斷命令,則直接返回子節(jié)點(diǎn)的執(zhí)行結(jié)果!例如下圖這樣:
Inverter(取反)裝飾節(jié)點(diǎn)
子節(jié)點(diǎn)的任務(wù)完成后返回值,在這個(gè)節(jié)點(diǎn)會(huì)被取反并傳遞到上一級(jí)中!
Repeater(重復(fù)/循環(huán))裝飾節(jié)點(diǎn)
有三個(gè)屬性設(shè)置:執(zhí)行次數(shù),是否一直重復(fù),運(yùn)行直到返回錯(cuò)誤!
Return Failure (返回失敗)裝飾節(jié)點(diǎn)
只要子節(jié)點(diǎn)當(dāng)前的狀態(tài)不是 running,也就是子節(jié)點(diǎn)執(zhí)行結(jié)果無(wú)論是 success 還是 failure,都返回 failure!如果子節(jié)點(diǎn)狀態(tài)是 running的話則返回 running!
Return Success(返回正確)裝飾節(jié)點(diǎn)
只要子節(jié)點(diǎn)當(dāng)前的狀態(tài)不是 running,也就是子節(jié)點(diǎn)執(zhí)行結(jié)果無(wú)論是 success 還是 failure,都返回 success!如果子節(jié)點(diǎn)狀態(tài)是 running的話則返回 running!
Task Guard(任務(wù)守衛(wèi))裝飾節(jié)點(diǎn)
類(lèi)似于多線程互斥操作中使用的 Lock 標(biāo)記,為了避免公共數(shù)據(jù)被多次引用!下圖以外部行為樹(shù)為例進(jìn)行演示!這里使用并行觸發(fā)兩個(gè)外部行為樹(shù),如果不加上 TaskGuard,那么兩邊都會(huì)去并行執(zhí)行外部行為樹(shù),現(xiàn)在加上 TaskGuard 后同一時(shí)間只能執(zhí)行一個(gè),而另一個(gè)要等待執(zhí)行完畢才能執(zhí)行
上圖 TaskGard 配置如下圖:
Until Failure(直到失敗)裝飾節(jié)點(diǎn)
直到子節(jié)點(diǎn)返回 failure,否則一直循環(huán)執(zhí)行子節(jié)點(diǎn), 如下圖:
Until Success(直到成功)裝飾節(jié)點(diǎn)
直到子節(jié)點(diǎn)返回 success,否則一直循環(huán)執(zhí)行子節(jié)點(diǎn)!
轉(zhuǎn)自:http://www.jianshu.com/p/64b5fe01fb1c
總結(jié)
以上是生活随笔為你收集整理的Behavior Designer 中文版教程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 双路由器设置经验:无线路由器接房东的网线
- 下一篇: GraphPad Prism 列联表教程