摘要:上一篇我們講到了關(guān)于行為樹的內(nèi)存優(yōu)化,這一篇我們將講述行為樹的另一種優(yōu)化方法基于事件的行為樹。而函數(shù)負(fù)責(zé)將行為壓入隊列首端,節(jié)點則負(fù)責(zé)設(shè)置行為執(zhí)行狀態(tài)并顯示調(diào)用監(jiān)察函數(shù)。
上一篇我們講到了關(guān)于行為樹的內(nèi)存優(yōu)化,這一篇我們將講述行為樹的另一種優(yōu)化方法——基于事件的行為樹。
問題在之前的行為樹中,我們每幀都要從根節(jié)點開始遍歷行為樹,而目的僅僅是為了得到最近激活的節(jié)點,既然如此,為什么我們不多帶帶維護一個保存這些行為的列表,以方便快速訪問呢。我們可以把這個列表叫做調(diào)度器,用來保存已經(jīng)激活的行為,并在必要時更新他們。
解決辦法我們不再每幀都從根節(jié)點去遍歷行為樹,而是維護一個調(diào)度器負(fù)責(zé)保存已激活的節(jié)點,當(dāng)正在執(zhí)行的行為終止時,由其父節(jié)點決定接下來的行為。
監(jiān)察函數(shù)為了實現(xiàn)基于事件的驅(qū)動,我們必須要有一個監(jiān)察函數(shù),當(dāng)行為終止時,我們通過執(zhí)行監(jiān)察函數(shù)通知父節(jié)點并讓父節(jié)點做出相應(yīng)處理,這里我們通過C++標(biāo)準(zhǔn)庫中的std::funcion實現(xiàn)監(jiān)察函數(shù)
using BehaviorObserver = std::function
調(diào)度器負(fù)責(zé)管理基于事件的行為樹的核心代碼,負(fù)責(zé)對所有需要更新的行為進行集中式管理,不允許復(fù)合行為自主管理和運行自己的子節(jié)點。。。這里我們將調(diào)度器整合進了BehvaiorTree類。當(dāng)然也可以弄個多帶帶的類進行管理。
class BehaviorTree { public: BehaviorTree(Behavior* InRoot) :Root(InRoot) {} void Tick(); bool Step(); void Start(Behavior* Bh,BehaviorObserver* Observe); void Stop(Behavior* Bh,EStatus Result); private: //已激活行為列表 std::dequeBehaviors; Behavior* Root; }; void BehaviorTree::Tick() { //將更新結(jié)束標(biāo)記插入任務(wù)列表 Behaviors.push_back(nullptr); while (Step()) { } } bool BehaviorTree :: Step() { Behavior* Current = Behaviors.front(); Behaviors.pop_front(); //如果遇到更新結(jié)束標(biāo)記則停止 if (Current == nullptr) return false; //執(zhí)行行為更新 Current->Tick(); //如果該任務(wù)被終止則執(zhí)行監(jiān)察函數(shù) if (Current->IsTerminate() && Current->Observer) { Current->Observer(Current->GetStatus()); } //否則將其插入隊列等待下次tick處理 else { Behaviors.push_back(Current); } } void BehaviorTree::Start(Behavior* Bh, BehaviorObserver* Observe) { if (Observe) { Bh->Observer = *Observe; } Behaviors.push_front(Bh); } void BehaviorTree::Stop(Behavior* Bh, EStatus Result) { assert(Result != EStatus::Running); Bh->SetStatus(Result); if (Bh->Observer) { Bh->Observer(Result); } }
我們通過一個雙端隊列保存已激活行為,在更新時從首端去走哦偶行為,再將需要更新的行為壓入隊列尾端。當(dāng)發(fā)現(xiàn)任務(wù)終止時,執(zhí)行其監(jiān)察函數(shù)。
而Start()函數(shù)負(fù)責(zé)將行為壓入隊列首端,Stop()節(jié)點則負(fù)責(zé)設(shè)置行為執(zhí)行狀態(tài)并顯示調(diào)用監(jiān)察函數(shù)。
大部分動作和條件代碼并不受事件驅(qū)動方式的影響。而復(fù)合節(jié)點則是受事件驅(qū)動影響最明顯的節(jié)點。復(fù)合節(jié)點不再自己更新和管理子節(jié)點,而是通過向調(diào)度器提出請求以更新子節(jié)點。這里我們以Sequence節(jié)點為例。
/順序器:依次執(zhí)行所有節(jié)點直到其中一個失敗或者全部成功位置
class Sequence :public Composite { public: virtual std::string Name() override { return "Sequence"; } static Behavior* Create() { return new Sequence(); } void OnChildComplete(EStatus Status); protected: virtual void OnInitialize() override; protected: Behaviors::iterator CurrChild; BehaviorTree* m_pBehaviorTree; };
void Sequence::OnInitialize() { CurrChild = Children.begin(); BehaviorObserver observer = std::bind(&Sequence::OnChildComplete, this, std::placeholders::_1); Tree->Start(*CurrChild, &observer); } void Sequence::OnChildComplete(EStatus Status) { Behavior* child = *CurrChild; //當(dāng)當(dāng)前子節(jié)點執(zhí)行失敗時,順序器失敗 if (child->IsFailuer()) { m_pBehaviorTree->Stop(this, EStatus::Failure); return; } assert(child->GetStatus() == EStatus::Success); //當(dāng)前子節(jié)點執(zhí)行成功時,判斷是否執(zhí)行到數(shù)組尾部 if (++CurrChild == Children.end()) { Tree->Stop(this, EStatus::Success); } //調(diào)度下一個子節(jié)點 else { BehaviorObserver observer = std::bind(&Sequence::OnChildComplete, this, std::placeholders::_1); Tree->Start(*CurrChild, &observer); } }
因為現(xiàn)在各節(jié)點由調(diào)度器統(tǒng)一管理,所以Update函數(shù)不再需要。我們在OnIntialize()函數(shù)中設(shè)置需要更新的首個節(jié)點,并將OnChildComplete作為其監(jiān)察函數(shù)。在OnchildComplete函數(shù)中實現(xiàn)后續(xù)子節(jié)點的更新。
總結(jié)通過基于事件的方式,我們可以在行為樹執(zhí)行時節(jié)省大量的函數(shù)調(diào)用,對其性能無疑是一次巨大的提升。
github連接
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/19684.html
摘要:原文鏈接本文內(nèi)容包含以下章節(jié)本書英文版這個章節(jié)主要討論了在游戲中經(jīng)常用到的一些基礎(chǔ)的人工智能算法。行為樹是把的圖轉(zhuǎn)變成為一顆樹結(jié)構(gòu)。根據(jù)當(dāng)前游戲的環(huán)境狀態(tài)得到某一個行為的效用值。 作者:蘇博覽商業(yè)轉(zhuǎn)載請聯(lián)系騰訊WeTest獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。原文鏈接:https://wetest.qq.com/lab/view/427.html 本文內(nèi)容包含以下章節(jié): Chapter 2 ...
摘要:另外,當(dāng)并行器滿足條件提前退出時,所有正在執(zhí)行的子行為也應(yīng)該立即被終止,我們在函數(shù)中調(diào)用每個子節(jié)點的終止方法監(jiān)視器監(jiān)視器是并行器的應(yīng)用之一,通過在行為運行過程中不斷檢查是否滿足某條件,如果不滿足則立刻退出。將條件放在并行器的尾部即可。 從上古卷軸中形形色色的人物,到NBA2K中揮灑汗水的球員,從使命召喚中詭計多端的敵人,到刺客信條中栩栩如生的人群。游戲AI幾乎存在于游戲中的每個角落,默...
摘要:從游戲界的角度來說人工智能技術(shù)的發(fā)展可以為游戲帶來什么改變和收益。使用人工智能技術(shù)可以給游戲帶來更多更好的內(nèi)容,也可以減輕游戲開發(fā)的成本。 作者:蘇博覽,騰訊互動娛樂高級研究員商業(yè)轉(zhuǎn)載請聯(lián)系騰訊WeTest獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。原文鏈接:https://wetest.qq.com/lab/view/412.html 本文內(nèi)容包含以下章節(jié): Chapter 1.3 Why Ga...
摘要:監(jiān)督大多數(shù)情況下,機器學(xué)習(xí)依賴于被標(biāo)記為真或假的數(shù)據(jù)?;貧w學(xué)習(xí)回歸學(xué)習(xí)是最重要和廣泛使用的機器學(xué)習(xí)和統(tǒng)計工具之一。風(fēng)險規(guī)避機器學(xué)習(xí)給企業(yè)提供了防止詐騙者陷入困境并減輕潛在貨幣和監(jiān)管復(fù)雜化的能力。 摘要: 了解機器學(xué)習(xí)發(fā)展史、機器學(xué)習(xí)是什么?機器學(xué)習(xí)有什么?看看本文就夠了。 如今機器學(xué)習(xí)已經(jīng)成為了這個時代的熱門話題。機器學(xué)習(xí)已經(jīng)存在了幾十年,但直到最近我們才得以利用這項技術(shù)。 接下來,讓...
閱讀 980·2023-04-25 18:51
閱讀 1965·2021-09-09 11:39
閱讀 3347·2019-08-30 15:53
閱讀 2148·2019-08-30 13:03
閱讀 1363·2019-08-29 16:17
閱讀 648·2019-08-29 11:33
閱讀 1951·2019-08-26 14:00
閱讀 2178·2019-08-26 13:41