CryEngine里的行为选择树(Behavior Selection Tree)


前些日子,经人推荐,看了一些CryEngine里AI部分的介绍,当然,作为一个大型商业引擎,AI部分涵盖了很多内容,特别的,我注意到里面关于行为树的部分,从CryEngine 3.3开始,行为树被舍弃了,取而代之的是称为“行为选择树”的模块,我并没有找到关于为何要做出这样的选择的官方说明,因此,我就仔细阅读了关于行为选择树的一些文档,记录在这里,因为我并没有用过CryEngine以及它所提供的相关工具,以下所有的心得都源自对于官方文档的阅读,如有不对,欢迎指正和讨论。

行为选择树(Behavior Selection Tree),顾名思义,它的重点在于为“行为”和“选择”

  • 行为:AI在每一个时刻,都必须处在某一个行为中
  • 选择:AI在每一个时候,都会根据外部的条件,选择某一个行为作为当前的行为状态

这两点并不是很难理解,换句话说,就是每一个AI智能体都永远处在它所应该在的行为状态中(当然,这里的“应该”指人为预设的行为选择逻辑)

在行为选择树中有几个相关的概念,或者说术语

  • 选择变量(Selection Variable):用来做选择逻辑的变量值,其实说白了,就可以认为是一个布尔值,要么是真(True),要么是假(False)
  • 信号变量(Signal Variable):用来改变“选择变量”的事件,“信号”是有外部的AI系统或行为选择树本身发出的,在行为选择树中,信号变量是和选择变量绑定的,要改变选择变量,官方建议是通过发送信号来实现(不建议直接修改),比如有一个信号变量是OnEnemyDead,一个选择变量是IsEnemyDead,那么当行为选择树收到这个信号时,就会把IsEnemyDead设成True,这样就影响了行为选择树的选择逻辑。
  • 叶节点映射(Leaf Transition):定义了名字和行为的关系,相当于给行为节点定义了一个别名。在行为选择树的定义中,节点是用别名来引用的,这样的好处就是,行为树的结构是可以复用的。

为了更好的说明上面复用的问题,我们可以举这样一个例子,比如有两种动物,一个是猫,一个是狗,同样是闲逛,猫有猫的方式CatWalk(猫步?),狗有狗的方式DogWalk,同样是吃饭,猫是CatEating,狗是DogEating,当饿的时候,会从闲逛行为切换到吃饭行为,吃饱了,就会再次切换回闲逛,所以对这两种动物来说,它们的行为选择树结构和逻辑上是一样的,不一样的只是他们的具体行为的执行方式,所以他们的行为选择树是可以复用的,叶节点映射就可以很好的处理这种情况,如下面的定义文件示例

--猫的叶节点映射

<LeafTranslations>

<Map node="Walk" target="CatWalk"/> --定义此别名所对应的具体行为

<Map node="Eat" target="CatEating"/>

</LeafTranslations>

--狗的叶节点映射

<LeafTranslations>

<Map node="Walk" target="DogWalk"/>

<Map node="Eat" target="DogEating"/>

</LeafTranslations>

--行为选择树定义,使用的时候别名,而不是具体行为的名字

<Priority name="Root">

<Leaf name="Eat" condition="IAmHungry"/> --选择条件是饿了

<Leaf name="Walk"/>

</Priority>

基本上,以上这些就是行为选择树的全部概念了,和行为树比起来,我觉得有两个比较明显的不同:

  1. 对于“前提”(Precondition)的处理:在行为树的定义中,“前提”是可以带逻辑的,或是简单的逻辑代码,也或是复杂的逻辑代码,而在行为选择树中,它的前提被定义成了一个和“信号”所绑定的布尔变量,也就是说在它的前提中,不包含对于逻辑的计算,所有的这些计算都是在外部的其他AI模块(比如,感知系统,团队系统等等)中。所以行为选择树的“前提”可以看成是对一般的行为树前提的一个严格限定。
  2. 叶节点映射:行为树的定义中,并没有别名系统,即使行为树的结构是一样的,但不同的智能体上也会绑定各自的行为树。行为选择树很好的引入了别名系统,这样就可以使行为树得到了复用,可以说,这一点是对行为树的一个改进。

