用行为树的方式思考


这段时间做了很多和AI无关的事情,做了个Flash的3D引擎,用汇编写了些shader,做了很多引擎的工具,脚本,插件,游戏也发布了首个预告片(点击这里),一年多的工作收获满满,职位从AI Engineer变成了Engineer(“专科大夫”到“全科大夫”?)。虽然很多工作看似和AI没什么关系,但做AI时的那些经验也带给了我不少思考和借鉴。我的博客里分享的最多的就是行为树(Behavior Tree),被浏览的最多的是行为树,提问最多的还是行为树,行为树作为现在AI的流行技术确实得到了很多的关注,不少游戏和开发者都从中受益。我今天想聊聊的并不是行为树在AI中的某个具体应用,就像标题所说的,我想聊聊如何用行为树的方式去思考问题,这也是我这些时间做其他东西的时候一直在考虑的,并且我也成功的将行为树的思想用到了非AI的模块中,效果不错。如果你还不了解行为树,请先阅读本博客中关于介绍行为树的相关文章(点击这里)。

行为树从本质上来说,是一颗逻辑树,它把所有的行为逻辑用树形结构串联起来,仔细观察的话,可以发现行为树的核心思想有三个方面:

  • 逻辑分离
  • 逻辑关联
  • 逻辑抽象

听上去很玄乎,其实是很简单的东西,可以先想想我们平时要做一个功能是怎么做的,我们会先定义一个函数,定义好输入和输出,然后在这个函数里写代码来实现功能逻辑,这是第一步,是最直接和简单的方式。后来,当这个功能越来越复杂的时候,这个函数里的代码就会越来越长,变得难以阅读和维护,我们就会把一些逻辑拿出去,变成另一个函数,原先那个函数里就变成了一些简单逻辑和函数的组合,再然后,我们发现有些函数可以变成一些通用函数,我们就会把这些函数集合起来变成一个库,这样其他的函数也可以访问这个函数来获取他的逻辑功能。

这里的整个过程就包含了上面所说的三个方面,把逻辑移出去变成一个新的函数,就是“逻辑分离”,原本函数里的简单逻辑和函数组合就是“逻辑关联”,把函数变成通用库就是“逻辑抽象”。AI是游戏的逻辑大户,充斥着大量的游戏逻辑和算法,所以就特别需要好的架构来维护和管理“逻辑”,要不整个代码就是一团糟,不仅无法维护,而且也很难除错,现有的AI的架构基本都围绕这个展开。

让我用有限状态机(FSM)来举个例子,在FSM中就包含了逻辑的分离和抽象,它有“状态”这个概念,这就是一个逻辑块,它的逻辑块也可以重用,但它对于逻辑的关联做的相对比较薄弱,由状态自己来决定何时跳转,并且跳转比较随意,所以逻辑的关联性比较模糊,这就导致FSM在多状态的情况下很难维护。所以后来有了层次化的有限状态机(HFSM),部分解决了逻辑关联模糊的问题,但FSM的设计原理导致它并没有办法从根本上解决问题。但对于状态和跳转都不是很复杂的功能,FSM是个不错的选择。

让我们再回到行为树,行为树把逻辑分散在节点中,每个节点负责自己的逻辑部分,这些逻辑节点又可以被放在行为树的其他部分,也就是可以被重用。在这个基础上,行为树又抽象了三个逻辑概念,控制逻辑,前提逻辑,行为逻辑,其中行为逻辑用来描述功能,控制逻辑和前提逻辑用来描述逻辑间的关联,对于逻辑关联的抽象是行为树相较于FSM的一个重大突破,它使得逻辑的关联“可视化”了,用过行为树的人都会有这样的感觉,我只要看一下树的结构,我就能知道整个AI行为是如何协作的了,也正是这样的优势使得行为树现在被越来越多的用在了AI逻辑中。

