UE4行为树详解

UE4行为树详解(持续更新,才怪)

ksun

139 人赞同了该文章

前言

本文的目的是希望帮助开发者能更好地理解行为树执行顺序,并更合理的实现AI逻辑。而且尽量说人话

需要一定的基础,希望你至少做了几个简单AI。

很久没有碰AI开发了,我鸽了,对不起,此文请配合评论一起食用(我对AI开发的部分概念,理解有错误)

关于行为树的博客文章

这些都写的很棒哎。

[UE4][AI] 浅析UE4-BehaviorTree的特性102 赞同 · 10 评论文章​编辑

0.UML类图

下列类图描述了,UE4引擎中,Runtime/AIModule/Classes/BehaviorTree文件夹内,重要的类之间的关系。

图1:行为树核心类图

图2:UBTTaskNode类图

图3:UBTDecorator类图

图4:UBTService类图

图5:黑板值类型的类图


1.Composites 节点

这一小节,如果没完全看懂,可以先往后看。

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 时,则会等待并行任务执行结束。

多试验。

2.任务节点

任务节点的作用是做特定的事情,是描述谁在什么时间做了什么,在行为树的最底部(叶子节点),是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)

3.装饰器节点

装饰器是作用在行为树中的一种节点,作用是控制其他节点是否执行,或者打断正在执行的任务。

图9:BlackBoard装饰器

作为例子,引擎自带的BlackBoard装饰器(图9)的功能是,各种黑板值与填入的常量值做比较,根据比较结果控制修饰节点是否执行。

tips:ROOT下方的第一个Selector节点上方,装饰器的颜色不一样,因为这个装饰器是和RunBehavior这个任务节点,RunDynamicBehavior这个任务节点有关。我没有尝试过把复杂的行为树简化为多个子树,一般我不会在ROOT下方第一个Composites节点使用装饰器。

如何理解装饰器的ObserverAborts?

图10:装饰器的ObserverAborts,4个可选项

当装饰器上级的 Composites 节点是 Selector(图10),则 ObserverAborts 有4个可选值:

  • None
  • Self
  • LowerPriority
  • Both

图11:装饰器的ObserverAborts,2个可选项

当装饰器上级的 Composites 节点是 Sequence(图11),则 ObserverAborts 有2个可选值:

  • None
  • Self

解释4个选项的区别:

  • None:选了None的装饰器不起任何作用。个人认为是为了方便调试,你可以不用删除这个装饰器,选择None,可以忽略这个装饰器判断。
  • Self:(可以立刻停止自身的)若装饰器的计算结果为True,则执行修饰的任务,如果装饰器修饰Composites节点,则允许继续查找下级节点。如果装饰器结果是false,则不执行修饰的任务,如果这个任务修饰正在持续执行的任务,当装饰器计算结果由True改变为False时,当前任务会停止;如果装饰器修饰Composites节点,则不会继续查找下级节点。
  • LowerPriority:(可以立刻停止低优先级的)若当前装饰器计算结果为False,则不能执行当前修饰的节点,要执行比这个优先级低的节点。若装饰器计算结果True,会执行当前节点,并且会停止比当前节点低优先级的各种节点。进一步理解,当有一个任务是持续任务正在执行,修饰这个任务的 LowerPriority 装饰器,从True变成False,则不会停止持续任务,因为不是Self,但是如果这个任务结束了,下次再想执行,就不能执行了,因为装饰器是False。
  • Both:(既可以立刻停止自身的,又可以立刻停止低优先级的)单词意思是两者,哪两者?Self和Lower Priority。当这种装饰器为True,则可以执行当前节点,停止低优先级的节点。如果这装饰器为False,则停止当前修饰的节点,执行低优先级的节点。

看了上面糊涂没有呢,前两个还能明白,后面的优先级是什么意思?先来看看下面的例子(图12)。

图12:子树优先级,数字越小,优先级越高

每个节点的右上角有个小数字,数字越小优先级越高。

tips:一定要注意,放行为树的节点要整整齐齐,否则会出大问题,比如(图13),白色线段是按ABCD顺序执行,但实际上角标数字提示我们任务是按BACD顺序执行的。

图13

(挖坑NotifyObserver)


(挖坑服务)


其他:关于制作行为树的理解

  • Pawn或者Character。角色可以指定一个AIController控制,比如一个狼狗角色,可以切换AIController,既可以是攻击你的怪物,也可以是NPC,或者是你可以用命令控制的宠物。角色内可以封装和角色相关的行为,角色中也有很多关键的状态,比如自定义一个bBettle战斗状态,这个状态如果在行为树中会用到,则可以将这个状态作为副本放在黑板值中,但是注意,不要在角色类中操作黑板值的更新。
  • AIController。具备移动导航功能,感知相关功能,这个类用来驱动角色类行为,然后可以写修改黑板值的逻辑。比如,角色类中有个bBettle战斗状态,某个怪物感知到附近有攻击目标,AIController就会做2件事,设置角色的bBettle=True,设置黑板值BettleState==True。有时候你需要立刻停止正在执行的任务,可以调用UBehaviorTreeComponent::RestartTree方法。
  • 行为树资源。由各种通用的节点组成的逻辑脚本集合,行为树资源在内存中是唯一的,也就是说,多个角色会公用一个行为树资源。
  • 黑板资源和黑板值。用于传递数据。思考一下什么值需要变为黑板值存在呢?就是通用的用于判断子树或者任务能否执行的值,阻止做某件事,或者阻止做某件事的同时允许做某事。那就需要设置成黑板值了。黑板值可以简单的是角色属性的副本,更有可能是一个判断结果的临时保存。
  • 任务。具体要做的事。可以去更新黑板值,比如更新一个位置信息。调用角色内封装好的行为。个人建议,不要在任务中写复杂的关于角色内部要做的事。另外不要在任务中绑定角色的代理,因为这个任务对于所有角色是共享的。
  • 装饰器。条件判断,控制行为树子树的切换,控制是否执行节点。
  • 服务。间隔更新黑板值。或者间隔判断某个情况,再更新黑板值。
  • EQS:个人不建议在商业项目大规模使用。
  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UE4(Unreal Engine 4)是一款功能强大的游戏开发引擎,其中包括了行为编辑器,可以帮助开发者快速、灵活地创建和编辑游戏角色的行为UE4的行为编辑器是一个可视化的工具,可以让开发者通过拖拽、连接和调整节点来构建角色的行为逻辑。行为是一种形结构,由各种节点组成,每个节点代表一个行为或条件。开发者可以在编辑器中创建不同类型的节点,例如选择节点、序列节点、并行节点等,来表达角色的行为流程。节点之间可以通过连接线进行连接,形成一个完整的行为。 在行为编辑器中,开发者可以自定义节点的行为和参数。例如,可以为节点添加自定义的脚本或代码,来实现更复杂的行为逻辑。同时,还可以设置节点的优先级、执行顺序和条件,以及在不同情况下的跳转路径,来控制角色的行为流程。 行为编辑器还提供了一些常用的节点模板,例如移动、攻击、闲置等,开发者可以直接使用这些模板节点,然后根据自己的需求进行参数和行为的调整。此外,行为编辑器还支持多个角色之间的共享行为,可以实现多个角色共用同一套行为逻辑。 总之,UE4的自定义行为编辑器为开发者提供了一个强大的工具,可以快速构建和编辑游戏角色的行为逻辑。无论是简单的行为还是复杂的逻辑,都可以借助行为编辑器来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值