CryEngine中提供了一个编辑器来生成行为选择树,从文档的介绍来看,可以很好的编辑,调试和管理行为选择树。我想之所以行为选择树有这样的设计,和编辑器,以及整个AI系统的融合有很大的关系。编辑器的要求就是直观,对于前提的简单处理可以很好的保证这一点,比如,我可以仅仅改变一个选择变量就改变其行为选择,对于调试来说,也是极为便利的,设想一下如果前提是带有逻辑运算的,那在编辑器中,那就需要再写一个前提脚本来配置前提,会相对复杂得多。

另外,在实际开发中,会发现对于树(或者树的某个分支)的复用是十分必要的,就像我们不喜欢代码的冗余一样,对于行为树的冗余,有时也是不能接受的,所以不管从树的设计上来说,还是编辑器来说都要提供复用的架构设计,行为选择树就很好的做到了这一点,特别要指出的是,简单的前提设计在复用上有提供了一个很好的帮助,因为它仅仅是一个变量,所以在前提部分我们就不需要特别关注复用的问题了。如果是带复杂逻辑的前提,那势必我们还需要关心前提的复用问题。简单就是美啊!

总结下,行为选择树可以看成是对行为树的部分功能简化,和拓展,为编辑器的设计,为调试带来了很大的帮助,当然,由于它把前提的运算移到了外部,所以就更多的需要外围其他AI系统的支援了。有兴趣的朋友,不妨到CryEngine的主页了解试用一下这个引擎和它的行为选择树。

参考资料:

http://freesdk.crydev.net/display/SDKDOC2/Behavior+Selection+Tree+Editor

http://freesdk.crydev.net/display/SDKDOC5/Behavior+Selection+Trees

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



(已被阅读7,649次)

7 评论

  1. 我自己瞎琢磨的框架居然和这个比较像。。
    说说自己的看法:树只负责逻辑的走向控制, 这样树维护起来很方便,判断逻辑放在外部是为了节省计算资源。同时方便树的共享。
    只用selector树是因为可以用多个单一功能的树进行组合,比如移动树,动画树,旋转树,等等。因为所有单一功能的具体Action节点必然只有一个。也方便action节点的提炼共享。
    这样设计维护都很清晰,而且系统效率也会得到较好控制。

  2. 其实我游戏里面最后写出来的角色动画选择逻辑就是他这样子的, 用的是条件优先级模式匹配的方式, 为了通用就把匹配的动作经过一层抽象映射. 最后换为数据驱动后也就是他的样子了.

  3. 其实我游戏里面最后写出来的角色动画选择逻辑就是他这样子的, 用的是条件优先级模式匹配的方式, 为了通用就把匹配的动作经过一层抽象映射. 最后换为数据驱动后也就是他的样子了.

  4. 请教一下:CryEngine中的行为选择树是不是更像决策树而不是行为树。首先他只有一种组合节点Priority Nodes,也就是selector。第二,他没有从一个状态到另一个状态的概念,在行为树的sequence中,执行完第一个action就会执行第二个,而行为选择树中每个action都需要单独去决策。

    1. 决策树在我的理解里是没有状态的维持的,它只是为了得到一个决策结果,但cryengine里的行为选择树应该是有状态的维持的,也就是说在最终的行为节点上是有一段行为逻辑的,并在条件满足的情况下持续地被执行。因为我没实际用过,单单只是从它的文档上来理解,可能有不对,欢迎指正

  5. 请教一下:CryEngine中的行为选择树是不是更像决策树而不是行为树。首先他只有一种组合节点Priority Nodes,也就是selector。第二,他没有从一个状态到另一个状态的概念,在行为树的sequence中,执行外第一个action就会执行第二个,而这边每个action都需要单独去决策。

发表评论

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

Copyright © 2011-2017 AI分享站    登录