7 - AI 入门
本文将初步介绍 UE5 中有关人工智能系统的入门知识,例如 AI 控制器 AIController
,黑板 Blackboards
和行为树的概念及简单使用。
AI Controller
PlayerController
和 AIController
这两个 Actor
类都继承自 AController
基类,AController
可以用来控制 Pawn
或者 Character
的动作。玩家控制器依赖于实际玩家的输入,而 AI 控制器通过运用人工智能来操控其拥有的角色,并根据设定好的规则对环境作出反应。一个 AI 控制器可由相同 AI Pawn 的多个实例使用,并且相同的 AI 控制器可在不同的 AI Pawn 类中使用。
主动拥有 Pawn
在 C++ 中,可用 Possess()
来拥有一个 Pawn:
void AController::Possess(APawn* InPawn);
可用 UnPossess()
解除 Pawn 的拥有状态:
void AController::UnPossess();
在调用 Possess()
或 UnPossess()
的同时,也会触发 OnPossess()
或 OnUnPossess()
回调。
PawnSensing 组件
该组件赋予被控制的 Pawn
的感知功能,它可以从视觉和听觉两方面感知其他 Pawn
的存在,并作出相应反应。
当 AI 沿着其视线看到 Pawn
类的实例时,会触发 OnSeePawn()
事件;当 AI 检测到由某 Actor
中的 Pawn Noise Emitter
组件发出的特定噪音时,会触发 OnHearNoise()
事件。
导航网格体 NavMesh
UE5 通过导航网格体 NavMesh 告诉人工智能哪些环境是可导航的,而哪些环境是不可导航的。UE5 还支持动态的 NavMesh,它支持动态对象在环境中移动时实时更新 NavMesh,这使得 AI 能够识别环境变化,并适应导航路径。
在引擎中拖入一个 NavMesh,让它和地面相交,然后按下 P 键启用导航 Debug 视图,出现的绿色便是可导航的区域。效果如下:
RecastNavMesh
在创建 NavMesh 的同时,一个名字为 RecastNavMesh-Default
的 Actor 被自动创建。这个 RecastNavMesh
可以被视为 NavMesh 的 “大脑”,它包含了调整 NavMesh 所需的参数,这些参数将直接影响 AI 如何在给定区域中导航。
RecastNavMesh
包含的参数很多,常用到的参数如下:
- 显示 Display:影响可视化调试
NavMeshBoundsVolume
生成的导航区域,方便 debug。 - 生成 Generation:决定 NavMesh 如何生成,以及确认哪些区域是可导航的,哪些不是。常用到的参数如下:
- Cell Size:NavMesh 在给定空间中生成可导航区域的精度;
- Agent Radius:在此区域导航的 Actor 的半径。
- Agent Height:在此区域导航的 Actor 的高度。
- Agent Max Slope:游戏世界中可能存在的倾斜角度。
- Agent Max Step Height:AI 可以导航到的台阶高度,以楼梯台阶为单位。
GetRandomPointInNavigableRadius
如果想要获取导航网格体内的随机一点,需要使用 GetRandomPointInNavigableRadius()
函数。
行为树和黑板
行为树和黑板协同工作,允许 AI 遵循不同的逻辑路径,并根据各种条件和变量做出决策。
黑板
黑板是定义变量(也被称为键 Key)的地方,方便行为树使用这些变量做决策。
行为树
行为树是一种可视化编程工具,可以根据某些因素和参数告诉 Pawn 该做什么。它由一组对象组成,即 组合器(Composites),任务(Tasks),服务(Services)和装饰器(Decorators),它们共同定义 AI 将如何根据设置的条件和逻辑流程进行行为响应。
组合器 Composites
组合器节点告诉行为树如何执行任务和其他操作,也能附加装饰器和服务,以便在执行行为树分支前应用可选条件。
常见的组合器节点如下:
Selector:Selector 节点按从左到右的顺序执行其子任务,并在其中一个子任务成功时停止执行之后的子任务。
此外,能在节点右上角处发现数字,这是节点的执行顺序,一般是从上到下、从左到右。
Sequence:Sequence 节点从左到右顺序执行其子任务,当其中一个子任务失败将停止执行之后的子任务。
Simple Parallel:可同时执行任务和新的独立逻辑分支。例如下图,Wait 任务执行的同时 Sequence 也在执行:
在 “细节” 面板中可以调节该节点的
Finish Mode
参数:Immediate
:该节点将在主任务完成后成功完成。例如上图,Wait 任务完成后后面的 Sequence 会自动停止。Delayed
:该节点将在主任务和后面的任务完成后才成功完成。
任务 Tasks
这里提供 AI 可以完成的任务,UE5 默认内置了一些任务,包括移动到特定位置,旋转以面对目标,发射武器等。也能通过在蓝图或 C++ 中创建自己的任务。
例如下图中定义了一个任务,从 ReceiveExecuteAI 事件开始,基于 AI 自己 Pawn 的位置随机生成一个新位置,然后在 FinishExecute 结束,并返回是否成功。
装饰器 Decorators
装饰器是可以添加到任务或组合器的条件,通常用于实现分支逻辑。除了使用内置的装饰器外,也能通过蓝图自定义装饰器。
服务 Services
和装饰器比较相似,也能添加到任务或组合器间,但服务允许我们基于它定义的间隔执行节点分支。
要想运行行为树,可在 AIController 中的 BeginPlay 中调用 RunBehaviorTree:
实践
AI 寻路
接下来尝试实践一下,实现 AI 按巡逻点寻路的功能。
首先,新建一个基于 Actor 的蓝图类 BP_AIPoints
,用于存储巡逻点。在这个类上新建一个 vector 数组 Points
,勾选 “实例可编辑” 和 “显示 3D 控件”。然后新建一个函数 GetNextPoint
,用于在 Points
中获取一个巡逻点,这个巡逻点是本地坐标的,因此要转换为世界坐标:
然后将这个蓝图类放到地图上,在细节面板中添加数个巡逻点,然后在地图上摆好:
接下来要让敌人的蓝图类引用这个巡逻点实例,新建一个 PatrolPoints
变量,引用 BP_AIPoints
,别忘了勾选实例可编辑。然后在地图上点击敌人,绑定引用变量。接下来实现敌人的行为树:
其中找到巡逻点的函数如下:
最后编译好蓝图就行了。
AI 感知
除了巡逻外,AI 还能够通过视线和听力来感知玩家,这里将实现如下两个功能:
- AI 对玩家的视觉感知;
- AI 对玩家发出噪音的听觉感知;
首先要在对应 AI Controller 中添加 Pawn Sensing
组件,用于处理视觉感知和听觉感知。
然后实现相关事件:
敌人感知到玩家的
OnSeePawn()
事件:敌人感知到 Pawn 时,如果这个 Pawn 是玩家,就会更新它的玩家角色对象引用黑板变量,方便行为树的后续敌人追捕玩家操作。
-
敌人听到玩家发出的噪声时,如果噪声距离敌人的位置在敌人的听觉范围内,就会更新噪声位置黑板变量,方便行为树的后续敌人前往噪声地点操作。
接下来处理敌人看到玩家的行为树。敌人看到玩家后应该要追捕玩家,追到后对玩家造成伤害,之后原地等待并重置看到的玩家:
其中,Damage Player
节点负责对玩家造成伤害,它的任务 BTTask_DoAttack
的实现如下:
Reset Player Seen
节点负责清除敌人看到的玩家这一对象引用黑板变量:
最后处理敌人听到玩家噪声的行为树。敌人听到玩家发出的噪声后,如果敌人处在噪声范围内,就会前往噪声位置调查,等待后重置听到玩家的状态:
当然,别忘了给玩家 Character
添加发出噪声的 Pawn Noise Emitter
组件:
然后在该发出噪声的逻辑中调用该组件的 Make Noise
函数,例如下图在玩家按下鼠标左键开火时发出噪声:
运行时生成 AI Actor
可以使用 SpawnAIFromClass()
函数在运行时生成 AI Actor,需要指定生成的 Pawn
,使用的行为树 Behavior Tree
,变换等:
参考资料
中文翻译:《UE5 C++ 游戏开发完全学习教程》
英文原版:《Elevating Game Experiences with UE5》