摘要:上一篇我們講到了關于行為樹的內(nèi)存優(yōu)化,這一篇我們將講述行為樹的另一種優(yōu)化方法基于事件的行為樹。而函數(shù)負責將行為壓入隊列首端,節(jié)點則負責設置行為執(zhí)行狀態(tài)并顯示調(diào)用監(jiān)察函數(shù)。
上一篇我們講到了關于行為樹的內(nèi)存優(yōu)化,這一篇我們將講述行為樹的另一種優(yōu)化方法——基于事件的行為樹。
問題在之前的行為樹中,我們每幀都要從根節(jié)點開始遍歷行為樹,而目的僅僅是為了得到最近激活的節(jié)點,既然如此,為什么我們不多帶帶維護一個保存這些行為的列表,以方便快速訪問呢。我們可以把這個列表叫做調(diào)度器,用來保存已經(jīng)激活的行為,并在必要時更新他們。
解決辦法我們不再每幀都從根節(jié)點去遍歷行為樹,而是維護一個調(diào)度器負責保存已激活的節(jié)點,當正在執(zhí)行的行為終止時,由其父節(jié)點決定接下來的行為。
監(jiān)察函數(shù)為了實現(xiàn)基于事件的驅動,我們必須要有一個監(jiān)察函數(shù),當行為終止時,我們通過執(zhí)行監(jiān)察函數(shù)通知父節(jié)點并讓父節(jié)點做出相應處理,這里我們通過C++標準庫中的std::funcion實現(xiàn)監(jiān)察函數(shù)
using BehaviorObserver = std::function
調(diào)度器負責管理基于事件的行為樹的核心代碼,負責對所有需要更新的行為進行集中式管理,不允許復合行為自主管理和運行自己的子節(jié)點。。。這里我們將調(diào)度器整合進了BehvaiorTree類。當然也可以弄個多帶帶的類進行管理。
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() { //將更新結束標記插入任務列表 Behaviors.push_back(nullptr); while (Step()) { } } bool BehaviorTree :: Step() { Behavior* Current = Behaviors.front(); Behaviors.pop_front(); //如果遇到更新結束標記則停止 if (Current == nullptr) return false; //執(zhí)行行為更新 Current->Tick(); //如果該任務被終止則執(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); } }
我們通過一個雙端隊列保存已激活行為,在更新時從首端去走哦偶行為,再將需要更新的行為壓入隊列尾端。當發(fā)現(xiàn)任務終止時,執(zhí)行其監(jiān)察函數(shù)。
而Start()函數(shù)負責將行為壓入隊列首端,Stop()節(jié)點則負責設置行為執(zhí)行狀態(tài)并顯示調(diào)用監(jiān)察函數(shù)。
大部分動作和條件代碼并不受事件驅動方式的影響。而復合節(jié)點則是受事件驅動影響最明顯的節(jié)點。復合節(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; //當當前子節(jié)點執(zhí)行失敗時,順序器失敗 if (child->IsFailuer()) { m_pBehaviorTree->Stop(this, EStatus::Failure); return; } assert(child->GetStatus() == EStatus::Success); //當前子節(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ù)中設置需要更新的首個節(jié)點,并將OnChildComplete作為其監(jiān)察函數(shù)。在OnchildComplete函數(shù)中實現(xiàn)后續(xù)子節(jié)點的更新。
總結通過基于事件的方式,我們可以在行為樹執(zhí)行時節(jié)省大量的函數(shù)調(diào)用,對其性能無疑是一次巨大的提升。
github連接
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/19684.html
摘要:原文鏈接本文內(nèi)容包含以下章節(jié)本書英文版這個章節(jié)主要討論了在游戲中經(jīng)常用到的一些基礎的人工智能算法。行為樹是把的圖轉變成為一顆樹結構。根據(jù)當前游戲的環(huán)境狀態(tài)得到某一個行為的效用值。 作者:蘇博覽商業(yè)轉載請聯(lián)系騰訊WeTest獲得授權,非商業(yè)轉載請注明出處。原文鏈接:https://wetest.qq.com/lab/view/427.html 本文內(nèi)容包含以下章節(jié): Chapter 2 ...
摘要:另外,當并行器滿足條件提前退出時,所有正在執(zhí)行的子行為也應該立即被終止,我們在函數(shù)中調(diào)用每個子節(jié)點的終止方法監(jiān)視器監(jiān)視器是并行器的應用之一,通過在行為運行過程中不斷檢查是否滿足某條件,如果不滿足則立刻退出。將條件放在并行器的尾部即可。 從上古卷軸中形形色色的人物,到NBA2K中揮灑汗水的球員,從使命召喚中詭計多端的敵人,到刺客信條中栩栩如生的人群。游戲AI幾乎存在于游戲中的每個角落,默...
摘要:從游戲界的角度來說人工智能技術的發(fā)展可以為游戲帶來什么改變和收益。使用人工智能技術可以給游戲帶來更多更好的內(nèi)容,也可以減輕游戲開發(fā)的成本。 作者:蘇博覽,騰訊互動娛樂高級研究員商業(yè)轉載請聯(lián)系騰訊WeTest獲得授權,非商業(yè)轉載請注明出處。原文鏈接:https://wetest.qq.com/lab/view/412.html 本文內(nèi)容包含以下章節(jié): Chapter 1.3 Why Ga...
閱讀 921·2023-04-25 18:51
閱讀 1875·2021-09-09 11:39
閱讀 3285·2019-08-30 15:53
閱讀 2104·2019-08-30 13:03
閱讀 1314·2019-08-29 16:17
閱讀 587·2019-08-29 11:33
閱讀 1888·2019-08-26 14:00
閱讀 2126·2019-08-26 13:41