任务打断机制
behavior-dog
在执行一个行为树时,会按照前序遍历的规则依次执行节点任务,并根据任务的执行结果决定是否继续执行行为树。当任务不能在一帧内完成时, behavior-dog
会将他的状态标记为 运行中 ,然后在接下来每帧执行时,跳过遍历的步骤,直接执行该任务,直到他的状态变成 完成 为止。
由于 behavior-dog
不会重新遍历整棵树,因此当一个 运行中 的任务所依赖的外部条件发生变化时, behavior-dog
并不能及时做出响应。在行为树中,这个条件可以是比该任务优先级高的任何其他任务,因为行为树总是先执行高优先级的任务,再执行低优先级的任务。也可以是某个数据,比如黑板上的某一个键值,当他被修改时,任务的执行逻辑也将做出相应改变,甚至终止执行。
因此行为树需要有一个任务打断机制来响应这些变化。
拦截任务
使用 拦截任务 ,可以达到打断任务的目的。
拦截任务可由事件被动触发,也可以搭配触发器一起使用。
比如下面这 2
个内置的拦截任务,
拦截任务 | 拦截任务分组 | 触发器 | 触发器分组 | 描述 |
---|---|---|---|---|
Gate | Intercepter | CtrlGate | Action/Trigger | 开关任务。当他打开时,子任务可以执行,当他关闭时,子任务终止。 |
Interrupt | Intercepter | RequestInterrupt | Action/Trigger | 中断任务。收到指定事件或触发器的中断请求时,打断子任务。 |
条件中断
条件中断是 组合任务 所特有的。
支持以下 4
种类型:
NONE | SELF | LOWER_PRIORITY | BOTH |
---|---|---|---|
不打断 | 打断子任务 | 打断低优先级分支的任务 | 打断子任务和低优先级分支的任务 |
SELF
当有子任务被标记为 运行中
时,每帧评估该子任务 左边 的条件任务,再根据组合任务的执行规则决定是否打断 运行中
的子任务。
当前分支是运行分支时,
SELF
打断才会生效。
举个赛车游戏的例子,在满足下列 2
个条件的情况下,赛车将 进入狂飙模式 :
- 氮气充足
- 玩家 按下 R 键
他的行为树在 预览模式下 看起来像是这样的,
此时,按照行为树的执行规则,在 SpeedBoost
任务不主动结束的情况下,赛车将一直处于狂飙模式。
但是我们希望执行的逻辑是,当上述 2
个条件中有一个不符合时,立即 退出狂飙模式 。
比如,氮气消耗完了 ,不管玩家按没按 R 键都应该退出狂飙模式。
➠ 这时打断机制就派上用场了。
将 Sequence
任务的 abortType
属性设置为 SELF
,
SELF
打断机制下, Sequence
任务会在执行 SpeedBoost
之前,先评估比他优先级高的其他条件子任务。当他们都返回成功时,才执行 SpeedBoost
,如果有一个返回失败,则立即打断 SpeedBoost
。
预览模式下可以看出,行为树不再只执行 SpeedBoost
任务了,而是每帧都重新评估 CmpNum
和 IsKeyDown
任务。
当 SpeedBoost
任务被 CmpNum
任务打断时是这样的,
LOWER_PRIORITY
当低优先级的兄弟任务被标记为 运行中
时,每帧评估所有条件子任务,然后将结果报告给父任务,父任务再根据其规则决定是否打断 运行中
的子任务。
当前分支不是运行分支时,
LOWER_PRIORITY
打断才会生效。
再举个例子,还是刚刚这个赛车游戏。
赛车在行驶过程中,如果:
- 氮气不足 或者
- 玩家 没按 R 键
赛车将 开始收集氮气 。
此时,行为树看起来像下面这样,
此时我们希望,当不满足上述 2
个条件时, 停止收集氮气 。
将 Sequence
任务的 abortType
属性设置为 LOWER_PRIORITY
,
LOWER_PRIORITY
打断机制下, Sequence
任务会评估自己的条件子任务,当他们都返回成功时, Sequence
也返回成功。而由 Selector
任务的规则得知,当 Sequence
这个子任务返回成功时, CollectBooster
任务是不会被执行的,因此打断 CollectBooster
。
预览模式下,
当玩家按下 R 键时,将会打断 CollectBooster
任务的执行,
BOTH
BOTH
表示同时支持 SELF
和 LOWER_PRIORITY
打断机制。
上面例子中,当 Sequence
任务的 abortType
属性设置为 BOTH
时,将可以打断 CollectBooster
和 SpeedBoost
这 2
个任务。
重新评估分支
重新评估分支也可以达到打断任务的目的。
他会每帧都 从头开始 执行分支下的所有子任务,而不是直接执行被 标记为运行 的子任务。
目前内置了以下 2
个重新评估分支任务,
任务 | 分组 | 描述 |
---|---|---|
RevalSequence | Composite | 每帧从头开始执行子任务,当有一个子任务返回失败时,打断其他正在执行的子任务 |
RevalSelector | Composite | 每帧从头开始执行子任务,当有一个子任务返回成功时,打断其他正在执行的子任务 |
以上一节的赛车游戏为例。
我们可以将 Sequence
任务替换为 RevalSequence
,这样即使不设置 abortType
属性,行为树也会在执行 SpeedBooster
之前执行另外 2
个条件任务。
不过此时只有 SpeedBooster
任务能被打断,
而打断 CollectBooster
任务,需要
- 将
RevalSequence
的abortType
属性设置为LOWER_PRIORITY
,或者 - 将
Selector
也替换成RevalSelector
。
比如,将 Selector
替换成 RevalSelector
,再看看预览结果,