尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

UE5行为树实战:从零构建巡逻AI,掌握选择器与序列节点核心用法

UE5行为树实战:从零构建巡逻AI,掌握选择器与序列节点核心用法 1. 项目概述从零构建一个会巡逻的AI如果你刚开始接触虚幻引擎5的行为树看到那些“选择器”、“序列”、“装饰器”的节点是不是感觉有点无从下手别担心这很正常。行为树是UE5中构建AI逻辑的核心工具它用可视化的方式把AI的“思考过程”画了出来远比写一堆复杂的if-else判断要直观和强大。今天我们就来手把手搞定一个最经典、也最实用的AI案例一个会在地图上巡逻发现玩家后立刻追击玩家离开视线一段时间后又会乖乖回去巡逻的“守卫”。听起来复杂其实核心逻辑用行为树里的两个“合成节点”——选择器Selector和序列Sequence——就能轻松搭建起来。我保证跟着这篇保姆级教程走5分钟你就能理解它的运作骨架半小时内就能在编辑器里跑起来。这个巡逻AI的“大脑”由三部分组成行为树Behavior Tree负责决策流程黑板Blackboard负责存储AI知道的信息比如玩家在哪、我能不能看见他AI控制器AIController负责感知世界并更新黑板。我们的目标就是让这三者协同工作实现一个既聪明又稳定的AI伙伴或者对手。2. 核心思路拆解AI的“决策流程图”在动手之前我们必须先想清楚这个AI应该怎么“思考”。行为树本质上就是一个自上而下执行的决策流程图理解这一点至关重要。2.1 行为树的核心合成节点与执行流行为树的执行从根Root节点开始它决定了使用哪块“黑板”AI的记忆库。之后逻辑就交给了它的子节点通常是合成节点。我们这次的主角就是两个最基础的合成节点选择器Selector你可以把它理解成一个“或”逻辑。它会从左到右依次尝试执行它的子节点。只要有一个子节点执行成功它就立刻停止并向上级报告“我成功了”。如果所有子节点都失败它才报告失败。在我们的巡逻AI里它用来在“追击玩家”和“巡逻”这两个互斥的状态间做选择。序列Sequence这代表一个“与”逻辑。它同样从左到右执行子节点但要求每一个子节点都必须成功。只要有一个子节点失败整个序列就立刻停止并报告失败。我们用序列来组织一个状态内需要按顺序完成的一系列动作。关键理解选择器决定了“做什么”追击还是巡逻序列决定了“怎么做”追击的步骤或巡逻的步骤。行为树的强大之处在于高优先级的任务比如追击失败后AI会自动退而求其次执行低优先级任务比如巡逻整个过程无需我们写任何状态切换的代码。2.2 我们的AI决策逻辑基于上述原理我们的AI大脑设计如下最高优先级追击玩家。只要条件满足看见玩家就执行“追击”序列。最低优先级/后备行为巡逻。当“追击”的条件不满足或失败时就执行“巡逻”序列。条件判断通过“黑板”中的一个布尔值HasLineOfSight来判断是否看见了玩家。这个值由AI控制器的感知系统来更新。用伪代码表示就是根节点 - 选择器AI Root ├── 序列追击玩家[条件HasLineOfSight True] └── 序列巡逻[默认行为]这个结构清晰明了也是行为树最经典的模式之一。接下来我们就进入虚幻引擎把这张“思维图”画出来。3. 项目初始化与核心资产创建万事开头难但第一步走稳了后面就顺了。我们从一个干净的第三人称模板项目开始。3.1 创建AI专属资产首先在内容浏览器里为我们的AI角色创建一个独立的文件夹比如/AI把相关的蓝图都放进去方便管理。复制玩家角色找到模板自带的ThirdPersonCharacter蓝图复制一份到/AI文件夹重命名为BP_EnemyCharacter。这将是我们的AI实体。创建AI控制器在/AI文件夹中右键选择“蓝图类”然后搜索并选择AIController作为父类命名为BP_EnemyController。AI控制器是AI的“大脑”负责运行行为树和感知。配置AI角色打开BP_EnemyCharacter。删除里面所有现成的蓝图脚本比如移动输入我们需要一个纯净的、由AI控制的角色。在“组件”面板中找到CharacterMovement组件在细节面板中将Max Walk Speed最大行走速度设为120。这是AI巡逻时的速度。在蓝图工具栏点击“类默认值”在细节面板中找到“Pawn”类目下的AI Controller Class将其设置为BP_EnemyController。这样这个角色生成时就会自动被我们的AI控制器接管。布置导航网格在关卡中从“放置Actor”面板拖入一个Nav Mesh Bounds Volume。按R键进入缩放模式将其放大到覆盖整个你希望AI活动的区域。按下P键可以看到地面变成绿色这表示导航网格已生成AI可以在这里寻路移动了。3.2 设置AI的“记忆黑板”黑板是行为树和各任务节点之间通信的桥梁它存储着AI当前知道的所有“事实”。在/AI文件夹右键选择“人工智能” - “黑板”命名为BB_Enemy。打开BB_Enemy我们需要创建三个键KeyEnemyActor(对象类型)键类型选择Object基类选择Actor。这个键用来存储玩家角色的引用。HasLineOfSight(布尔类型)键类型选择Bool。这个键表示AI是否能看到玩家。PatrolLocation(向量类型)键类型选择Vector。这个键用来存储AI下一个要去巡逻的位置坐标。实操心得给黑板键起名时一定要清晰、准确。EnemyActor比Target更明确HasLineOfSight比CanSee更符合布尔值的命名习惯。好的命名是后期调试和团队协作的基础。4. 行为树框架搭建绘制AI的思维骨架现在我们来创建行为树并把刚才设计的逻辑画出来。创建行为树在/AI文件夹右键选择“人工智能” - “行为树”命名为BT_Enemy。打开它在“细节”面板中将Blackboard Asset设置为刚才创建的BB_Enemy。构建主干从左侧的“根”节点拖出连线添加一个**选择器Selector**节点。选中它在细节面板中将“节点名称”改为AI Root。这个节点将成为我们整个AI逻辑的决策核心。从AI Root节点拖出连线添加一个**序列Sequence**节点命名为Chase Player。这将是追击分支。再次从AI Root节点拖出连线在Chase Player的右侧添加另一个**序列Sequence**节点命名为Patrol。这将是巡逻分支。最后从AI Root再拖出一条线在最右边添加一个Wait任务节点将等待时间设为1.0。重要提示很多初学者会忽略最后这个Wait节点。AI Root是一个选择器它要求至少有一个子节点成功执行。在极端情况下如果Chase Player和Patrol两个序列都因为某些意外原因立即失败了选择器就会因为没有成功的子节点而失败导致行为树停止运行。添加一个永远会成功的Wait任务作为“保底”行为可以确保行为树在任何情况下都不会停止AI至少会在那里“发呆”。这是一个非常实用的稳定性技巧。现在的树结构应该是Root-AI Root (Selector)-Chase Player (Sequence)Patrol (Sequence)Wait (Task)。填充追击序列从Chase Player序列拖出连线添加一个Rotate to Face BBEntry任务节点。在细节面板中确认其Blackboard Key自动或手动设置为了EnemyActor。这个任务会让AI旋转面向黑板中存储的EnemyActor即玩家。接下来我们需要一个自定义任务来改变AI的移动速度。点击行为树编辑器工具栏的“新建任务”按钮创建一个新的任务蓝图命名为BTT_ChasePlayerBTT是行为树任务的前缀。我们先创建它具体逻辑稍后编写。在Chase Player序列中在旋转任务之后添加我们刚创建的BTT_ChasePlayer任务节点。最后添加一个Move To任务节点将其Blackboard Key设置为EnemyActor。这样AI就会朝玩家移动。填充巡逻序列从Patrol序列拖出连线添加一个新的自定义任务命名为BTT_FindRandomPatrol。这个任务负责为AI寻找一个随机的巡逻点。然后添加一个Move To任务节点将其Blackboard Key设置为PatrolLocation。AI将前往BTT_FindRandomPatrol任务设置的位置。最后添加一个Wait任务节点将等待时间设为4.0随机偏差设为1.0。这样AI到达巡逻点后会等待3到5秒4±1秒然后再开始下一次寻路。至此行为树的视觉框架就搭建完毕了。它清晰地表达了我们的意图AI优先尝试追击旋转、加速、移动如果追击条件不满足或失败则尝试巡逻找点、移动、等待如果连巡逻都失败就在原地等待1秒后重试。5. 编写自定义任务赋予AI行动细节框架有了现在需要为两个自定义任务注入灵魂。5.1 任务一追击玩家时加速BTT_ChasePlayer这个任务的核心是修改AI角色的移动速度。打开BTT_ChasePlayer任务蓝图。在事件图表中右键添加事件Event Receive Execute AI。这个事件在行为树执行该任务时触发。从Event Receive Execute AI节点的Controlled Pawn引脚拖出添加一个Cast To BP_EnemyCharacter节点。这是为了安全地获取我们控制的AI角色。我们需要在AI角色蓝图中创建一个函数来修改速度而不是直接在任务里修改。这是一种更规范的做法。打开BP_EnemyCharacter蓝图。在“我的蓝图”面板点击“函数”旁边的号新建一个函数命名为UpdateWalkSpeed。在函数细节面板点击“输入”旁边的号添加一个float类型的输入命名为NewWalkSpeed。在函数图表中从“组件”列表拖出CharacterMovement组件从其引脚搜索并调用Set Max Walk Speed节点。将函数的输入引脚NewWalkSpeed连接到Set Max Walk Speed节点的New Max Walk Speed引脚。回到BTT_ChasePlayer任务蓝图。在Cast To BP_EnemyCharacter成功后的引脚调用UpdateWalkSpeed函数。我们需要为这个速度值设置一个变量方便调整。右键点击UpdateWalkSpeed节点的NewWalkSpeed输入引脚选择“提升为变量”将变量命名为ChaseSpeed。在“我的蓝图”的变量面板中选中ChaseSpeed在细节面板中勾选“实例可编辑”。这样我们就能在行为树编辑器中直接修改这个任务的追击速度了。将ChaseSpeed变量连接到UpdateWalkSpeed函数的输入。然后从UpdateWalkSpeed的执行引脚连出添加一个Finish Execute节点并确保其Success引脚被勾选表示任务成功完成。异常处理从Cast To BP_EnemyCharacter节点的Cast Failed引脚连出添加另一个Finish Execute节点不要勾选Success。这表示如果控制的Pawn不是我们的AI角色任务就失败。注意事项为什么要在角色蓝图里写函数而不是在任务里直接Set Max Walk Speed这涉及蓝图的设计原则。任务节点应该专注于调度和决策具体的角色属性修改应该由角色自身负责。这样降低了耦合度如果未来移动逻辑变了只需要改角色蓝图而不用修改所有相关的任务节点。5.2 任务二寻找随机巡逻点BTT_FindRandomPatrol这个任务负责在AI周围随机找一个能走到的位置并存入黑板。打开BTT_FindRandomPatrol任务蓝图。同样添加Event Receive Execute AI和Cast To BP_EnemyCharacter。在转换成功后首先调用UpdateWalkSpeed函数是的和追击任务用同一个。将输入值提升为变量命名为PatrolSpeed设为125.0并勾选“实例可编辑”。这样巡逻时速度会稍慢。从Controlled Pawn引脚拉出调用Get Actor Location获取AI当前位置。搜索并添加Get Random Reachable Point in Radius节点。将上一步获取的Location连接到其Origin引脚。这个函数会在以Origin为中心、指定Radius的球形范围内寻找一个导航网格上可以到达的随机点。将Radius提升为变量命名为PatrolRadius默认值设为1000.0单位厘米即10米并勾选“实例可编辑”。从Get Random Reachable Point in Radius节点的Return Value引脚拉出添加一个Branch分支节点。这个返回值是bool类型True表示成功找到了随机点。在分支的True方向添加一个Set Blackboard Value as Vector节点。将其Key提升为变量命名为PatrolLocationKey并在细节面板将其默认值设置为我们在黑板中定义的PatrolLocation键通常可以从下拉列表选择。将Get Random Reachable Point in Radius节点的Random Location输出引脚连接到Set Blackboard Value as Vector的Value引脚。这样就把找到的随机位置存入了黑板。最后连接Finish Execute成功。在分支的False方向即没找到随机点我们可以让AI待在原地。再添加一个Set Blackboard Value as Vector节点使用相同的PatrolLocationKey。将Get Actor Location获取的AI当前位置连接到其Value引脚。然后连接Finish Execute成功。这样即使找不到新点AI也不会卡住任务依然成功只是移动的目标点是当前位置相当于等待。同样不要忘记处理Cast Failed的情况连接一个失败的Finish Execute。6. 配置AI控制器为AI装上“眼睛”AI控制器是连接感知输入和行为树决策的枢纽。我们需要让它能“看到”玩家并根据看到的结果更新黑板。打开BP_EnemyController蓝图。添加事件Event On Possess。当这个控制器接管Possess一个Pawn我们的AI角色时这个事件触发。从该事件拉出搜索添加Run Behavior Tree节点将其BTAsset设置为BT_Enemy。这样AI角色一生成就会开始运行我们设计好的行为树。在“组件”面板点击添加搜索并添加AIPerceptionComponentAI感知组件。选中AIPerceptionComponent在细节面板找到“AI感知”类目点击号添加一个AI Sight Config视觉配置。在AI Sight Config中展开Detection by Affiliation勾选Detect Neutrals。因为我们的玩家角色默认是中立阵营这样AI才能“看见”他。在AIPerceptionComponent的细节面板找到“事件”部分点击On Target Perception Updated旁边的号。这个事件在感知到任何Actor比如看到或失去视线时触发。在事件图表中我们需要处理这个事件从Actor引脚拉出添加Actor Has Tag节点将Tag设置为Player。我们稍后会给玩家角色打上这个标签用于过滤感知到的目标确保AI只对玩家有反应。从Stimulus引脚拉出添加Break AIStimulus节点获取这次感知的详细信息。添加一个Branch节点。将其条件设置为Actor Has Tag返回True并且Break AIStimulus节点的Successfully Sensed引脚为True。这表示“成功感知到了一个带有Player标签的Actor”即“看到了玩家”。处理“看到玩家”Branch为True的情况首先我们需要取消可能正在进行的“丢失视线”计时器。添加一个Get节点获取我们之后会创建的计时器句柄变量例如叫LOSTimerHandle然后连接Clear and Invalidate Timer by Handle节点。然后获取黑板并更新它。添加Get Blackboard节点如果之前没创建过黑板变量需要先创建一个Blackboard Key类型的变量并赋值BB_Enemy。从黑板拉出连续添加两个Set Value as节点一个类型选Bool键名设为HasLineOfSight值设为True另一个类型选Object键名设为EnemyActor值连接到On Target Perception Updated事件的Actor引脚。处理“丢失玩家视线”Branch为False或者Successfully Sensed为False的情况这表示AI这次更新没有看到玩家或者玩家离开了视线。我们需要设置一个计时器如果一段时间后仍然没看到玩家就认为玩家真的“丢失”了AI应该停止追击。添加Set Timer by Event节点。将其Time提升为变量命名为LOSTimeout默认值设为4.0秒。将其Return Value提升为变量命名为LOSTimerHandle用于存储计时器引用。创建一个自定义事件例如叫OnLOSTimeout并将其连接到Set Timer by Event的Event引脚。在这个自定义事件里我们需要将黑板中的HasLineOfSight设为False并将EnemyActor清空设为None。关键点在Branch的True分支里我们清除了这个计时器。这意味着只要AI持续看到玩家这个“丢失计时器”就会被不断重置永远不会触发。只有当玩家离开视线计时器才会开始走4秒后触发OnLOSTimeout事件AI才放弃追击。7. 最终联调与装饰器应用所有零件都准备好了现在进行最后的组装和调试。标记玩家打开模板自带的玩家角色蓝图ThirdPersonCharacter。在类默认值的细节面板中找到“Actor”类目下的Tags点击号添加一个标签输入Player。这样AI的感知系统就能通过标签识别出玩家。配置AI角色旋转打开BP_EnemyCharacter在类默认值的细节面板中找到“Pawn”类目勾选Use Controller Rotation Yaw。这样当行为树执行Rotate to Face BBEntry任务时AI角色才会正确地旋转身体朝向玩家。为行为树添加条件装饰器这是让整个逻辑运转起来的最后一步。打开行为树BT_Enemy。右键点击Chase Player序列节点选择“添加装饰器 - 黑板”。选中新添加的装饰器在细节面板中进行关键设置Observer Aborts设置为Both。这是最重要的设置。它意味着当这个装饰器监视的黑板键值发生变化时它不仅会中止自己所在的低优先级分支Chase Player也会中止其他正在运行的低优先级任务。简单说就是“看见玩家就立刻打断巡逻去追击看不见了就立刻停止追击回去巡逻”。Blackboard Key选择HasLineOfSight。Key Query选择Is Set。这意味着当HasLineOfSight这个键的值被设置不是默认值且为True时条件通过。你可以把这个装饰器的“节点名称”改成Has Line of Sight?这样更直观。设置任务参数在行为树编辑器中选中我们创建的两个自定义任务节点BTT_ChasePlayer和BTT_FindRandomPatrol在细节面板中可以看到我们之前设置为“实例可编辑”的变量ChaseSpeed,PatrolSpeed,PatrolRadius。在这里你可以根据需求调整数值比如把ChaseSpeed调到500让AI跑得更快。现在点击编辑器中的“运行”按钮。你应该能看到AI角色在场景中开始巡逻。控制玩家角色靠近AI一旦进入其视野范围AI会立刻转身加速向你冲来。如果你跑开并躲起来AI会在原地徘徊寻找你大约4秒后放弃追击重新开始随机巡逻。8. 常见问题与深度优化指南第一次搭建成功很有成就感但在实际项目中你肯定会遇到各种问题。这里我总结了一些常见坑点和进阶优化思路。8.1 调试与问题排查AI不动检查导航网格按下P键确认AI所在位置和目标位置是否在绿色的导航网格区域内。导航网格体积是否覆盖了所有需要移动的区域检查控制器在关卡中选中AI角色查看“世界大纲”中它是否被正确的BP_EnemyController所“占有”Possessed。检查行为树在运行时打开行为树资产(BT_Enemy)可以看到节点高亮显示执行状态通常为绿色。观察执行流是否卡在某个节点。同时观察“黑板”面板看HasLineOfSight、EnemyActor、PatrolLocation等键的值是否正确更新。AI不追击玩家检查玩家标签确认玩家角色蓝图上的Player标签是否拼写正确大小写敏感。检查感知配置确认AIPerceptionComponent中的Detect Neutrals已勾选。检查装饰器确认Chase Player序列上的黑板装饰器Observer Aborts是否为BothKey Query是否为Is Set。检查视线AI的视觉有距离和角度限制。在AIPerceptionComponent的AI Sight Config中调整Sight Radius视野半径和Peripheral Vision Half Angle Degrees周边视觉半角。AI行为怪异抽搐、原地转圈检查移动公差Move To任务节点有一个Acceptable Radius接受半径参数。默认值可能太小AI会试图精确到达某个点而不断微调。可以适当调大如50-100。检查旋转确保BP_EnemyCharacter的Use Controller Rotation Yaw已启用。8.2 性能与设计优化建议将速度修改逻辑改为服务节点我们目前是在BTT_ChasePlayer和BTT_FindRandomPatrol任务中修改速度。但更优雅的做法是使用服务节点。你可以创建一个BTS_UpdateSpeed服务附加到Chase Player和Patrol序列下的Move To任务上。服务节点会在其所属分支执行期间以一定频率持续运行。这样速度管理会更集中逻辑也更清晰。使用预设巡逻路径代替随机点对于需要固定路线的守卫随机点并不合适。可以创建一个BP_PatrolPath蓝图里面包含一个Spline组件样条线或一个Actor数组作为路径点。让BTT_FindRandomPatrol任务改为按顺序或随机获取这些预设点这样AI的巡逻路线就是可控的。追击最后已知位置现在的AI会直接冲向玩家的实时位置。更智能的做法是当玩家离开视线时让AI移动到玩家最后被看到的位置Last Known Position并在那里搜索一会儿。这需要在AI控制器的On Target Perception Updated事件中当Successfully Sensed为False但之前看到过玩家时记录下Stimulus中的Stimulus Location刺激位置到一个新的黑板键如LastKnownLocation然后修改行为树让追击分支的Move To目标指向这个位置。添加攻击行为当AI通过Move To成功接近玩家后可以通过Move To节点的On Success引脚或者添加一个Is At Location装饰器来判断可以在Chase Player序列后面再接一个“攻击”序列。这个序列可以播放攻击动画、调用造成伤害的接口等。记得在攻击行为前后用服务节点管理AI的移动和旋转。行为树就像搭积木掌握了选择器和序列这两个最基础的“积木块”你就已经能构建出绝大多数游戏AI的决策逻辑了。剩下的就是根据具体需求设计更复杂的任务、装饰器和服务不断丰富AI的“技能树”。多动手实验多观察运行时行为树的执行流你会越来越得心应手。
返回列表