有这么一种AI程序结构: Behaviour Tree  , 参考 文章。

最近想把基于HFSM的AI改成基于BT的AI.所以有一些思考。


我认为 Behaviour Tree 本质上定义了这么一个结构:

认为Condition是C , Action 是A,

那么BT 是一组 C(1-n)->A 的集合。

C1&C2&C3 -> A1

C2&C3 ->A2

如果全部C 都是and 连接,那么BT 本质上可以认为是一个DT,最终会筛选出一个合适的Action.  对于非and的,可以人为构造出一个新的C,比如C(~c1 || c3 && c2 || ~ c4) 


Cn->A1

Cn->A2

Cn->A3

我们可以写出这种顺序的条件-行为触发器序列,BT 只是提供了变化排序的手段,可以断言人工也能组织正确---当然更费时。


那么,当前处在哪个状态下?没有状态了,所有的行为全部使用C来进行区分。

那么会不会有重复的C出现?会,原图中扔手雷的条件就是反复出现的。

扔手雷-上子弹-射击,这个顺序是人为规定的,而且是经过安排的。因为

1、这三个行为都挂载在一个顺序节点下。

2、如果射击在上子弹行为之前,就会因为射击失败而导致无法上子弹。

在评论里有一个人问到了核心之一, Action是一个瞬时行为,还是一个异步行为?

我认为这取决于决策怎么进行,DT 每次只对整个树进行一次决策,而且总是重头进行决策,最终选择一个Action作为结果。

BT 考虑异步的行为: 扔手雷 - 3s 后完成,返回True, 该上子弹了, 假设大量的敌人还是存在,更合理的行为应该是再来一发boom。

所以为了总是让角色有合理的行为,BH在筛选出行为之后,就应该等当前行为执行完成之后,再做下一次的决策。


Action是否是原子操作,无法被打断?我不想讨论“原子性”,考虑这样一个行为:移动到指定目标身边小于3米的地方。

这是不是一个原子行为?答案是我们可以将他实现为原子的,也可以加一个Interrupt操作。

如果是Interruptable的Action,那是什么导致我们需要中断当前操作,而立即进行一次重新决策呢?

问题一:假设我们规定某些重要的环境感知会引发重新决策,那么是哪些?


好,搁置问题,进一步考虑。

行为可被打断,说明这是一个“意图",而不是一个“行为”,表示角色“想移动到指定目标身边”,如果中途遇到障碍,甚至可以启动寻路系统,搜索出一条合适的道路来协助完成这个“意图”。

意图:当Boss血量低于30%时释放技能neng死玩家:这个意图需要多个行为来协助完成

C(MP不足)-->A(补蓝)

C(非禁魔)&C(技能无CD)-->A(释放技能)


所以,这是一个规划。只是写死的而已。

因此BT的Action我认为是一个意图Action,其内部的实现还是有若干过程的,但这个细节不需要策划进行关心。他只需要关心该意图是否已经达成---顺序节点不一定是达成意图的唯一组合模式--neng死玩家的方法很多。

FSM和BT的对比?

老实说我没感觉FSM比BT差到哪里去了,我的核心工作量还是编写更多的Condition种类,和Action种类,策划通过工具自行安排状态机的行为。

当最极端的,一个State里只有一个Action时,效果和BT是一样的:角色从IdleState 开始,根据不同的Condition和各Condtion的逻辑运算,激活不同的Transation,切换到TargetState,调用一下Action,在Action完成时,返回IdleState,等待下一次决策。

在另外的层,配置感知条件,中断主状态循环,执行特殊行为的状态机(一层或若干层),完成后再返回主状态机层的循环。

核心还是在安排。


另外C-->A 这种Trigger集合,可以转化为一个 DT来提高决策效率。

最极端情况下,可以不用State来区分各种情况,而是这样:

C1 CxCy ->A1
C1 CaCb ->A2

C2 CxCy ->A2
C2 CaCb ->A1

使用以前状态机里的条件来区分这些集合,再导成一颗DT。不见得比状态机快多少... 但是子状态里确实可以使用DT来完成决策的加速。

BT 使用组合节点、装饰节点、条件节点来达成对Action的遴选
而State通过对Condition(条件节点)的逻辑运算(装饰节点)、State执行Action的方式(顺序、并发、容错等)来达成同样的目的。

其实一个Action是否适合并发、顺序,由其自身来决定的,比如移动 和 射击 就可以并发---这是在FPS游戏里,如果像是在War3 这种游戏里呢?(War3的单位攻击时不得不停在原地完成攻击行为),如果这个攻击是瞬发的呢?

所以不是结构决定了Action是否是并行的,而是Action决定了自身是否可并发。

让可并行的Action顺序执行没有问题,但是反过来就有问题了。

真正干活的函数,应该是角色内部行为状态机,比如移动和攻击,我们都是调用FSMInputEvent来完成,在War3里,当角色尝试进行攻击时,必然先退出移动状态(如果当前在移动的话)。如果当前角色行为状态机不接受 决策系统(AI)的结果,那么只需要简单的忽略这个Event,而外界环境是没有变化的(真的吗?),所以再次进行决策时,决策结果还是会一样的,如果不一样,说明环境变了。

环境是否变化?取决于你怎么让AI记录这个环境,有些情况是瞬时环境,只有当函数被调用时才发生,比如 角色受到伤害了,我们要去反击那个打我们的人。

受击是一个瞬时行为,我们必须让感知系统记录在案,而不是立即中断当前行为,进行一次决策。

当然,这也不是必须,你可以尝试使用事件触发,中断当前行为并立即重新进行决策,可能好也可能坏。


关于事件通知,唯一不好的地方在于可能一帧之内会有多重通知过来,如果每次来一个通知,决策就重新进行,那么可能会导致AI直接跳过了一些重要的条件参考---因为还没进入那个状态呢----使用BT不会,或者使用Trigger集合的方法也不会,但是会有多次决策带来的开销问题, 使用高度优化的DT,最终可以达到 O(nlogn)的时间开销,但是谁架得住调用次数过多呢。

记录在案则可以有效的减少多事件源带来的决策开销。


嗯,不知道对不对。