AI系统的思考和实现【转载】

在我的概念里,AI应该分为两层:决策(Decision)行为(Behavior),当然这个是从狭义的AI角度来说的,广义上来说,AI还会包括一些其他要件,比如物理,动画等等。现在这个项目里在做Behavior模块的一些东西,从做结构开始就参与其中,期间经过了几次大的重构。

可能Behavior在不同文章里的含义会有些不同,我这里的行为层从某种程度上可以说是一个中间件,上层是AI的决策逻辑,下层是动画资源,工作目标就是把AI的请求转换成真正的动画播放请求。行为的定义是比较关键的一点,比如一个小狗,它可以有走,跑,睡觉,撒尿,吃饭,喝水等等。这些就可以认为是他的一系列Behavior,或者叫逻辑动作(Logical Action)。这些预定义好的行为就组成了一个集合,可以称作行为池(Behavior Pool)

如何组织起这些行为?Behaviour在组织过程中可以分为两种:

  • 单一行为(Simple Behavior)
  • 复合行为(Composite Behavior)

在上面Pool里定义的就是单一行为,复合行为可以看成把单一行为组合后形成的新的行为,拿小狗为例,比如我们定义一个复合行为,“ 边吃饭边喝水”,这个复合的行为可以看成是“吃饭”和“喝水”两个单一行为并行后的结果。常用的复合方式有几种:

  • 序列(Sequenece):一个个行为接着做
  • 并行(Parallel):两个或多个行为同时进行
  • 选择(Selector):从候选行为中选择一个执行,包括脚本选择,和随机选择

这就牵涉到实现的相关问题,如何定义各层之间的接口,如何定义统一的结构。在实现前,一般有几个必须要考虑的问题,当然,这些不仅适用于游戏AI开发,也适用于其他类型的软件开发。

  • 利于扩展:因为在开发中需求可能一直在膨胀,预先设计的系统必须尽可能的满足这种需求。
  • 利于任务分摊:当一个结构确定了之后,会有多个人一同来实现相应的功能和细节,所以,实现上要尽可能的使任务独立,方便多人协作。
  • 利于调试:能实时的监控运行情况。

下面来介绍一下我尝试过的两种实现方法,当然,这里给出的是一些思路,一些很细节的东西,我不会很深入,要不就成代码解释了,顺便说一句,这两个名字是我自己取得,只是为了便于说明和理解。先介绍第一个。

基于动态命令队列的行为系统(Based on Dynamic Command Queue’s Behavior System)

AI系统的思考和实现【转载】 - 建筑学概论 - Seven的部落格

这种系统自顶向下分为三个部分, 行为命令(Behavior Command),行为机(Behavior Machine),行为结点(Behavior Node)

  1. 行为命令(Behaviour Command):是面向AI决策层的接口,AI把行为命令压入行为队列,等待行为机的处理。这里的命令都是预先定义好的,再用上次举过的小狗的例子,决策层发现小狗的疲劳度很高了,需要“睡觉”,就可以发一个定义好的睡觉的命令,然后再由下层行为机负责处理。
  2. 行为机(Behaviour Machine):行为机的主要任务是维护一个行为队列,并执行队列中的行为命令,每一个行为命令都对应一个定义好的执行单元,即行为结点
  3. 行为结点(Behaviour Node):真正的行为执行单元,是一组简单行为的集合。结点内部,根据行为命令中的相关信息,进行处理,如选择动画,播放动画等等

在这种结构中,复合行为是由预定义的行为命令来实现的,比如定义一个 “边吃饭边喝水”的命令,这样的话,如果我们要扩展,只需要扩展行为命令,然后再添加相应的行为结点作为执行单元即可。行为机是整个系统的核心部分,我做的时候,接受命令请求的队列容量是2,即当前执行的和下一个要执行的,这可以根据实际需求来调整。另外实现过程中,发现有可能有用的Tips,给大家参考:

  • 检测行为命令是否相同:AI可能重复发一个行为命令(很多情况下无法避免),所以在命令进入队列的时候,需要检测这个命令是否与队列中相同
  • 检测行为命令的优先级:因为命令是被压入的,而不是立即执行的,所以可能有时希望命令间有优先级,比如,小狗 ”睡觉“比”移动“的优先级高,这样当”睡觉“指令入队列的时候,”移动“命令就不会覆盖掉”睡觉“ 的指令。
  • 提供特殊命令:行为机可能需要提供一些控制命令,如重置,剔除,暂停,查询等给AI接口,当然,过多的控制命令会破坏行为机的封装性,只能平衡一下了。
  • 用FSM(有限状态机)来实现行为结点:一个行为执行可能是一个需要不断重入的过程,也就需要几帧或者几十帧来完成,这样可以考虑用FSM把行为结点分为Enter,Execute,Exit三段(我叫这种为3E结构)来处理,会十分的方便。

调试这种结构也很便捷,可以做一个工具来监控整个行为命令队列的运行情况,比如,当前是哪个,下一个是哪个,集成在AI的调试工具里即可。这种基于命令的结构的缺点主要是,没法设计行为结构,GD也没法参与其中,而且不是一种层次逻辑结构,下面这种结构在游戏界也是一种主流。

基于静态树结构的行为系统(Based on Static Tree’s Behavior System)

AI系统的思考和实现【转载】 - 建筑学概论 - Seven的部落格

这种结构就是传说中的Behavior Tree(下简称BT,不要想成另一个BT),BT的实现方法可以有很多种,这里说的是我的实现方法。

BT的基本结构是节点(Node),节点分为两种:

  • 行为节点(Ternimal Node)
  • 控制节点(Control Node)

在BT的叶节点上全部是是行为节点,中间的全部是控制节点,控制节点就包括了我前面说过的三种基本结构,选择,并行,序列。

代码实现上来说,BT的结构是一个通用的框架,所以应该尽量使得通用和特定的部分分开,比如,控制节点的逻辑是通用的,但逻辑的实现是游戏特定的,如果这样说还是不好理解的话,我来举个例子,比如一个控制节点是一个选择结构的节点,选择节点的逻辑是选择一个子节点来运行,这就是通用的部分,但如何选择是要根据不同情况定制的,这就是特定的部分。正是有了这样的要求,所以,我就引入了一个节点脚本(Node Script)的概念。

每一个控制节点都会绑定一个节点脚本负责实现控制节点的逻辑定制,这样在构建整个BT的过程中,就分为以下几个步骤

  1. 搭建整个BT的结构,可以由GD一起参与
  2. 对每一个控制节点编写控制逻辑
  3. 编写每一个叶节点,即行为节点的代码

可以看到,这样的一个过程很容易用一个可视化的Tool来实现,画画,拖拖,写写,越想越赞,随便说一句,我对做可视化的AI结构一直相当的有兴趣!这里,对于代码的结构就不多说了,对OO熟悉的话应该很容易搭建这样一个系统,写一些Tips,大家可以参考:

  • 行为节点可以使用 1P+3E的简单FSM结构,即,Precondition,Enter,Execute,Exit,这样可以有很大的灵活度
  • 对于控制节点必须提供一个Transition接口的调用来完成相关的清理工作,比如从这个节点转到另一个节点,必须调用上一个节点Transition来完成转换
  • BT的每个节点可以允许挂的子节点数可以自定义,如果允许两个的话,就是2叉树,这样会使树的结构比较大(可以想象一下,如果我要做4个行为的序列,那树的层次就是3),但接口会相对简单
  • 构建BT时,最好为每一个节点取一个名字,以方便从callstack观察运行路径

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

posted @ 2018-01-17 22:57  前程路88号  阅读(189)  评论(0编辑  收藏  举报