精华内容
下载资源
问答
  • 行为树

    千次阅读 2017-09-14 10:04:46
    最近打算好好研究一下行为树,在使用行为树之前,我们应该先理解行为树的基本概念和相关的逻辑,然后我们就Unity3D平台下的行为树插件的使用来进行学习行为树。 什么是行为树 如果了解过状态机,会知道在行为树...

    最近打算好好研究一下行为树,在使用行为树之前,我们应该先理解行为树的基本概念和相关的逻辑,然后我们就Unity3D平台下的行为树插件的使用来进行学习行为树。

    什么是行为树

    如果了解过状态机,会知道在行为树之前,在实现AI用得比较多的技术是状态机,状态机理解起来是比较简单的,即一个状态过渡到另一个状态,通过判断将角色的状态改变即可,如果学习过Unity的Mecanim动画系统,会更加直观的理解。

    但是状态机在状态较多的情况下会使状态之间的切换变得异常繁琐,同时状态之间很难复用。

    在这种情况下,行为树被发明出来,行为树的优点如下:

    1. 行为树提供大量的流程控制方法,使得状态之间的改变更加直观;
    2. 整个游戏AI使用树型结构,方便查看与编辑;
    3. 方便调试和代码编写;
    4. 最重要的:行为树方便制作编辑器,可以交由策划人员使用;

    行为树相关资料

    这里给出我知道的行为树相关的资料,大家有更好的资料希望可以告诉我:

    AI分享站

    腾讯游戏行为树框架:支持C++、C#(Unity3D)

    其中,腾讯的开源项目提供了C#编写的行为树编辑器源码和不错的相关文档(中文的哦),非常具有学习价值。

    行为树原理

    行为树是一种树形结构,所以其可以分成3种节点类型:

    (画丑了点,见谅...)

    1. 红色的节点:根节点,没有父节点的节点;
    2. 蓝色的节点:组合节点,有父节点和子节点的节点;
    3. 白色的节点:叶节点,没有子节点的节点;

    而编号为深度优先访问的顺序;

    节点的返回

    每个节点都会有一个返回值,可能出现的返回值有3个,如下:

    • 运行中:表示当前节点还在运行中,下一次调用行为树时任然运行当前节点;
    • 失败:表示当前节点运行失败;
    • 成功:表示当前节点运行成功;

    下面我们来细说一下这几个节点;

    根节点

    行为树的入口节点,可以是任意类型的节点;

    组合节点

    行为树的组合节点是由下面几种类型来组成的:

    选择节点/优先选择节点(Selector)

    该节点会从左到右的依次执行其子节点,只要子节点返回“失败”,就继续执行后面的节点,直到有一个节点返回“运行中”或“成功”时,会停止后续节点的运行,并且向父节点返回“运行中”或“成功”,如果所有子节点都返回“失败”则向父节点返回“失败”。

    随机选择节点(Random Selector)

    之前的选择节点是有优先级顺序的,而随机选择节点的执行顺序是随机的。但每个节点只会执行一次,比如包含子节点:A、B、C、D、E;使用随机选择节点,执行顺序可能是:D、E、A、C、B或其他组合。其它规则同选择节点一致。

    顺序节点(Sequence)

    该节点会从左到右的依次执行其子节点,只要子节点返回“成功”,就继续执行后面的节点,直到有一个节点返回“运行中”或“失败”时,会停止后续节点的运行,并且向父节点返回“运行中”或“失败”,如果所有子节点都返回“成功”则向父节点返回“成功”。

    修饰节点(Decorator)

    修饰节点只包含一个子节点,用来以某种方式来改变这个子节点的行为。

    修饰节点的类型比较多,这里我们说一些比较常见的修饰节点:

    Until Success和Until Failure

    循环执行子节点,直到返回“成功”或“失败”为止。

    比如Until Success在子节点返回“运行中”和“失败”时都会向父节点返回“运行中”,返回“成功”时向父节点返回“成功”。

    Until Failure在子节点返回“运行中”和“成功”时都会向父节点返回“运行中”,返回“失败”时向父节点返回“成功”。

    Limit

    执行子节点一定次数后强制返回“失败”。

    当子节点运行指定次数后还没有返回“失败”则该节点向父节点返回失败。

    Timer

    子节点不会立即执行,而会在指定的时间到达后才开始执行。

    TimeLimit

    指定子节点的最长运行时间,如果子节点在指定时间到达后还在运行则强制返回“失败”。

    Invert

    对子节点的返回结果取“非”,即子节点返回“成功”则该节点返回“失败”,子节点返回“失败”则该节点返回成功。

    并行节点(Parallel)

    不同于选择和顺序节点依次执行每个节点,并行节点是“同时”执行所有的节点,然后根据所有节点的返回值判断最终返回的结果。

    这里的“同时”会迷惑住不少人,实际上,行为树是运行在单一线程上的,并不会在并行节点上开多个线程来进行真正的同时执行,那么“同时”的含义是什么?

    我们知道选择或顺序节点会依次执行所有的子节点,当子节点返回“成功”或“失败”后就会停止后续节点的执行,而并行节点也会依次执行所有的子节点,无论子节点返回“成功”或“失败”都会继续运行后续节点,保证所有子节点都得到运行后在根据每个子节点的返回值来确定最终的返回结果。

    并行节点一般可以设定退出该节点的条件,比如:

    • 当全部节点都返回成功时退出;
    • 当某一个节点返回成功时退出;
    • 当全部节点都返回成功或失败时退出;
    • 当某一个节点返回成功或失败时退出;
    • 当全部节点都返回失败时退出;
    • 当某一个节点返回失败时退出;

    叶节点

    条件节点(Condition)

    条件节点可以理解为一个if判断语句,当条件的测试结果为true时向父节点传递success,结果为false时向父节点传递failure;

    该节点搭配一些组合节点可以完成各种判断跳转,比如搭配顺序节点,可以做出“是否看见敌人”->“向敌人开火”的AI;

    行为节点(Action)

    行为节点用来完成具体的操作,比如,移动到目标点,执行开火等代码逻辑,多种情况下行为节点会返回running和success;

    行为节点也可能会使用多帧来完成;

    子树的复用

    我们设计好的行为树可以在其他树中作为一颗子树来进行使用,最大可能的复用子树可以减少开发量。

    行为树和状态机的选择

    行为树并不能完全的替代状态机,两者都可以用来处理AI编写问题,但行为树是“轮询”机制,而状态机是“事件”机制,在最终使用之前一定要好好权衡和选择才行。

    天道酬勤,功不唐捐!
    展开全文
  • 行为树插件

    2018-06-22 16:21:59
    行为树插件,行为树插件,行为树插件,重要的事情说三遍
  • #概述BehaviorTree组件编辑器是Egret Pro为用户提供的一...#打开行为树首次打开行为树编辑器,使用鼠标右键在资源视窗新建一个行为树资源,双击资源视窗中的行为树资源图标:打开行为树编辑器后,您将看到三个主体部...

    #概述

    BehaviorTree组件编辑器是Egret Pro为用户提供的一个直观、可视化AI行为树编辑器,可以创建复杂的AI行为并且无需编写代码。使用行为树编辑器,您不需要知道基础行为树的实现机制,只需了解例如动作,复合,条件和装饰器等基本任务类型即可。

    #打开行为树

    首次打开行为树编辑器,使用鼠标右键在资源视窗新建一个行为树资源,双击资源视窗中的行为树资源图标:

    打开行为树编辑器后,您将看到三个主体部分:

    ①菜单列表:您可以选择Egret Pro中内置的节点类型、用户新建的节点类型添加至您的AI行为树中。另外,当您编辑出错时,此处也会有错误栏提示您修正错误。

    ②可视化编辑区域:您可以在这里编辑行为树的结构、增添行为节点或是分支。

    ③属性编辑区域:当您选中某一节点时,节点的属性会在右侧呈可编辑的状态列出,您可以通过Inspector视窗修改任意节点属性,就像您在Egret Pro的Inspector视窗编辑实体组件属性一样。

    #什么是行为树?

    #行为树还是状态机?

    在早期游戏开发中,有限状态机被广泛使用,它包含了有限个的状态和状态间的转换条件,符合我们对AI直观的认知。随着时代更替,游戏中的AI被赋予了更多的期望与要求,而有限状态机的缺点开始愈发致命——它随着状态和转换条件的增多而变得错综复杂,十分难以拓展,并且各个状态之间高度耦合,无法复用。因此,多数游戏开发者选择行为树来构建游戏AI。

    #行为树原理

    行为树被广泛用于游戏中的AI决策。由于游戏中的AI大多数都是按照规则设定好的,对于AI行为的判断类似一颗树一样,有很多判断分支。行为树的行为都是在叶节点上,是真正AI要执行的行为逻辑,每个判断分支从上至下,执行到叶节点,把最终判断返回成功的行为节点逻辑执行,从而实现决策,这就是行为树的基本原理。

    值得注意的是,并不是每个节点都有行为逻辑,仅叶节点才是我们真正通过行为树决策出来的结果,您可以通过添加叶节点定义AI行为逻辑。

    所有行为树都有一个起始的入口节点EntryTask,此节点不可选中更改,需要有且仅有一个子节点分支来才可以执行整个行为树逻辑。

    #基本节点类型

    #Action 行为节点

    行为节点时最普通的任务节点,它是唯一执行行为的节点。

    例如:攻击 巡逻 拾取道具等等

    #Composites 复合任务节点

    复合节点用来控制树的遍历方式,每种组合节点的遍历方式都不相同。

    1.选择节点

    选择节点(Select),遍历方式为从左到右依次执行所有子节点,只要节点返回 Fail,就继续执行后续节点,直到一个节点返回Success为止,停止执行后续节点。如果有一个节点返回Success 则向父节点返回Success 。否则向父节点返回 Fail。

    2.顺序节点

    顺序节点(Sequence),它从左向右依次执行所有节点,只要节点返回Success,就继续执行后续节点,当一个节点返回Fail 时,停止执行后续节点。向父节点返回 Fail ,只有当所有节点都返回 Success 时,才向父节点返回 Success。

    3.并行节点

    并行节点(Parallel)有 N 个节点,每次执行所有节点,直到一个节点返回 Fail 或者全部返回 Success为止,此时并行节点向父节点返回 Fail 或者 Success,并终止执行其他所有节点 。

    #Conditionals 条件任务节点

    条件任务节点:只有满足了条件才返回Success的节点,例如内置的随机节点。

    #Decorators 修饰任务节点

    修饰节点(Decorator) 为对子节点进行修饰,

    修饰节点常用的几个类型如下:Inverter 对子节点执行结果取反

    Repeater 重复执行子节点 N 次

    Return Failure 执行到此节点时返回失败

    Return Success 执行到此节点时返回成功

    Unitl Failure 直到失败,一直执行子节点

    Until Success 直到成功,一直执行子节点

    #制作一个简易AI行为树

    为了更好的理解行为树工作流,我们将利用行为树制作一个随机输出log消息的简易AI。

    首先您打开行为树编辑器以后,点击左侧创建节点列表 复合节点列表下的 Select选择节点,可以看到屏幕中央生成一个选择节点。

    点击拖动节点下方的小方块,拖拽一下,您会发现拖拽出了一条曲线,我们将根据曲线来确定节点和节点之间的父子关系。

    拖拽时,您会发现可以链接节点上方小方块的区域呈高亮,这代表着这个节点可以被建立关系,将曲线另一端拖拽至高亮区域,松开鼠标,您就可以构建两个节点的父子关系。

    成功建立关系后,发现 节点右侧 以及窗口左侧 会有错误提示,这代表着Selector是一个复合节点,必须要有子节点才行为树逻辑才可以运行。

    我们将行为树依据上述操作,构建成下方图示:

    温馨提示:您在构建树时,可以利用鼠标中键拖拽整个树编辑窗口,利用滚轮来缩放树编辑窗口的视野。节点也支持框选移动。

    选中LogAction节点,右侧Inspetor可以编辑节点的属性。

    将两个log Action的log属性分别设置为1111和2222。

    点击上方的save按钮进行保存,每次使用行为树都要利用这个按钮进行保存。

    回到场景 选中一个物体,添加组件 -> @egret -> 行为树组件

    添加成功后将组件的behaviour设置为刚刚编辑的newBehaviorTree

    保存后运行项目。

    大功告成!

    #自定义节点

    您肯定不会满足于只用Egret Pro有限的内置的节点来完成一些简单的游戏行为树AI,显然您学会自定义节点来实现的AI逻辑是至关重要的。下面的内容您将学会 如何新建节点 以及 完成一个简易怪物AI。

    #节点生命周期

    每个节点都有调用生命周期,

    展开全文
  • 行为树代码

    2019-04-27 16:30:39
    此代码是一个完整的例子,行为树的具体实现,可解码运行
  • 行为树概念行为树(behavior tree)的概念最早来源halo这款游戏里的ai控制结构,它通过类似于决策树的树形决策结构来选择当前环境下应该做出的具体行为。由于这种ai控制结构在配置、调试、复用之上的便利,行为树的...

    行为树概念

    行为树(behavior tree)的概念最早来源halo这款游戏里的ai控制结构,它通过类似于决策树的树形决策结构来选择当前环境下应该做出的具体行为。由于这种ai控制结构在配置、调试、复用之上的便利,行为树的使用也逐渐成为了现在游戏的主流ai配置方式。unreal现在自带了行为树功能,而unity也有很多行为树相关的插件。下图就是unreal中配置完成的一个简单的行为树结构 :

    行为树样例

    下面我们来简单解释一下这颗行为树的功能。unreal的行为树的执行流是从上到下,从左到右,每个节点执行之后都会有相应的返回值true 或者false, 返回之后控制权转移给当前节点的父节点,来确定下一步的执行:行为树在执行时的第一个入口是ROOT节点,所有的行为树都会有此root节点, 当root节点执行完成之后,会重新开始一次执行,类似于无终止条件的循环。

    进入root节点之后,进入Ai State这个Selector节点,依次从左到右执行他的三个子节点,当任一节点执行返回true的时候则不再执行后续的子节点,直接返回true给父节点。这个节点的逻辑就是让被控制的Entity进入追逐玩家状态还是进入巡逻状态。

    chase player节点是一个sequence,这个节点被一个decorator节点修饰,导致只有在has line of sight返回true的时候才能进入执行,如果decorator返回false则执行流回到Ai State节点。它的三个子节点会从左到右依次执行,用来控制Entity去追打玩家的具体步骤:

    Rotate to face BB entry,朝向目标

    BTT_ChasePlayer 设置自己进入追击状态,追击速度为500

    Move To,移动到敌人位置

    所以这个状态内,被控制的Entity会首先朝向敌对目标,然后设置自己为速度500的追逐状态,追逐到目标之后,返回true, 如果其中任意一个节点返回false,则后续子节点不再执行,当前节点也返回false。Patrol节点是一个不带decorator的Sequence,他执行的时候也是从左到右执行三个子节点:

    BTT_FindRandomPatrol 设置自己为以自己为中心的随机巡逻状态,巡逻速度为125,巡逻半径为1000,获取半径上的一个随机点

    Move To 移动上一个节点确定的位置

    Wait 等待4-5秒

    所以这个Patrol的执行内容就是,以125的速度走到以自身为中心的半径1000的圆的任意一点,走到之后等待4-5秒,然后返回。最后的等待1s是为了两个状态都无法进入的时候的fallback,避免root节点空跑占用cpu。

    行为树定义

    整个行为树就类似于我们写的一个函数调用,他的树形结构就类似于传说中的图形化编程。一颗行为树最终运行时,还依赖于他的执行环境,例如范围内有没有目标就可以让这棵样例行为树控制的Entity呈现出不同的表现。这些行为树的运行环境,我们可以抽象为一个key value的容器,叫做黑板Blackboard, 代表行为树的内部存储的所有参数。当一颗行为树在特定的黑板环境中运行时,行为树的控制权不断地在树形结构中转移,类似于程序计数器Program Counter。运行时某一特定时刻的拥有控制权的节点集合则定义为行为树的格局Configuration。

    因此一颗行为树的运行时描述,包括如下三个方面:行为树的自身结构,所有节点的逻辑关系和相关参数: Structure

    行为树的执行环境,一个键值对的集合: Blackboard

    行为树的活动节点集合: Configuration

    行为树的结构是以树的形式来组织的,每个节点都有相印的节点类型,配合相关参数承载不同的功能。在上面的行为树样例中,我们可以对相关的节点进行归类:黑色的调度控制节点(composition node),包括Root, Patrol, Chase Player

    紫色的行为表现节点(action node), 包括Rotate to face BB entry, BTT_ChasePlayer, Wait, Move To, BTT_FindRandomPatrol, 他们可以当作函数来调用,通过传入特定的参数去调用这些行为节点,来完成特定任务。

    蓝色的修饰器节点(decorator node),用来作为特定节点的前置条件。

    上面的这几类节点类型都是来自于unreal,在不同的行为树之中,对于节点的划分也是各有不同。总的来说,一个行为树的结构描述都具有如下几个部分:行为树是一个树结构,根节点就是root节点,作为行为树的入口,节点类型为Root,每个行为树有且只有一个Root类型节点;

    所有的叶子节点的类型一定是Action,同时Action类型的节点一定不能作为非叶子节点来使用。

    非叶子节点也称为组合节点Composition,可以有一个或多个子节点,root节点一定只有一个子节点

    Action节点类型和Composition的节点类型可以做进一步的细分

    每种类型的的组合节点能拥有的子节点数量与节点类型有关

    一个节点的所有子节点是一个有序列表

    有些节点可以附加特定参数来执行,有些节点则不需要参数

    一颗行为树可以以叶子节点的形式被另外一颗行为树进行调用,就相当于一棵树挂接到了另外一棵树上一样

    行为树的运行

    在明确了行为树的定义之后,行为树的控制表现还依赖于它在特定环境下的执行路径。为了推理行为树的执行路径,我们需要对行为树的运行规则做规定。这里我们把一个节点标记为N, 他的子节点列表标记为N.sons,第i个子节点为N.sons[i] ,他的父节点标记为N.parent, 节点的运行标记为N.run(),运行完成之后返回true或者false,代表节点执行成功或者失败。

    对于Action节点来说,因为他是叶子节点, 不带控制功能,所以他是不影响执行流的。能影响执行流的节点只能是Composition节点。在具体的行为树节点类型定义中,常见的Composition节点细分见下:Sequence节点,他的执行流程就是顺序执行所有的子节点,当一个子节点执行结果为false的时候终止执行并返回false,如果没有子节点返回false则返回true。他的run函数定义如下:

    bool run()

    {

    for(std::uint32_t i = 0 ;i < sons.size(); i++)

    {

    if(!sons[i].run())

    {

    return false;

    }

    }

    return true;

    }Select节点,他的执行流程就是顺序执行所有的子节点,当一个子节点执行结果为true的时候终止执行并返回true,如果没有子节点返回true则返回false。他的run函数定义如下:

    bool run()

    {

    for(std::uint32_t i = 0 ;i < sons.size(); i++)

    {

    if(sons[i].run())

    {

    return true;

    }

    }

    return false;

    }IfElse节点,他拥有三个子节点,当第一个子节点返回true的时候执行第二个子节点并返回此子节点的返回值,否则执行第三个节点并返回这个节点的返回值。他的run函数定义如下:

    bool run()

    {

    if(sons[0].run())

    {

    return sons[1].run();

    }

    else

    {

    return sons[2].run();

    }

    }While节点, 他有两个子节点,当第一个子节点执行返回true的时候,执行第二个子节点然后重新开始执行流程,如果第一个子节点返回false则执行完成,并返回true。他的run函数定义如下:

    bool run()

    {

    while(sons[0].run())

    {

    sons[1].run();

    }

    return true;

    }Root节点,他只有一个子节点,当子节点返回的时候,返回子节点的返回值:

    bool run()

    {

    return sons[0].run();

    }Probility节点,他根据内部存储的权重参数,每次执行时随机选择特定子节点执行,并返回执行结果:

    bool run()

    {

    std::uint32_t idx = random.choice();//根据权重选择节点索引 return sons[idx].run();

    }

    针对Sequence类型,还有一些衍生类型:AlwaysSequence, 顺序执行所有子节点,不管子节点的返回值,执行完所有子节点之后返回true;

    RandomSequence, 每次执行的时候都以随机序列去执行所有的子节点,任一子节点在执行时返回false则中断执行并返回false,否则返回true

    在上面的节点类型分类中,我们并没有看到decorator节点的定义,但是我们可以通过Sequence节点模拟出来,只需要将decorator里面的判断函数作为Action节点去执行,对于被任意修饰器修饰的节点都可以转换为含有修饰器判断节点和具体执行节点的Sequence节点进行替换。在Unreal中,他优先采用decorator方式的理由如下:在行为树的标准模型中,条件语句是任务叶节点,除了成功和失败,它不会执行任何其他操作。虽然没有什么可以阻止您执行传统的条件语句任务,但是强烈建议使用我们的装饰器(Decorator)系统处理条件语句。

    使条件语句成为装饰器而非任务有几个显著的优点。

    首先,条件语句装饰器使行为树UI更直观、更易于阅读。由于条件语句位于它们所控制的分支树的树根,如果不满足条件语句,您可以立即看到行为树的哪个部分是“关闭的”。而且,由于所有的树叶都是操作任务,因此更容易看到行为树对实际操作的排序。在传统模型中,条件语句位于树叶之间,因此您需要花费更多的时间来确定哪些树叶是条件语句,哪些树叶是操作。

    条件语句装饰器的另一个优点是,很容易让这些装饰器充当行为树中关键节点的观察者(等待事件)。这个功能对于充分利用行为树的事件驱动性质至关重要。

    行为树的驱动方式

    在标准行为树中,节点的运行是由tick-driven的,每间隔一段时间开始从root节点开始执行。当特定外部事件需要响应的时候,有时会按需调用root节点的执行。由于这个行为树在执行的时候,对于上次的执行结果是无记忆的,所以Entity的状态机要处理好各种追击、攻击、受击、巡逻状态的强制切换,避免表现异常。最坏情况下一次执行会遍历所有的节点,如果tick间隔过小,则行为树执行会消耗大量cpu。同时如果一段时间内执行的路径结果都相同,行为树就相当于空跑浪费cpu。所以在标准行为树模型里面,如何动态的选择tick间隔是优化的重点。

    为了解决这种tick间隔带来的问题,行为树的模型演进出了基于事件驱动(event-driven)的行为树。这里行为树的更新不再是基于tick,而是基于任务的完成和外部事件的trig。同时每个Action节点开始有了状态,他的执行可能不再是调用之后立即返回,而是开始了一个需要一定时间才能执行的过程,当过程执行结束之后才返回执行结果。同时,任意的一个过程现在都需要支持中断操作,以支持外部环境的改变引发的更高优先级任务的处理。

    以追逐目标这个例子来说:在tick驱动的行为树中,我们需要定期从根节点执行,查询我们是否已经离目标点足够近,如果足够近则执行已经到达目标的分支,否则执行追逐逻辑。到发起追逐到追逐完成期间,可能多次执行行为树。

    在事件驱动的行为树中,一旦进入了Move To节点,则会发起一个寻路过程,同时节点标记为running状态。在寻路到目标之后过程返回,控制权移交到当前节点的父节点,然后进行下一步的操作。一个完整的流程不涉及到行为树的其他节点,相对tick驱动的行为树来说,行为树的决策消耗大大降低了。

    在寻路过程中,目标可能已经死亡或者传送了导致目标丢失,此时我们需要终止当前过程的执行。在事件驱动的行为树中,为了实现对外部事件的响应功能,常见的可选方案有如下两个:为过程添加前置条件,在过程执行期间定期检查前置条件是否满足,如果不满足则中断当前过程的执行并返回false。

    为行为树添加Parallel节点和WaitEvent节点,

    Parallel节点执行时,会顺序执行所有的子节点,而不会阻塞在子节点的过程调用上,如果任一子节点返回,则所有的其他节点都会被打断, 同时Parallel节点返回true。为了支持这个结构,我们需要对原有的行为树调用结构进行修正,因为这里暂时不再给出他的run函数定义。

    WaitEvent节点执行时,会注册对特定事件的回调,然后阻塞不返回。当行为树接收到特定事件之后,对应的回调句柄被调用,相关的WaitEvent节点返回true。

    通过在Parallel节点下同时挂载多个节点,就可以达到在执行特定过程的时候对外部事件进行响应的功能。

    在Unreal和本人所在的项目组都采取的是Parallel方案,但是引入Parallel方案也带来了新的问题,就是策划可能配置出多个过程同时进行的Parallel节点。试想一下同时开启两个对不同目标点的寻路所带来的后果,Entity的状态表现会非常的糟糕。Parallel结构里面不能同时开启多个持续性过程,一般来说是一个主要目标过程附加一些WaitEvent或者WaitTimer的阻塞过程,这些附加的阻塞过程不会干扰主要目标过程,他们的执行也是一些辅助性的工作。

    所以在Unreal中,特别提到了Simple Parallel节点。简单平行节点只有两个子项:一个必须是单个任务节点(拥有可选的装饰器),另一个可以是完整的子树。可以将简单平行节点理解为“执行A的同时,也在执行B"。例如“攻击敌人,同时也朝敌人移动。“从基本上而言,A是主要任务,B是在等待A完成期间的次要任务或填充任务。

    行为树与状态机

    但是如果遇到了需要终止当前主要任务的事件,则Parallel结构也是不够用的。例如在巡逻过程中遇到敌人,我们需要立即进入战斗状态,此时需要中断当前任务的执行来开启新的任务。类似的还有在不断的放技能过程中如果发现自己的血量低于了特定百分比则进入狂暴状态。为了处理这种紧急事件的打断,我组的实现方案是在行为树的上层加一个状态机来进行管理。

    状态机有一个默认状态,在每个状态中执行特定任务的行为树,同时状态与状态之间有一个基于事件的跳转表。当Entity的AI接受到一个外部事件的时候:当前状态所执行的行为树优先处理这个事件,查看当前阻塞的WaitEvent的节点是否有对此事件的监听。如果有,则行为树来处理这个事件

    如果行为树没有对这个事件进行处理,则状态机来查看当前状态下是否有对于这个事件的新状态跳转。如果有对应的跳转,终止当前行为树的执行,跳转到对应的状态,开启新状态下的行为树的执行

    在本项目,对于一个小怪来说,他一共有三种状态:patrol状态,用来处理怪物的巡逻,这个是状态机的默认状态

    fight状态,用来处理遇到敌人的战斗

    return状态,用来处理战斗结束之后的处理,例如先回到出生点,然后重置AI来重新以默认状态开始执行

    在这个小怪的跳转表,主要处理两个事件enter_battle和exit_battle:patrol状态遇到enter_battle事件切换到fight状态

    fight状态遇到exit_battle事件切换到return 状态

    这样一个基本的小怪AI就配置完成了,最后先预告一下自己基于Qt5写的行为树编辑器,项目地址 https://github.com/huangfeidian/behavior_tree :

    下一篇文章来讲行为树的相关工具,包括编辑器、运行时、代码预处理器和调试器,估计周末才有空更新。

    展开全文
  • 最近打算好好研究一下行为树,在使用行为树之前,我们应该先理解行为树的基本概念和相关的逻辑,然后我们就Unity3D平台下的行为树插件的使用来进行学习行为树。什么是行为树如果了解过状态机,会知道在行为树之前,...

    最近打算好好研究一下行为树,在使用行为树之前,我们应该先理解行为树的基本概念和相关的逻辑,然后我们就Unity3D平台下的行为树插件的使用来进行学习行为树。

    什么是行为树

    如果了解过状态机,会知道在行为树之前,在实现AI用得比较多的技术是状态机,状态机理解起来是比较简单的,即一个状态过渡到另一个状态,通过判断将角色的状态改变即可,如果学习过Unity的Mecanim动画系统,会更加直观的理解。

    但是状态机在状态较多的情况下会使状态之间的切换变得异常繁琐,同时状态之间很难复用。

    在这种情况下,行为树被发明出来,行为树的优点如下:

    行为树提供大量的流程控制方法,使得状态之间的改变更加直观;

    整个游戏AI使用树型结构,方便查看与编辑;

    方便调试和代码编写;

    最重要的:行为树方便制作编辑器,可以交由策划人员使用;

    行为树相关资料

    这里给出我知道的行为树相关的资料,大家有更好的资料希望可以告诉我:

    其中,腾讯的开源项目提供了C#编写的行为树编辑器源码和不错的相关文档(中文的哦),非常具有学习价值。

    行为树原理

    行为树是一种树形结构,所以其可以分成3种节点类型:

    1a0ad91643d105f7fd78ffb80aca84a5.png(画丑了点,见谅...)

    红色的节点:根节点,没有父节点的节点;

    蓝色的节点:组合节点,有父节点和子节点的节点;

    白色的节点:叶节点,没有子节点的节点;

    而编号为深度优先访问的顺序;

    节点的返回

    每个节点都会有一个返回值,可能出现的返回值有3个,如下:

    运行中:表示当前节点还在运行中,下一次调用行为树时任然运行当前节点;

    失败:表示当前节点运行失败;

    成功:表示当前节点运行成功;

    下面我们来细说一下这几个节点;

    根节点

    行为树的入口节点,可以是任意类型的节点;

    组合节点

    行为树的组合节点是由下面几种类型来组成的:

    选择节点/优先选择节点(Selector)

    该节点会从左到右的依次执行其子节点,只要子节点返回“失败”,就继续执行后面的节点,直到有一个节点返回“运行中”或“成功”时,会停止后续节点的运行,并且向父节点返回“运行中”或“成功”,如果所有子节点都返回“失败”则向父节点返回“失败”。

    随机选择节点(Random Selector)

    之前的选择节点是有优先级顺序的,而随机选择节点的执行顺序是随机的。但每个节点只会执行一次,比如包含子节点:A、B、C、D、E;使用随机选择节点,执行顺序可能是:D、E、A、C、B或其他组合。其它规则同选择节点一致。

    顺序节点(Sequence)

    该节点会从左到右的依次执行其子节点,只要子节点返回“成功”,就继续执行后面的节点,直到有一个节点返回“运行中”或“失败”时,会停止后续节点的运行,并且向父节点返回“运行中”或“失败”,如果所有子节点都返回“成功”则向父节点返回“成功”。

    修饰节点(Decorator)

    修饰节点只包含一个子节点,用来以某种方式来改变这个子节点的行为。

    修饰节点的类型比较多,这里我们说一些比较常见的修饰节点:

    Until Success和Until Failure

    循环执行子节点,直到返回“成功”或“失败”为止。

    比如Until Success在子节点返回“运行中”和“失败”时都会向父节点返回“运行中”,返回“成功”时向父节点返回“成功”。

    Until Failure在子节点返回“运行中”和“成功”时都会向父节点返回“运行中”,返回“失败”时向父节点返回“成功”。

    Limit

    执行子节点一定次数后强制返回“失败”。

    当子节点运行指定次数后还没有返回“失败”则该节点向父节点返回失败。

    Timer

    子节点不会立即执行,而会在指定的时间到达后才开始执行。

    TimeLimit

    指定子节点的最长运行时间,如果子节点在指定时间到达后还在运行则强制返回“失败”。

    Invert

    对子节点的返回结果取“非”,即子节点返回“成功”则该节点返回“失败”,子节点返回“失败”则该节点返回成功。

    并行节点(Parallel)

    不同于选择和顺序节点依次执行每个节点,并行节点是“同时”执行所有的节点,然后根据所有节点的返回值判断最终返回的结果。

    这里的“同时”会迷惑住不少人,实际上,行为树是运行在单一线程上的,并不会在并行节点上开多个线程来进行真正的同时执行,那么“同时”的含义是什么?

    我们知道选择或顺序节点会依次执行所有的子节点,当子节点返回“成功”或“失败”后就会停止后续节点的执行,而并行节点也会依次执行所有的子节点,无论子节点返回“成功”或“失败”都会继续运行后续节点,保证所有子节点都得到运行后在根据每个子节点的返回值来确定最终的返回结果。

    并行节点一般可以设定退出该节点的条件,比如:

    当全部节点都返回成功时退出;

    当某一个节点返回成功时退出;

    当全部节点都返回成功或失败时退出;

    当某一个节点返回成功或失败时退出;

    当全部节点都返回失败时退出;

    当某一个节点返回失败时退出;

    叶节点

    条件节点(Condition)

    条件节点可以理解为一个if判断语句,当条件的测试结果为true时向父节点传递success,结果为false时向父节点传递failure;

    该节点搭配一些组合节点可以完成各种判断跳转,比如搭配顺序节点,可以做出“是否看见敌人”->“向敌人开火”的AI;

    行为节点(Action)

    行为节点用来完成具体的操作,比如,移动到目标点,执行开火等代码逻辑,多种情况下行为节点会返回running和success;

    行为节点也可能会使用多帧来完成;

    子树的复用

    我们设计好的行为树可以在其他树中作为一颗子树来进行使用,最大可能的复用子树可以减少开发量。

    行为树和状态机的选择

    行为树并不能完全的替代状态机,两者都可以用来处理AI编写问题,但行为树是“轮询”机制,而状态机是“事件”机制,在最终使用之前一定要好好权衡和选择才行。

    展开全文
  • 行为树实例

    2016-04-02 19:33:50
    游戏AI行为树的示例,可做参考
  • 行为树的节点

    2021-01-06 23:27:18
    一,行为树几大节点: Root节点:只能有一个子节点,并且该节点必须是“ 复合”节点。不能将任何Decorator或Service附加到Root,在root可以指定其黑板资源; Task节点:行为树的叶子节点,可以附加Decorator或...
  • 最近要做游戏ai,看了一下行为树的东西,因为用的java,所以主要看了jbt:https://github.com/gaia-ucm/jbt这玩意分编辑器JBTEditor和核心库JBTCore两部分。在JBTEditor里边编辑好之后,导出xml文件;项目代码通过...
  • 行为树执行流程解析

    2021-01-06 17:39:48
    行为树的开始执行的流程图: 一,执行流程 UBehaviorTreeComponent:处理行为树的执行逻辑; StartTree()行为树执行入口; 1,PushInstance() 调用UBehaviorTreeManager 中的LoadTree加载资源;创建新的...
  • AI行为树

    2019-10-03 23:38:44
    如何完成行为树的实现的呢,要找清楚黑板,行为树,AI_Controller的关系,黑板实际上就是行为树的数据库。 1. 先进行整体架构,创建Character,AIController,行为树,黑板,将其关系理顺,首先将控制器与角色链接在...
  • 一、运行 行为树前提 注:(在AI Controller 中运行行为树,如果在其他蓝图中将要获取到AI Controller 这个类才可以进行运行并调用行为树中的一系列操作) 1.在运行行为树之前要有一个Pawn(可以为角色,或者其他...
  • 行为树 BT

    2020-09-11 14:49:14
    行为树的概念会比状态机要复杂些——行为树是一个包含逻辑节点和行为节点的树结构,每次需要找出一个行为的时候,会从树的根节点出发,遍历各个节点,找出第一个和当前数据相符合的行为。 ...
  • 简易行为树

    2021-04-05 15:28:41
    行为树(Behavior Tree) 是一棵用于控制AI决策行为的、包含了层级节点的树结构 主要就是: 用树的结构解决状态切换判断问题的 那咋解决呢? 用这颗树,自顶向下的,通过某些条件来搜索这棵树,最终确定需要做的行为...
  • AI-行为树

    千人学习 2015-06-22 10:06:59
    本课程主要是针对AI算法中的行为树给大家通过案例分析如何使用。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,427
精华内容 2,170
关键字:

行为树