但如果我们再往前思考一步,可以发现如果仅仅把行为树限制在AI部分,显得有点可惜,就像我前面一直强调,行为树就是逻辑树,是一种对于逻辑的维护和管理的架构。游戏中很多地方都是有逻辑的,有些甚至会非常复杂,这些地方为什么不能用行为树的方式来思考和实现呢?经过实践,我发现这是完完全全可行的,我甚至可以这样说,只要存在复杂的逻辑,就可以用行为树的方式去做,它可以很好的帮助你理清思路,实现漂亮的逻辑代码。由于行为树与AI有了“密切”的绑定,所以甚少接触AI的程序员对行为树基本不是很了解,这也导致行为树并没有得到广泛的应用,甚至都没有作为一个候选方案。

我有幸在现在项目里,做了很多其他的模块,所以我也把行为树的一些思路用到了其他的模块中,发现写起来非常的顺,也很爽快。我举一个我们在项目中碰到的例子,就是任务系统,做过游戏的人都知道,这个系统在逻辑层面是很复杂的,内容繁多,但如果用行为树的方式去思考的话,就会发现这个复杂的问题一下子就简化了。

仔细分析任务系统的话,可以把任务系统分成几个部分,一个是接这个任务条件,然后是任务的完成目标,然后是奖励(这个和下面的讨论关系不大,暂且略过)。我们先可以抽象两个概念,“单个条件”和“单个目标”。单个的条件包含“怎么算是达到条件”的一段逻辑,单个的目标也是一段逻辑,包含了“怎么算是完成”。所以这些都可以做成一个个逻辑单元,就像行为树的前提和行为节点一样。另外根据设计的需求,接任务的条件可能有多个,完成的目标也可以有多个,所以这些单个逻辑之间就存在逻辑关联,所以我们可以借鉴行为树中控制节点的概念,把这些逻辑关联也抽象出来,成为“关联”,比如一个最简单的逻辑关联就是“并且”,这样我们就可以描述这样一个逻辑,要完成目标1,“并且”完成目标2,这里我们就把两个“单个目标逻辑”用“关联”串起来了。

最后,我们就在每个任务中定义了两颗逻辑树(换个通用的名称,其本质和行为树是一样的),一个是接这个任务的条件树,一个是完成这个任务的目标树,这样每个任务都可以用配置文件来配,我们也做了一个工具来帮助设计师来生成和编辑任务,作为程序只要维护那些可以被用到的“条件”,“目标”,“关联”就可以了。

Mission Complete!非常简单!

这就是行为树的思维方式带来的好处,我们在游戏的教程系统,技能系统等相对逻辑复杂的系统中都或多或少有用到这样的架构方式,使得单个的逻辑被高度提炼和抽象,逻辑的关系非常清晰,分工也变得更为简单。

好,这次就聊到这里,推荐大家可以在碰到复杂逻辑,梳理不清的时候,尝试用行为树去思考一下,一定会有另一番天地~

————————————————————————
作者:Finney
Blog:AI分享站(http://www.aisharing.com/)
Email:finneytang@gmail.com
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————



(已被阅读9,244次)

9 评论

  1. 我向询问下博主,对于强客户端的游戏开发,是否可以考虑采用mvc的流程,把 C 用行为树实现,来黏合m和v?我自己感觉还可以,特地来询问下

    1. 就像我说的,行为树只是对逻辑的一种组织,不限定于可以用在那里,就像状态机一样,只要有复杂逻辑的地方,都可以尝试

  2. 曾研究过一些业余的可视化游戏制作软件,得到了一些启发,和博主的思路类似:一切逻辑都是“状态”和“状态转移的条件”,把这二者抽象出来以至于可以可视化操作,对游戏的流程简化有巨大的帮助。不过过犹不及,粒度切分太细也会造成代码过于流程繁琐,感觉这也是可视化游戏编程最让人不爽的一点。
    PS:博主视频看起来好霸气,羡慕啊,做万年泡菜游戏的伤不起

    1. 如何切分粒度是一个很艺术的问题,需要调整和平衡,我借鉴过层次化状态机的思路,做成层次化的行为树,这样可以好一点

发表评论

电子邮件地址不会被公开。

Copyright © 2011-2017 AI分享站    登录