资讯专栏INFORMATION COLUMN

游戏AI(三)—行为树优化之基于事件的行为树

vvpvvp / 1501人阅读

摘要:上一篇我们讲到了关于行为树的内存优化,这一篇我们将讲述行为树的另一种优化方法基于事件的行为树。而函数负责将行为压入队列首端,节点则负责设置行为执行状态并显示调用监察函数。

上一篇我们讲到了关于行为树的内存优化,这一篇我们将讲述行为树的另一种优化方法——基于事件的行为树。

问题

在之前的行为树中,我们每帧都要从根节点开始遍历行为树,而目的仅仅是为了得到最近激活的节点,既然如此,为什么我们不多带带维护一个保存这些行为的列表,以方便快速访问呢。我们可以把这个列表叫做调度器,用来保存已经激活的行为,并在必要时更新他们。

解决办法

我们不再每帧都从根节点去遍历行为树,而是维护一个调度器负责保存已激活的节点,当正在执行的行为终止时,由其父节点决定接下来的行为。

监察函数

为了实现基于事件的驱动,我们必须要有一个监察函数,当行为终止时,我们通过执行监察函数通知父节点并让父节点做出相应处理,这里我们通过C++标准库中的std::funcion实现监察函数
using BehaviorObserver = std::function;

行为调度器

调度器负责管理基于事件的行为树的核心代码,负责对所有需要更新的行为进行集中式管理,不允许复合行为自主管理和运行自己的子节点。。。这里我们将调度器整合进了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::deque Behaviors;
        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;
    //执行行为更新
    Current->Tick();
    //如果该任务被终止则执行监察函数
    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);
    }
}

我们通过一个双端队列保存已激活行为,在更新时从首端去走哦偶行为,再将需要更新的行为压入队列尾端。当发现任务终止时,执行其监察函数。
而Start()函数负责将行为压入队列首端,Stop()节点则负责设置行为执行状态并显示调用监察函数。

事件驱动的复合节点

大部分动作和条件代码并不受事件驱动方式的影响。而复合节点则是受事件驱动影响最明显的节点。复合节点不再自己更新和管理子节点,而是通过向调度器提出请求以更新子节点。这里我们以Sequence节点为例。
/顺序器:依次执行所有节点直到其中一个失败或者全部成功位置

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;
    //当当前子节点执行失败时,顺序器失败
    if (child->IsFailuer())
    {
        m_pBehaviorTree->Stop(this, EStatus::Failure);
        return;
    }
    
    assert(child->GetStatus() == EStatus::Success);
    //当前子节点执行成功时,判断是否执行到数组尾部
    if (++CurrChild == Children.end())
    {
        Tree->Stop(this, EStatus::Success);
    }
    //调度下一个子节点
    else
    {
        BehaviorObserver observer = std::bind(&Sequence::OnChildComplete, this, std::placeholders::_1);
        Tree->Start(*CurrChild, &observer);
    }
}

因为现在各节点由调度器统一管理,所以Update函数不再需要。我们在OnIntialize()函数中设置需要更新的首个节点,并将OnChildComplete作为其监察函数。在OnchildComplete函数中实现后续子节点的更新。

总结

通过基于事件的方式,我们可以在行为树执行时节省大量的函数调用,对其性能无疑是一次巨大的提升。
github连接

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/19684.html

相关文章

  • 游戏人工智能 读书笔记 (四) AI算法简介——Ad-Hoc 行为编程

    摘要:原文链接本文内容包含以下章节本书英文版这个章节主要讨论了在游戏中经常用到的一些基础的人工智能算法。行为树是把的图转变成为一颗树结构。根据当前游戏的环境状态得到某一个行为的效用值。 作者:苏博览商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。原文链接:https://wetest.qq.com/lab/view/427.html 本文内容包含以下章节: Chapter 2 ...

    xinhaip 评论0 收藏0
  • 游戏AI行为研究及实现

    摘要:另外,当并行器满足条件提前退出时,所有正在执行的子行为也应该立即被终止,我们在函数中调用每个子节点的终止方法监视器监视器是并行器的应用之一,通过在行为运行过程中不断检查是否满足某条件,如果不满足则立刻退出。将条件放在并行器的尾部即可。 从上古卷轴中形形色色的人物,到NBA2K中挥洒汗水的球员,从使命召唤中诡计多端的敌人,到刺客信条中栩栩如生的人群。游戏AI几乎存在于游戏中的每个角落,默...

    Harriet666 评论0 收藏0
  • 游戏人工智能 读书笔记 (游戏和人工智能相互影响

    摘要:从游戏界的角度来说人工智能技术的发展可以为游戏带来什么改变和收益。使用人工智能技术可以给游戏带来更多更好的内容,也可以减轻游戏开发的成本。 作者:苏博览,腾讯互动娱乐高级研究员商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。原文链接:https://wetest.qq.com/lab/view/412.html 本文内容包含以下章节: Chapter 1.3 Why Ga...

    CrazyCodes 评论0 收藏0
  • 深入了解机器学习

    摘要:监督大多数情况下,机器学习依赖于被标记为真或假的数据。回归学习回归学习是最重要和广泛使用的机器学习和统计工具之一。风险规避机器学习给企业提供了防止诈骗者陷入困境并减轻潜在货币和监管复杂化的能力。 摘要: 了解机器学习发展史、机器学习是什么?机器学习有什么?看看本文就够了。 如今机器学习已经成为了这个时代的热门话题。机器学习已经存在了几十年,但直到最近我们才得以利用这项技术。 接下来,让...

    caikeal 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<