(2)行为树
行为树是一种无环图。(树都是无环的......)节点通过边相连,出度节点为父节点,入度节点为子节点,无子节点(出度为0)的节点为叶子节点,无入度节点的节点为根节点。标准行为树的父节点只能有1个(否则会出现环,但不是可逆环)
每个子树定义了一个或简单或复杂的行为。根节点以指定频率不断的发出信号(Tick/Signal),信号传播到每个节点,执行相应计算,并返回SUCCESS,FAILURE,RUNNING状态。当状态返回到根节点,一个Tick结束。
(3)节点类型
(4)节点优先级
比如,小机器人的自卫和躲避障碍的优先级,应该高于吃地上的小球的优先级。实际上,无论是Unity的Behavior Designer插件还是Unreal的内置行为树,都通过一种Abort Condition的机制,实现了这种优先级的处理。
当优先条件出现时,强制中断当前RUNNING的节点。
(5)处理RUNNING STATE
[5.1]从头开始执行,可以不会错过优先级更高的系节点,但是效率低
[5.2]从RUNNING的节点开始执行,但是有可能会错过优先级更改的节点。
(6)组合节点扩展:记住RUNNING节点。
如果节点A指令是去A点,节点B指令是去B点,Sequence是他们的父节点。A刚执行了一个Tick,马上执行B的话,就会出现Agent在AB两个方向颤抖。返回RUNNING状态,下次还是用去A点这个指令开始执行,直到到达A点之后才执行去B点的指令,才能解决这个问题。
(7)组合节点扩展:概率随机选择子节点。
(8)保存数据
Unity Behavior Designer: Shared Variable+Global Variable.
UE4: Blackboard.
注意:每个树运行实例会有自己的Blackboard作为树的数据存储空间。如果将行为树类比为一个Class,Blackboard则支撑了树的对象实例(Object Instance)一千个Agent不会有一千颗行为树实例,但是却会有一千个Blackboard。
图1:行为树,与Blackboard与Agent的关系
实现要点
(1)需要一个可视化调试工具,能够看到当前正在执行的节点(我的先省略了吧)。
树必须知道上一帧打开的节点。这是为了能够在这一个Tick关掉我们没有调用的节点。
(2)必须能够序列化树。
(3)必须有一些基础事件:Open,Tick,Close
Open:Close状态下,第一次调用Tick之前
Tick:每次接到脉冲
Close:Open状态下,返回SUCCESS,FAILURE,或者被BTs强制关掉之后,调用之。
(4)树只存储结构,数据和Agent对象的引用,存放于Blackboard中。
UE4的源代码还没有来得及看。就Unity而言,BehaviorDesigner并没有做到黑板。如果不使用External Tree的方式建立行为树,则每个GameObject都会建立一个行为树的实力,所以论效率来说,确实还是远远不如UE4的,尤其是在大规模和大数量行为树出现的时候。
即使使用了External Tree,也仅仅是通过“Pooled”也就是通过对象池去优化,并没有真正做到树的结构与树的实例分离。所以就行为树而言,还是看看UE4的实现更加“科学”。
不过好在,Unity的插件BehaviorDesigner插件,至少做到了每个节点都是一个非Mono的类。使得不至于一棵树就搞出来几千个MonoBehavior来让效率崩溃。
顺便黑一下UE4 :-) 嘿嘿嘿嘿嘿嘿嘿嘿,纯属娱乐,别当真:-)
下次有时间会写一个UE4 行为树 与BehaviorDesigner的对比。