最近打算好好研究一下行为树,在使用行为树之前,我们应该先理解行为树的基本概念和相关的逻辑,然后我们就Unity3D平台下的行为树插件的使用来进行学习行为树。
如果了解过状态机,会知道在行为树之前,在实现AI用得比较多的技术是状态机,状态机理解起来是比较简单的,即一个状态过渡到另一个状态,通过判断将角色的状态改变即可,如果学习过Unity的Mecanim动画系统,会更加直观的理解。
但是状态机在状态较多的情况下会使状态之间的切换变得异常繁琐,同时状态之间很难复用。
在这种情况下,行为树被发明出来,行为树的优点如下:
这里给出我知道的行为树相关的资料,大家有更好的资料希望可以告诉我:
AI分享站
腾讯游戏行为树框架:支持C++、C#(Unity3D)
其中,腾讯的开源项目提供了C#编写的行为树编辑器源码和不错的相关文档(中文的哦),非常具有学习价值。
行为树是一种树形结构,所以其可以分成3种节点类型:
而编号为深度优先访问的顺序;
每个节点都会有一个返回值,可能出现的返回值有3个,如下:
下面我们来细说一下这几个节点;
行为树的入口节点,可以是任意类型的节点;
行为树的组合节点是由下面几种类型来组成的:
该节点会从左到右的依次执行其子节点,只要子节点返回“失败”,就继续执行后面的节点,直到有一个节点返回“运行中”或“成功”时,会停止后续节点的运行,并且向父节点返回“运行中”或“成功”,如果所有子节点都返回“失败”则向父节点返回“失败”。
之前的选择节点是有优先级顺序的,而随机选择节点的执行顺序是随机的。但每个节点只会执行一次,比如包含子节点:A、B、C、D、E;使用随机选择节点,执行顺序可能是:D、E、A、C、B或其他组合。其它规则同选择节点一致。
该节点会从左到右的依次执行其子节点,只要子节点返回“成功”,就继续执行后面的节点,直到有一个节点返回“运行中”或“失败”时,会停止后续节点的运行,并且向父节点返回“运行中”或“失败”,如果所有子节点都返回“成功”则向父节点返回“成功”。
修饰节点只包含一个子节点,用来以某种方式来改变这个子节点的行为。
修饰节点的类型比较多,这里我们说一些比较常见的修饰节点:
循环执行子节点,直到返回“成功”或“失败”为止。
比如Until Success在子节点返回“运行中”和“失败”时都会向父节点返回“运行中”,返回“成功”时向父节点返回“成功”。
Until Failure在子节点返回“运行中”和“成功”时都会向父节点返回“运行中”,返回“失败”时向父节点返回“成功”。
执行子节点一定次数后强制返回“失败”。
当子节点运行指定次数后还没有返回“失败”则该节点向父节点返回失败。
子节点不会立即执行,而会在指定的时间到达后才开始执行。
指定子节点的最长运行时间,如果子节点在指定时间到达后还在运行则强制返回“失败”。
对子节点的返回结果取“非”,即子节点返回“成功”则该节点返回“失败”,子节点返回“失败”则该节点返回成功。
不同于选择和顺序节点依次执行每个节点,并行节点是“同时”执行所有的节点,然后根据所有节点的返回值判断最终返回的结果。
这里的“同时”会迷惑住不少人,实际上,行为树是运行在单一线程上的,并不会在并行节点上开多个线程来进行真正的同时执行,那么“同时”的含义是什么?
我们知道选择或顺序节点会依次执行所有的子节点,当子节点返回“成功”或“失败”后就会停止后续节点的执行,而并行节点也会依次执行所有的子节点,无论子节点返回“成功”或“失败”都会继续运行后续节点,保证所有子节点都得到运行后在根据每个子节点的返回值来确定最终的返回结果。
并行节点一般可以设定退出该节点的条件,比如:
条件节点可以理解为一个if判断语句,当条件的测试结果为true时向父节点传递success,结果为false时向父节点传递failure;
该节点搭配一些组合节点可以完成各种判断跳转,比如搭配顺序节点,可以做出“是否看见敌人”->“向敌人开火”的AI;
行为节点用来完成具体的操作,比如,移动到目标点,执行开火等代码逻辑,多种情况下行为节点会返回running和success;
行为节点也可能会使用多帧来完成;
我们设计好的行为树可以在其他树中作为一颗子树来进行使用,最大可能的复用子树可以减少开发量。
行为树并不能完全的替代状态机,两者都可以用来处理AI编写问题,但行为树是“轮询”机制,而状态机是“事件”机制,在最终使用之前一定要好好权衡和选择才行。