前两个,正如它们的名字一样,是用来向它们的父节点通知运行的成功或失败。第三种是指还在运行中,结果还未确定,会在下一个 Tick 的时候再去检查这个节点的运行结果。这个功能非常重要,它可以让一个节点持续运行一段时间来维持某些行为。比如一个 Walk 节点会在计算寻路和让角色保持行走的过程中持续返回 Running 来让 AI 保持这一状态。如果寻路因为某些原因失败,或是除了某些状况让行走的行为不得不中止,那么这个节点会返回 Failure 来告诉它的父节点;如果这个角色走到了指定的目的地,那么节点返回 Success 来表示这个行走的指令已经成功完成。这些状态可以用来决定行为树的走向,确保 AI 可以按照我们预期的方式来以某些顺序去执行行为树里的行为。
这个次序节点让 AI 角色实现了从走向门、进门、关门等一系列连续动作。整个过程可以描述成这样:次序节点 ->Walk to Door (Success) ->次序节点(Running) ->Open Door (Success) ->次序节点(Running) ->Walk through Door (Success) ->次序节点(Running) ->Close Door (Success) ->次序节点(Running) -> 向次序节点的父节点返回 Success。如果 AI 因为某些原因未能成功走到门前,比如路被挡住了之类的,那么试图开门这些动作都没有意义了。即当 Walk to Door 这个动作失败后,次序节点就会向其父节点返回 Failure。注意这里不是向次序节点返回 Success,可以将次序节点理解成就是一个桥梁的作用,真正想得到这一连串执行结果的是该次序节点的父节点。
次序节点除了非常自然地用于进行一系列前后依存的动作之外,还可以用来做一些其他的事情,比如:
在上面这个例图中,次序节点的子节点不是一系列动作而是一系列的检查。这些子节点会检查 AI 角色是不是饿了,有没有食物,是不是在安全的地点,只有在它们都返回 Success 时,角色才会吃东西。这样使用次序节点可以实现类似于代码中 IF 判断和与门(AND gate)的效果。这些用于判断的子节点可以是其他的组合节点或是修饰节点等等来实现更丰富的效果。比如下面这个使用了逆变节点(注:逆变节点是一种装饰节点,后文阐述)的例子:
叶节点是最低层的节点,它们不会拥有子节点。叶节点是最强大的节点类型,它们是真正让你行为树做具体事情的节点。通过与组合节点和修饰节点的配合,再加上你自己对叶子节点功能的定义,你可以实现非常复杂的、智能的 AI 行为逻辑。拿代码作为类比的话,组合节点和修饰节点就好比那些改变代码 Flow( 工作流程 ) 的 If 判断和 While 循环等等,而叶节点就是那些真正起作用的被调用的方法,去让角色做具体的事情。
比如一个 Walk 叶子节点可以包含一个具体将要移动到的位置参数,而这个参数可以从其他变量里获得,比如角色将要前往的一个地点参数可以被 GetSafeLocation 这个节点所决定,存入一个变量里,然后 Walk 节点可以使用这个变量来定义它的目的地。在行为树中, 这样将一个节点的数据用在其他节点上的行为, 用好了是非常强大的, 行为树的运行中,这些不同的节点通过数据上下文来共同储存或使用一些持久数据(persistent data),使得行为树的功能变得强大。另一种叶节点的类型是调用其他的行为树并把当前行为树的数据传给对方,可见一种是获得数据另一种是产生数据。