赞
踩
139 人赞同了该文章
本文的目的是希望帮助开发者能更好地理解行为树执行顺序,并更合理的实现AI逻辑。而且尽量说人话。
需要一定的基础,希望你至少做了几个简单AI。
很久没有碰AI开发了,我鸽了,对不起,此文请配合评论一起食用(我对AI开发的部分概念,理解有错误)
这些都写的很棒哎。
[UE4][AI] 浅析UE4-BehaviorTree的特性102 赞同 · 10 评论文章编辑
下列类图描述了,UE4引擎中,Runtime/AIModule/Classes/BehaviorTree文件夹内,重要的类之间的关系。
图1:行为树核心类图
图2:UBTTaskNode类图
图3:UBTDecorator类图
图4:UBTService类图
图5:黑板值类型的类图
这一小节,如果没完全看懂,可以先往后看。
Sequence节点
Sequence节点。顺序节点,依次执行下级节点,若下级的所有节点都返回 Succeeded,则Sequence节点本身返回 Succeeded;若任何一个下级节点返回 Failed,则停止执行后续的下级节点,并且Sequence节点本身返回 Failed;如果 Sequence 节点下方没有任务节点,返回 Failed。
Selector节点
Selector节点。选择节点,从左到右依次选择执行下级节点,若有任何一个节点返回 Succeeded,则停止执行后续下级节点,并返回 Succeeded;若全部的下级节点都返回 Failed,则此 Selector返回 Failed;如果 Selector 节点下方没有任务节点,返回 Failed。
ApplyDecoratorScope选项
Sequence 和 Selector 都有 ApplyDecoratorScope 选项,意思是开启装饰器的作用域,当勾选 ApplyDecoratorScope 则这个Composites下级节点的装饰器,若执行状态不在所处的作用域,则装饰器是无效的。
为了更直观理解这个选项,你可以自己动手做一个例子(图15)。在执行“第二个Wait”时,若改变黑板值,令黑板值等于1,因为 ApplyDecoratorScope 等于 True,无法切换到“第一个Wait”。
SimpleParallel节点。并行节点,左边紫色的部分,必须连接一个任务节点,可称这个任务为“主要任务”,右边灰色的部分可以连 Composites 节点或者任务节点,是与主要任务并行执行(同时执行)逻辑所在的位置。这也意味着你可以并行节点的并行部分再放并行节点。
当主要任务返回 Succeeded,此SimpleParallel 节点返回 Succeeded;若主要任务返回 Failed,此 SimpleParallel 返回 Failed;SimpleParallel 的返回结果和并行部分的结果没有关系。
并行节点有一个 FinishMode 选项。若是 Immediate 时,则主要任务停止,并行任务立刻停止。若是 Delayed 时,则会等待并行任务执行结束。
多试验。
任务节点的作用是做特定的事情,是描述谁在什么时间做了什么,在行为树的最底部(叶子节点),是AI行为的终点。比如,移动到你的左边10cm,转身面向猎空,发出2次“哇”的声音。
你可以做任何事。
每个任务都有执行时间,目标,任务结束时的状态。我们可以把任务节点的执行时间简单的分为2种:
任务节点的状态由 EBTNodeResult 决定:
- UENUM(BlueprintType)
- namespace EBTNodeResult
- {
- // keep in sync with DescribeNodeResult()
- enum Type
- {
- Succeeded, // finished as success
- Failed, // finished as failure
- Aborted, // finished aborting = failure
- InProgress, // not finished yet
- };
- }
在蓝图版本(继承 BTTask_BlueprintBase)的任务节点中,如果在执行任务第一时间就调用FinishExecute() 或 FinishAbort(),那就是瞬时任务(图6),否则就是持续时间的任务(图7)。
图6:打印完成后立刻结束(瞬间)
图7:持续5秒后结束(持续)
图8:蓝图版本的状态对应关系
在写C++的任务时,瞬间完成的任务这样写 ↓
- // 瞬间完成的任务
-
- EBTNodeResult::Type UBTTask_MyTask::ExecuteTask(UBehaviorTreeComponent& OwnerComp,
- uint8* NodeMemory)
- {
- // do something
- return EBTNodeResult::Succeeded;
- // return EBTNodeResult::Failed;
- // return EBTNodeResult::Aborted;
- }
持续一段时间的任务这样写 ↓
- // 持续一段时间的任务
-
- UBTTask_MyTask::UBTTask_MyTask()
- {
- bNotifyTick = true; // 执行TickTask()的必要设置
- }
-
- void UBTTask_MyTask::TickTask(UBehaviorTreeComponent& OwnerComp,
- uint8* NodeMemory, float DeltaSeconds)
- {
- // check 一段时间后结束
- if(/*满足条件*/) FinishLatentTask(*BehaviorComp, EBTNodeResult::Succeeded);
- }
既然有持续一段时间的任务存在,那必然有打断这种任务的情况发生。
这就需要完全理解装饰器的功能了。
(挖坑IgnoreRestartSelf)
装饰器是作用在行为树中的一种节点,作用是控制其他节点是否执行,或者打断正在执行的任务。
图9:BlackBoard装饰器
作为例子,引擎自带的BlackBoard装饰器(图9)的功能是,各种黑板值与填入的常量值做比较,根据比较结果控制修饰节点是否执行。
tips:ROOT下方的第一个Selector节点上方,装饰器的颜色不一样,因为这个装饰器是和RunBehavior这个任务节点,RunDynamicBehavior这个任务节点有关。我没有尝试过把复杂的行为树简化为多个子树,一般我不会在ROOT下方第一个Composites节点使用装饰器。
如何理解装饰器的ObserverAborts?
图10:装饰器的ObserverAborts,4个可选项
当装饰器上级的 Composites 节点是 Selector(图10),则 ObserverAborts 有4个可选值:
图11:装饰器的ObserverAborts,2个可选项
当装饰器上级的 Composites 节点是 Sequence(图11),则 ObserverAborts 有2个可选值:
解释4个选项的区别:
看了上面糊涂没有呢,前两个还能明白,后面的优先级是什么意思?先来看看下面的例子(图12)。
图12:子树优先级,数字越小,优先级越高
每个节点的右上角有个小数字,数字越小优先级越高。
tips:一定要注意,放行为树的节点要整整齐齐,否则会出大问题,比如(图13),白色线段是按ABCD顺序执行,但实际上角标数字提示我们任务是按BACD顺序执行的。
图13
(挖坑NotifyObserver)
(挖坑服务)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。