行为树的节点

行为树是由控制节点、装饰节点、行为节点组成的一棵树。中间节点一般为控制节点和装饰节,用于控制行为树的执行流程,它们相对固定,一旦确定几乎不会变化。叶子节点由Action节点或Condition节点组成,使用者的大部分工作都是设计树的逻辑和行为节点。

行为树的每个节点都有一个返回值,它们分别是:

  • 成功(Success)
  • 失败(failed)
  • 运行中(running):表示当前帧没有执行完成、下帧继续执行。

控制类节点

控制节点一般为中间节点,用于控制行为树的执行流程,决定了其子节点是以顺序、并行、随机或其它方式执行。

  • 顺序节点

依次执行所有子节点,若当前子节点返回成功,则继续执行下一个子节点;若子当前节点返回失败,则中断后续子节点的执行,并把结果返回给父节点。节点1返回成功,继续执行节点2;节点2返回失败,则把结果返回给Sequences的父节点,节点3并不会执行。顺序节点相当于and语义。

  • 选择节点(Selector)

依次执行所有子节点,若当前子节点返回成功,则中断后续节点运行,并把结果返回给父节点。相当于or语义

当有的行为节点对应的代码执行较长,例如:播放动画,此时,这个行为节点会向父节点返回running,于是选择节点便不再执行后续节点,直接向父节点返回 running

  • 并行节点(Parallel)

依次执行所有子节点,无论失败与否,都会把所有子节点执行一遍。至于Parallel节点该返回什么值给父节点,这要看需求。比如:成功数 > 失败数返回成功,否则返回失败。

  • 随机节点(Random)

随机选择一个子节点来运行。机器人领域应该不会使用。在游戏设计里,AI角色每天会根据自己的心情选择是呆在家里、工作或是出门游玩,可以采用随机选择节点

  • 记忆节点(MemSequences、MemSelector)

使用很少。功能和顺序节点、选择节点类似,唯一不同是会保存当前执行进度(比如:保存当前子节点索引),下一帧继续执行当前节点,如果当前节点是中间节点,则会跳过前面的节点。

修饰节点(Decorator)

  • 逆变节点(Inverter):tick孩子节点一次,孩子节点失败返回SUCCESS,成功返回FAILURE,孩子节点运行则返回RUNNING。也就是对子节点的返回值取反,相当于not语义,它只会有一个子节点
  • 成功节点 (ForceSuccessNode):tick子节点一次,不管其子节点返回何值,都会返回Success给父节点
  • 重复节点 (Repeater):重复执行n次子节点(n作为数据输入),直到子节点返回失败,则该节点返回FAILURE,若子节点返回RUNNING ,则同样返回RUNNING。
  • RetryNode: 最多tick子节点 n 次,(n作为数据输入),直到子节点返回成功,则该节点返回 SUCCESS,若子节点返回RUNNING ,则同样返回RUNNING
  • 执行一段时间(MaxTime):重复执行子节点一段时间

  • action

动作节点通常实现服务客户端和动作客户端,也可以是一些简单的执行程序。action通常作为行为树中的叶子节点,负责具体行为和功能的实现。但这些具体的功能代码并没有在叶子节点中而是在对应的服务端。执行这种节点,可能只需要一帧就可以完成,也可能需要多帧才能完成。

它至少包含两个函数:

  • Init:用于初始化节点,比如读取配置数据初始化当前节点,只会执行一次。
  • OnTick:每一帧都会执行,节点的主要逻辑都在此函数中实现或调用。

  • condition

这是条件控制节点。比如判断电池电量,某一开关信号等等。

  • control

这是行为树中的控制流。类似c++语言中的if else,switch等等。它负责构建行为树的逻辑结构。sequeence,fallback等等就属于这个范畴。

黑板

行为树中的共有数据是存放在Blackboard中的。Blackboard是以键值对的形式存储数据的。各个行为树的叶子节点均可以通过key访问到需要的数据。节点的输入端口可以从黑板读取数据,节点的输出端口可以写入黑板。

Ports是用于在节点间交换数据而存在的。一个行为树叶子节点可以定义输入的Ports和输出的Ports。当不同叶子节点的Port有相同的key名称时,可以认为它们是相通的。当在行为树叶子节点中声明了这些Ports时,也需要同时在xml文件中描述这些Ports。

注意设置子树的__shared_blackboard属性为true,否则C++程序可能报错

获取全局黑板变量,也就是说它也用了单例模式。

1
auto blackboard = DecisionBlackboard::GetInstance().GetBlackBoard();

程序中如果加载了多个行为树,黑板变量可以共享。