虚幻4AI行为控制采用事件驱动模式,需由行为树和黑板配合使用的,行为树执行AI逻辑,黑板通过黑板变量来存储AI数据,
黑板变量的改变事件驱动AI行为树的逻辑执行。
每个AI都有自己的控制器(AIController),AI控制器上有两个组件负责其行为:行为树组件BehaviorTreeComponent和黑板组件BlackboardComponent,当AI生成时,会在Controller上初始化这两个组件,执行AI行为树。
另外,当前世界所有AI的行为树都由世界的AI系统里的BehaviorTreeManager来管理,BehaviorTreeManager有一个数组ActiveComponents存放世界中所有激活的行为树。
AI人物出生时会生成controller,在controller中初始化黑板和行为树(AAIController::RunBehaviorTree()),当BehaviorTreeComponent执行StartTree()方法时,会将此行为树添加到BehaviorTreeManager的行为树数组里:
行为树在被添加到Manager的数组中后,BehaviorTreeManager会从根节点按递归方式深度优先遍历整个行为树,初始化该行为树的所有node节点(BehaviorTreeManager::LoadTree),Manager对行为树节点的初始化工作主要为:
1)设置每个节点的父节点;
2)设置每个节点的执行顺序索引;
3)设置每个节点的下一执行节点;
4)设置每个节点在行为树中的深度;
5)设置每个节点数据所占的内存大小。
这样每个节点初始化并互相关联后,整个行为树也完成了初始化。
下图为虚幻4行为树节点的UML图:
UBTNode类是抽象类,为所有行为节点类的基类,主要有以下属性:
1)ParentNode--父节点;
2)NextExecutionNode--该节点在行为树中的下一执行节点;
3)ExecutionIndex --执行序号(深度优先);
4)TreeDepth--节点在行为树中的深度;
5)MemoryOffset-- 内存偏移。
这些节点的基本属性会在行为树初始化时由BehaviorTreeManager初始化。
BTCompositeNode即复合节点,复合节点分为3类:Selector、Sequence、SimplepParallel
从左向右依次执行其子节点,当其任意子节点执行成功时停止继续执行。
如果任意子节点执行成功,Selector节点返回成功;如果所有子节点都执行失败,Selector节点返回失败。
从左向右依次执行其子节点,当其任意子节点执行失败时停止继续执行。
如果任意子节点执行失败,Sequence节点返回失败;如果所有子节点都执行成功,Sequence节点返回成功。
简单来说,Selector节点即选择器节点,为了选择执行一系列子任务中的某一个而使用,故有一个子节点执行成功即可;Sequence节点为序列节点,是为了执行一个序列任务而使用的,所以需要序列中所有任务都执行成功,有一个执行失败都不行。
为简单并行节点,通常用来处理并发行为。运行执行两个子节点:一个必须为task节点A,另一个为一个分支树B。可以理解为,如果执行A任务,那么B也会执行,A是首要任务,B是次要任务。
AuxiliaryNode即辅助节点,主要分为Service(服务)和Decorator(装饰器)。
辅助节点提供了TickNode方法,也就是说这类节点是可以每帧(或一定时间间隔)都执行的。
Service即服务,行为树的Service节点被设计成用来更新AI“知识”的task节点的“后台”。
Service可以附着在复合节点或者task节点上,当Service节点下面的分支被激活时Service节点会被执行。
但与task节点不同,Service节点不会有任何返回值,也不会直接影响执行流程。
通常,它们通过Tice定期检查并向黑板中存储结果。
主要属性:
1)Interval Tick时间间隔;
2)RandomDeviation Interval的随机偏差;
3)bCallTickOnSearchStart控制初始时调用TickNode。
Decorate即装饰器,可以附着在复合节点或者Task节点上:
行为树中装饰器主要有2个作用:
1)用作条件判断;
2)控制行为树执行流的打断和跳转。
Decorator的ObserverAborts选项值控制其所发挥的作用,
当用作条件判断时,ObserverAborts值为None。此时若Decorator节点的判断条件满足,则会执行其所附着的复合节点或Task节点,反正则不执行。
另外,Decorator节点还有一个InverseCondition选项,其作用是另判断条件反转。
当我们需要自定义装饰器节点时,需要在节点的CalculateRawConditionValue()里编写条件判断逻辑。
ObserverAborts值不为None时,可实现中断正在执行的任务:
1)值为Self时,当条件不满足时,可以中断所在节点的任务和子任务,执行比所在节点优先级更低的任务和子任务;
2)值为LowPriority时,当条件满足时,可以中断比所在节点优先级更低的任务和子任务,执行所在节点的任务和子任务;
3)值为Both时所在节点和低优先级节点都会被中断。
需要注意,Decorator节点的中断模式受其父节点CompositeNode影响,CompositeNode包括Selector、Sequence、SimplepParallel,这些节点里提供了接口为Decorator返回中断模式:
CanAbortLowerPriority() 默认true
CanAbortSelf() 默认true
子类中重写了该方法:
Selector: CanAbortLowerPriority() true CanAbortSelf() true
Sequence: CanAbortLowerPriority() false CanAbortSelf() true
SimpleParallel: CanAbortLowerPriority() false CanAbortSelf() false
1)当父节点为Selector节点时,既能中断自己(Self),也能中断优先级更低的任务(LowPriority);
2)当父节点为Sequence节点时,只能中断自己(Self),不能中断优先级更低的任务(LowPriority)。
之所以不让Sequence中断优先级更低的任务,我的理解是因为如果从后面节点跳转到前面节点这样就破坏了sequence的执行顺序,违背了Sequence的设计初衷。
TaskNode节点是行为树的叶子节点,用于执行具体的实际行为。主要提供以下接口:
1)ExecuteTask():执行该任务,返回Succeeded, Failed 或者 InProgress,
如果返回的是InProgress,使用FinishLatentTask()方法。
2)AbortTask():中断任务,返回值为Aborted或InProgress,
如果返回的是InProgress,使用FinishLatentAbort()方法。
小结:
1)AI控制器拥有行为树和黑板组件,行为树和黑板配合使用;
2)BehaviorTreeManager统一管理世界所有AI的行为树;
3)行为树初始化按递归方式深度优先遍历整棵树;
4)Selector从左往右执行其子节点,直到一个达成,则 Select 达成并返回上层,否则失败并返回上层;
5)Sequence从左往右执行其子节点,直到一个失败,则 Sequence 失败并返回上层,否则达成并返回上层;
6)SimplepParallel通常用来处理并发行为,运行执行两个子节点:一个首要任务,一个次要任务;
7)Service和Decorator都可以附着在复合节点或者Task节点上;
8)Service节点不会有任何返回值,也不会直接影响执行流程;
9)Decorator节点可用于条件判断或者控制行为树执行流的打断和跳转;
10)ObserverAborts值为Self时,当条件不满足时,可以中断所在节点的任务和子任务,执行比所在节点优先级更低的任务和子任务;
11)ObserverAborts值为LowPriority时,当条件满足时,可以中断比所在节点优先级更低的任务和子任务,执行所在节点的任务和子任务;
12)ObserverAborts值为Both时所在节点和低优先级节点都会被中断;
13)当父节点为Selector节点时,Decorator既能中断自己(Self),也能中断优先级更低的任务(LowPriority);
14)当父节点为Sequence节点时,Decorator只能中断自己(Self),不能中断优先级更低的任务(LowPriority);
15)BTCompositeNode保存了身上所有的Service节点,以及所有的Children节点,Children节点包含CompositeNode\TaskNode\Decorate;
16)TaskNode必为叶子节点。
以上。