腾讯互娱研发部引擎技术中心 黄晨
游戏AI的目标之一就是要找到一种简单并可扩展的开发逻辑的方案,常用的技术包括有限状态机(FSM)、分层有限状态机(HFSM)、面向目标的动作规划(GOAP)、分层任务网络(HTN)等。
行为树作为次时代的AI技术,距其原型提出也约有10年左右,像Halo、Spore、Crysis等大作已经采用。目前,很多知名的游戏引擎也已整合或提供了自己的行为树组件,例如Unreal4、Unity引擎等。
本文首先简单介绍有限状态机的基本知识,然后重点介绍行为树的原理和特点,最后详细介绍了我们提供的behaviac行为树组件。
提到有限状态机,很多程序员都相当熟悉。状态机技术在游戏开发中也已变得很成熟和流行,它反映了从系统开始到现在的输入变化。
l 动作(Action):指在给定时刻要进行的活动的描述。
l 状态(State):指对象的某种形态,在当前形态下可能会拥有不同的行为和属性。
l 转移(Transition):表示状态变更,并且必须满足确使转移发生的条件来执行。
l 状态机(State Machine):控制对象状态的管理器。对象的状态不会无端的改变,它需要在某种条件下才会变换。状态会在某个事件触发之后变更,不同的状态也有可能决定了对象的不同属性和行为。
图1 有限状态机
如图1所示,状态机维护了一张图,图的节点是一个个的状态,节点和节点的连线是状态间根据一定规则所执行的状态转换,每一个状态内的逻辑都可以简要描述为:
如果满足条件1,则跳转到状态1;
如果满足条件2,则跳转到状态2;
…;
否则,不做任何跳转,维持当前状态。
有限状态机有着简单的优势,采用状态机来实现AI更符合思维的朴素表达。对于一些简单的AI,用状态机更加便捷,但是面对一些复杂的AI逻辑就会显得比较繁杂。同时,当需要对现有行为逻辑进行扩展的时候,代码上会显得比较吃力,因为要维护的状态量会成倍增加。
对于大型的系统,分层有限状态机(HFSM)也支持状态间切换的重用。但是状态机需要用转换连接状态,从而状态失去了模块性。
行为树,使得实现AI的过程变得更加需要技巧。框架设计者较为全面的考虑了可能会遇到的种种情况,把每种情况都抽象成了某个类型的节点,而游戏开发者要做的就是按照规范把各种节点连接成一棵所需的行为树。这样,行为树更加具有面向对象的特征,行为模块间的藕合度相对较低。
概念上,行为树就是一段脚本,以树的形式展现给用户。节点的执行结果由其父节点来管理,决定接下来做什么。由于节点间不再有转换,因此不再称为状态,节点只是行为。
行为树的基本结构如图2所示,从左到右依次是父子关系的节点:叶子节点主要是一些动作、条件和赋值等原子操作节点;中间包括最左边的父节点,主要是一些组合节点等。
图2 行为树的基本结构
行为树由叶子节点和中间节点组成,叶子节点包含了最基本的行为(如跑动、攻击等),当一个叶子节点被选择后,就会激活其对应的基本行为。中间节点代表逻辑单元,用于管理子节点如何执行。
叶子节点和中间节点主要包括五大类节点:动作、条件、组合、修饰、附件等。其中,动作、条件节点为叶子节点,组合、修饰节点为中间节点,附件是附属在这四类节点上面而不能独立存在的。
节点可能具有前提条件(Precondition)和效果(Effector)等附件,前提条件成功后才能执行该节点,效果用于表示该节点执行完毕后继续执行的操作。
行为树中最常用的节点包括:
l 动作节点(Action):属于叶子节点,用于描述一个最终执行的动作。
l 条件节点(Condition):属于叶子节点,用于描述一个条件是否成立。
l 选择节点(Selector):属于组合节点,用于顺序执行子节点,只要它的一个子节点返回成功,则整个分支返回成功,反之返回失败,类似程序中的逻辑或(OR)。
l 顺序节点(Sequence):属于组合节点,用于顺序执行子节点,只要它的一个子节点返回失败,则整个分支返回失败,反之返回成功,类似程序中的逻辑与(AND)。
节点返回值包括三个:成功(Success)、失败(Failure)以及正在执行(Running)。最基本的行为可能执行成功也可能失败,高等级的行为(中间节点)是否执行成功依赖于子节点是否执行成功,子节点执行失败可能导致其父节点选择另一个子节点。
行为树的执行通过帧循环的更新来驱动,不一定是每帧都需要更新,但是要周期性的执行。基于效率的考虑,行为树的执行有点类似于协程(Coroutine)的概念。如果有返回正在执行的节点,那么在行为树下一次执行的时候会接着执行,否则执行就从根节点重新开始,选择一个可行的子节点执行。
一棵行为树首先需要设置一个Agent类型,Agent也就是游戏中的AI角色。在这棵行为树中的所有节点(主要是叶子节点),可以进一步选择Agent的属性、方法以及其他变量等进行配置。这些Agent的类型、属性、方法等信息,称之为元信息,后文会更详细的对其进行介绍。
行为树的优势如下:
l 行为逻辑和状态数据分离,任何节点都可以反复利用。
l 重用性高,可用通过重组不同的节点来实现不同的行为树。
l 呈线性的方式布局,易扩展。
l 可配置,把工作交给策划。
behaviac组件是我们提供的一种行为树解决方案:
l 该组件已经开发了近三年,经过多次迭代已经比较成熟稳定。
l 在公司内部已经有多个项目在使用,包括《天天炫斗》、《全民夺宝》、《全面突击》、《MGAME》等。
l 支持C++和C#两种语言,并支持所有的主流平台,包括Windows/Linux/Android/iOS等,对Unity引擎有C#的原生支持。
l 功能强大,扩展性较强,文档丰富,支持中英文界面。
l 该组件的使用场景,支持但不限于游戏中的逻辑、角色的人工智能、动画的控制等方面。
behaviac组件的主要特性及用户价值:
l 编辑器和C++/C#运行时的交互基于元信息,既能充分利用程序代码的各种功能,也提供了对高层逻辑的图形化控制。
l 在编辑器中就可以创建和编辑元信息,方便策划提前实现原型。
l 支持XML、BSON、C++、C#等格式的文件导出,既提供了加载、执行的高效,也使用热加载极大的提高了开发效率。
l 功能完善易用的编辑器,支持Prefab、Undo/Redo、子树、事件等。
l 支持的数据类型和节点类型可方便的扩展。
l 实时或者离线调试,运行逻辑图形化、具体化,方便调试。
behaviac组件包括编辑器(Designer)和运行时(Runtime)两大部分,编辑器主要用于编辑行为树,运行时端主要解释和执行编辑好的行为树。编辑器和运行时之间通过元信息(Meta)进行交互,如图3所示:
图3 基于元信息的编辑器和运行时端的交互
元信息是behaviac组件的核心,包括Agent的类型、属性、方法以及类的实例名称等信息。注意图3中元信息的双向箭头,表示运行时端可以导出元信息给编辑器使用,也可以在编辑器中编辑并导出元信息到运行时中。
在运行时端,也即游戏代码端,通过注册(C++通过宏的方式,C#通过标记Attribute的方式,如图4所示)并导出供行为树引擎和编辑器使用的XML元信息。运行时端主要是由程序员写代码调用相关接口,通过将程序员编写的Agent类型及其属性和方法,然后将这些元信息导出到编辑器中,就可以交由策划在编辑器中对这些元信息进行使用。
此外,behaviac组件的特色之一就是编辑器中也可以创建和编辑元信息。编辑器主要交给策划编辑行为树,也可以编辑元信息。策划在项目开始初期,也就是程序员还没把代码写出来之前,策划就可以自己手动的创建一些Agent类型、属性和方法等元信息。这样可以加速游戏原型的创建,也就是策划不用等程序员,就可以进行游戏原型的编辑。
如图5所示,打开编辑器中提供的元信息浏览器,可以查看Agent类型。选中其中一个Agent类型之后,可以查看它的属性、方法等信息,并可以扩展Agent类型的成员属性和方法等,以及新建Agent类型等。
图4 在C#中通过Attribute标记元信息
图5 在元信息浏览器中查看和编辑元信息
Agent类作为行为树的基本组成元素,是供其他游戏类派生的基类,在图5所示的元信息浏览器中,我们可以查看所有导出和创建的Agent类型,这些Agent类型又包含变量和方法两大部分。
变量分为成员属性、定制的属性以及局部变量等。
l 成员属性:也即面向对象编程中类的成员属性。
l 定制的属性:主要是在编辑器中为Agent类型新建出来的成员属性。
l 局部变量:相对于成员属性和定制的属性这些全局属性而言。为什么称之为全局属性?在整个游戏项目中,可能会有很多棵行为树,我们称之为工作区来管理所有的行为树。每一种Agent类型的成员属性和定制的属性是可以供这个工作区中的所有行为树选择使用的,而局部变量只是针对某一棵行为树,局限于在这一棵行为树中所使用的变量。
方法包括成员方法、定制的方法等。
l 成员方法:也即面向对象编程中类的成员方法。
l 定制的方法:在编辑器中创建出来的方法。
最后,在编辑器中,就可以根据导出和创建的元信息,进行行为树的编辑。在行为树的每个节点上可以根据需要选择或设置属性、方法及其参数等,如图6所示。
图6 编辑节点的属性
开始项目时,首先需要在编辑器中创建一个工作区。工作区(Workspace)文件是管理游戏项目中所有行为树文件的配置文件,如图7所示:
图7 工作区文件
该配置文件可以指定XML元信息文件、行为树源文件的路径、导出文件的路径等,可以通过编辑器中的“新建工作区”或“编辑工作区”工具编辑相关设置,如图8所示:
图8 编辑工作区
这里,行为树源文件和导出文件的区别在于,行为树源文件是在编辑器中供策划编辑使用的初始原文件,包含了很多冗余的信息,比如UI显示所需的属性等。而导出文件是一种精简版或者压缩版的行为树文件,只是给运行时端使用的一种高效的执行文件。
行为树编辑器支持四种格式的文件导出,包括XML/BSON/C++/C#等,如图9所示。
其中,XML/BSON主要用于开发阶段,C++/C#主要用于最后的发布。C++/C#的内存和性能明显要优于XML/BSON的格式,所以我们建议在最后的发布过程,使用C++/C#的行为树导出文件。
图9 导出行为树
编辑器支持热加载,但是只针对XML/BSON格式的行为树文件。在开发过程中,只要更新行为树文件,并重新导出,在游戏运行过程中不用退出游戏,就可以及时查看行为树最新的效果。
编辑器支持跟游戏端进行连调,如图10所示。调试支持查看属性的变化(图11)、在节点上设置断点(图12)、高亮显示行为树的执行路径(图12)以及记录每帧信息以便后续查看等功能。
图10 连接游戏
图11 查看Agent的属性
图12 设置断点,高亮显示执行路径
腾讯 behaviac组件已经正式推出 官方首页:http://www.behaviac.com