搭建AI行为树
本文最后更新于23 天前,其中的信息可能已经过时,如有错误请留言
  • Shift + F1 运行游戏时释放鼠标,让你可以点编辑器里的其他窗口 / 蓝图 / 行为树 / 动画蓝图,但游戏不会退出。
  • Alt + 左键复制巡逻点、
  • Ctrl + Space 快速打开Content Drawer
  • 节点的颜色:蓝图基础 – Epic 维基
  • 找不到蓝图:
    • Window – My BluePoint

事件介绍

Task

Event Receive Execute AI

  • Cast To XXX
    • 把一个“泛型对象引用”尝试转换成你自己的 XXX 类型
  • 行为树的所有Task都以这个事件为起点(至少目前是),Event Receive Execute AI 当行为树执行到这个 BTTask 时触发
    • Owner Controller = 执行这个任务的 AIController
      Controlled Pawn = 这个 AIController 控制的 NPC
  • 所以Task是能直接获取到Controller和Controller所在的NPC的所有信息的,因此通过函数获取

当然在这之前会做一步转换cast to NPC:

AI Controller

Event On Possess

  • NPC_AIController是挂在一个Actor上的,可以通过下面这个事件将其声明的变量初始化

On Target Perception Updated (AIPerception)

  • 当 AI 对某个目标的感知状态发生变化时触发,也因此要在每个perception内部判断是否为当前的感知类型
  • 两个参数:
    Actor :被感知到的对象,比如NPC看到玩家,但它不一定永远是玩家,所以通常还要判断:Actor Has Tag “Player”
  • Stimulus:表示这次感知的详细信息。

虚幻引擎术语:

https://dev.epicgames.com/documentation/unreal-engine/unreal-engine-terminology

  • Actor = 场景里所有能存在的对象(Gameobject?)
  • 类型转换(Casting):将会提取特定类的Actor并尝试将其作为其他类进行处理。类型转换可能成功,也可能失败。如果类型转换成功,则可以在你类型转换到的Actor上访问特定于类的功能
  • 组件(Component) 是一种可以添加到Actor的功能
  • 玩家控制器(Player Controller):获取玩家输入,并将其转换到游戏内的互动中。每个游戏内部都至少具有一个玩家控制器。玩家控制器通常操控一个Pawn或角色作为玩家在游戏中的呈现方式。
  • AI控制器(AI Controller):就像玩家控制器操控Pawn作为玩家在游戏中的呈现方式,AI控制器(AI Controller)操控Pawn在游戏中呈现非玩家角色(NPC)。默认情况下,Pawn和角色都以基本AI控制器终结,除非它们被玩家控制器专门操控或者收到指令不允许为自己创建AI控制器。
  • 玩家状态(Player State):是游戏参与者在游戏中的状态,例如人类玩家或模拟玩家的机器人。非玩家AI作为游戏世界的一部分而存在,没有玩家状态。
  • 玩家状态可能包含的玩家信息示例包括:名称、当前级别、血量
  • 游戏模式(Game Mode) 设置要运行的游戏的规则。这些规则可以包括:
    • 玩家如何加入游戏。
    • 游戏是否可以暂停。
    • 任何游戏特定行为,例如获胜条件。
  • 笔刷(Brush):是用于描述3D形状的Actor,例如立方体或球体。可以将笔刷放置在关卡中以定义关卡几何体(这些几何体称为二进制空间分区或BSP笔刷)。例如,如果要快速封锁关卡,此功能非常有用。
  • 体积(Volumes):是带有边界的3D空间,根据连接到体积的效果,具有不同的使用方法。例如:
    • 阻挡体积(Blocking Volumes) 是可见的,用于阻止Actor通过它们。
    • 施加伤害体积(Pain Causing Volume) 对与其重叠的任何Actor造成持续伤害。
    • 触发器体积(Trigger Volumes) 的编程方式为,在Actor进入或退出体积时触发事件
  • 关卡(Unity的Scene)

蓝图

我理解蓝图就是可视化脚本,理由是首先文档中也提到“蓝图或脚本”的说法,说明是平级概念,其次蓝图中的事件图表,其功能很像Unity中的生命周期函数

蓝图的类型

  • 蓝图类型
    • 最常使用的蓝图类型是 关卡蓝图 和 蓝图类
    • 完整类型列表请参见蓝图类型.
  • 关卡蓝图
    • 关卡蓝图包含地图中关卡特有事件的逻辑。
      每个关卡都有一个关卡蓝图
    • 一些用途:引用和操控关卡中的Actor、使用关卡序列Actor控制过场动画、管理关卡流送
    • 关卡蓝图还可以与关卡中放置的蓝图类交互,例如读取变量和触发自定义事件。更多信息请参阅关卡蓝图
  • 蓝图类:
    • 蓝图类定义了可以作为实例放入地图中的新类或Actor类型。编辑整个项目中使用的蓝图类将更新其所有实例。
    • 蓝图类是创建门、开关、可收集物品、可摧毁场景等交互资源的理想类型。更多详情请参见蓝图类

动画蓝图不属于上面的任何一个类型蓝图的子类

图表(Graph)

  • 是一个节点网络,可连接到另一个节点网络以定义该网络的执行流
  • 每个蓝图都可以包含一个或多个图表,具体取决于蓝图类型,这些图表定义了蓝图特定方面的实现。蓝图中的各个图表也可以包含 子图表,这些子图表本质上是折叠到其自身单独图表中的节点集合,主要用于组织用途
  • Graph 就是蓝图里的“代码区域”,只不过 UE 不是写代码,而是用节点和连线来写逻辑

界面

https://dev.epicgames.com/documentation/unreal-engine/blueprints-visual-scripting-user-interface-for-blueprint-classes-in-unreal-engine

  • 3.组件
    • 在向Actor添加组件后,该ActorIU可以使用该组件提供的功能
    • 组件必须附加到Actor,不能独立存在
    • 组件事件:
      可以通过不同的方法将基于组件的事件和/或功能快速添加到蓝图的 事件图表(Event Graph)
      • 添加可以为其创建事件的组件,例如盒体组件(BoxComponent)
      • 在 细节(Details) 面板中,单击 添加事件(Add Event) 按钮并选择所需的事件类型
      • 无论采用哪种方式,都会将一个新的事件节点(基于选定类型)添加到 事件图表(Event Graph),该图表将自动打开
  • 4.我的蓝图(My Blueprint)
    • 选项卡显示了蓝图中图表、脚本、函数、宏等内容的树状列表。本质上其是蓝图的轮廓,以便让使用者更加便捷地查看蓝图现有元素或创建新元素
    • 右键可查看变量的引用位置

动画蓝图

事件图表(Event Graph)

  • 每个动画蓝图都有单个 EventGraph
  • 使用一组与动画相关的特殊事件来启动节点序列。
  • 此外EventGraph的最常见用途是更新AnimGraph节点使用的值或属性。

动画事件

  • EventGraph的用法是添加一个或多个事件以充当进入点,然后连接函数、流控制节点和变量以执行所需的操作
  • 有了AnimGraph中提供的CPU线程节点函数功能,推荐尽量少使用EventGraph。这是因为EventGraph与项目中的其他大部分蓝图逻辑一起在主游戏线程上执行。因此,在动画蓝图中采用复杂的EventGraph会降低总体性能。这些事件大部分有线程安全的对应事件,应尽可能使用后者。

动画图表(Anim Graph) 

AnimGraph可供你为角色创建特定于动画的逻辑。通常,这涉及创建节点来控制混合、变换骨骼、移动和其他类似动画效果。在AnimGraph中,你可以使用根据EventGraph或函数计算的值,然后将这些变量连接为AnimGraph节点的输入,例如混合空间。节点及其值的组合效果连接到 Output Pose,这是为每个帧定义角色的最终姿势的地方。

  • 状态机(State Machines), 用于构建基于状态的逻辑,通常用于移动。

事件图表的节点

节点作用Unity 类比
Event Blueprint Update Animation动画蓝图每帧执行一次,类似动画系统的 UpdateUpdate()
Try Get Pawn Owner获取当前动画蓝图绑定的角色 Pawn,也就是正在使用这个 AnimBP 的角色本体获取角色对象引用,比如 GetComponentInParent<PlayerController>()
Is Valid判断获取到的 Pawn 是否存在,防止空引用报错if (pawn != null)
Get Velocity获取 Pawn 当前的速度向量rigidbody.velocity / agent.velocity
Vector Length把速度向量转换成一个数值,也就是速度大小velocity.magnitude
Set把计算出来的速度写入设定的变量animator.SetFloat(“Speed”, speed)

混合空间

https://dev.epicgames.com/documentation/unreal-engine/blend-spaces-in-unreal-engine#混合空间概述

动画序列(Animation Sequence)

https://dev.epicgames.com/documentation/unreal-engine/animation-sequences-in-unreal-engine

角色蓝图

准备工作

  • 创建AI Controller蓝图

创建角色并绑定AI控制器

  • 创建Character类型的蓝图
  • 设置Mesh
    • 常见设置Location Z = -90 Rotation Z = -90
    • 目标是:人物站在胶囊体里面,脚在胶囊体底部,面朝蓝色箭头方向
  • 选中根节点(角色绑定AI控制器)
    • Details中找到Pawn
    • 设置AI Controller Class为我们刚刚创建的AI controller 蓝图类资源
    • Auto process Player
      • 如果角色已经放置在场景中属于Placed in world,自动生成的则属于Spawned,选择两者都启用

绑定AI控制器和行为树

现在要让我们的控制器运行我们的行为树

  • 打开创建的AI Controller类的蓝图
  • 添加节点Run Behaviour Tree与开始节点相连
  • BT Asset选择创建的NPC行为树资源

行为树设置

  • 有3个节点
  • slector节点:“多选一”,它会从左到右尝试子节点,只要有一个子节点成功,就认为自己成功
    • ,常用于“能攻击就攻击,不能攻击就追击,再不行就巡逻”这种优先级判断;
  • Sequence 可以理解成“按顺序执行”,它会从左到右依次执行子节点,只要中间有一个失败,整个 Sequence 就失败,只有所有步骤都成功才算成功
    • 常用于“发现玩家 → 移动到玩家 → 执行攻击”这种连续流程
  • Simple Parallel 会一边执行主任务,后台运行右侧的辅助逻辑
    • 比如 AI 一边移动追玩家,一边持续检测玩家状态或播放某种行为,通常用于需要并行监控的 AI 行为
    • Detail – 并行 – 完成模式
      • 直接,主任务完成时立刻终止后台树
      • 已延迟 ,主任务完成时等待副任务页完成才终止后台树(用wait测试)
  • RMC – 添加装饰器,给每一个节点添加额外的功能,比如冷却时间

创建运行黑板

黑板就是数据交换中心,不同task之间的这种数据交互就是通过黑板来交互的,不然task多了之后,不同任务之间的数据交换就会变得凌乱。

案例1:随机位置巡逻

设置巡逻地区

(左上角选项模式右侧)创建物体 – 体积 – 导航网络体边界体积

  • (NavMesh Bounds Volume) 是 UE 里用来指定 AI 可以寻路的范围 的体积盒子。你可以把它理解成:告诉引擎“在这个区域内生成可行走地图”。
  • 不是碰撞体,而是一个“计算范围”。AI 的 Move To、行为树里的 Move To、随机巡逻点等功能,都依赖这个导航网格。
  • 如果场景里有 NavMesh Bounds Volume,按 P 后你会看到地面上出现一大片绿色区域:

创建自定义任务(设定运行黑板中的MoveTarget变量的任务)

  • 行为树界面 – New Task
自定义事件
  • 创建Blackboard Key Selector类型变量,用于设置运行黑板中的参数
    • Blackboard Key Selector 是行为树节点中用于引用并选择黑板变量的类型,它本身不存储具体数据,只保存“要访问哪个 Blackboard Key”的信息。
    • 公开设置黑板参数的变量Nevigation,需要点开小眼睛,同时在蓝图的左上角点击compile
    • 还有除了报错记得编译否则行为树中即使公开了变量也不会显示
  • 创建事件接收执行 AI(Event Receive Execute AI) 节点
    • Owner Controller:表示当前控制这个 AI 的 AIController,可以理解成 AI 的“大脑”。
    • Controlled Pawn:表示被这个 AIController 控制的角色本体
  • Cast To NPC
    • 拿到当前 AI 控制的角色,并确认它是不是你的 NPC 蓝图
  • Set As NPC(特殊创建)
    • 把转换成功后的 NPC 存成变量,方便后面使用。
  • Get Actor Location
    • 获取 NPC 当前所在的位置
  • Get Random Location in Navigable Radius
    • 在指定范围内寻找一个 AI 可以走到的随机点
      Origin = NPC 当前坐标,Radius = 200:在 NPC 周围 200 范围内,找一个 NavMesh 上可行走的位置
    • 输出1:Random Location = 找到的随机位置
    • 输出2:Return Value = 是否找到了
  • Branch
    • 判断有没有成功找到可行走位置。
    • Set Blackboard Value as Vector如果找到了,就把随机点写入黑板。
  • Key = NavigationTarget
    Value = Random Location
    • 把随机巡逻点保存到黑板变量 NavigationTarget 里

当前 AI 控制的角色→ 转成 NPC→ 获取 NPC 当前坐标→ 在 NPC 附近半径 200 内随机找一个 NavMesh 上的点→ 如果找到了→ 把这个点写入黑板变量 NavigationTarget→ 后面的 Move To 节点移动到这个点

如果角色没有跟着播放动画怎么办?

  • 先复制一份动画蓝图,然后再修改
  • 查看状态机的条件,去掉多余的条件,在这个例子中是加速度,可通过debug看到运行时的值
    • 输入轴 → Add Movement Input → 产生加速度 → 产生速度
  • 右键Vector输出节点可将其分裂为XYZ三个输出

案例3:正向固定点路线巡逻

  • 打开NPC蓝图,新建数组Actor参数(右键转换为数组)用于存储巡逻位置
    并公开该参数(点击小眼睛)
  • 在文件夹中创建3个空的Actor类型的蓝图作为巡逻点,为其添加公告牌组件使其在场景中方便观察
  • 场景中放置1个巡逻点,Alt + 左键复制巡逻点
  • 选择NPC,Detail面板中添加巡逻点,可以通过右侧的吸管工具选中物体进行绑定
  • 进入行为树面板,新建一个任务
  • 在运行黑板创建一个向量作为Target,一个int类型参数作为巡逻点索引
    同时在任务中对应的创建2个黑板键索引器类型的变量用于设置这两个黑板参数
  • 在行为树中进行绑定,同时设定Move To的黑板键
  • 进入AI Controller蓝图(其实我不太理解为什么不在声明这个参数NPC的Pawn类型蓝图中设置),将黑板中的目标索引设置为0(作为初始目标点)
  • 添加一个新的任务放在最后用于在到达下一个巡逻点时改变目标巡逻点的索引

案例4:正向结束后反向固定点巡逻

添加装饰器Blackboard

当黑板条件变化时,要不要打断当前行为。

  • None
    • 不打断任何任务。条件变化了也不会主动中止正在执行的行为。
  • Self
    • 只打断自己所在的这条分支。
      比如当前分支条件不满足了,就停止这个分支。
  • Lower Priority(优先度可以通过右上角的序号判断,越小的优先度越高)
    • 打断优先级更低的分支(即右侧的所有分支)
      常用于:AI 正在巡逻,突然发现玩家,就打断巡逻,切到追击。
  • Both
    • 同时打断自己和低优先级分支。
      适合需要非常敏感切换的行为,比如发现玩家、丢失玩家、进入攻击范围等。

本例中,只需要中断自身逻辑即可,因此选中self

使用服务修改移动速度

  • 创建服务(注意不是任务)
  • 在任务中设置也是一样的效果,但是会麻烦一点,而且服务可以嵌套的配置

案例5:视力感知

  • 可以直接在咱们的AI控制器添加,也可以在NPC角色蓝图中添加
  • 如果AI控制器控制很多的NPC蓝图,那在AI控制器当中添加是更好的选择
参数作用
实现 / Implementation当前使用的感知类型,这里是 AISense_Sight,也就是视觉检测
视觉半径 / Sight RadiusAI 能看到目标的最大距离,比如 3000,目标在这个范围内才可能被看见
失去视觉半径 / Lose Sight RadiusAI 已经看到目标后,目标超过这个距离才会真正丢失,通常比 Sight Radius 大一点,防止频繁丢失/重新发现
次要视觉半角角度 / Peripheral Vision Half AngleAI 视野角度的一半,比如 90 表示总视野大约 180 度
按归属检测 / Detection by Affiliation设置 AI 会检测哪些阵营:敌人、友方、中立
到上个已见位置的自动成功范围目标离上次看见的位置多近时,AI 会更容易继续认为看得到它;-1 通常表示不用这个机制
视点向后偏移 / Point of View Backward Offset调整视觉检测起点向后偏移多少,一般默认 0 就行
接近剔除半径 / Near Clipping Radius太靠近 AI 时是否忽略检测,通常保持 0
调试颜色 / Debug Color调试时显示感知范围/视觉线的颜色
最大年龄 / Max Age感知信息保留多久;0 通常表示不额外保留或按默认处理
开始时已启用 / Starts Enabled游戏开始时这个视觉感知是否启用
主导感官 / Dominant Sense多种感知同时存在时,用哪个作为主要感知,比如 Sight、Hearing
  • 对于蓝图来说三个阵营都必须选中,玩家可以设置属于在哪一个阵营中,但是蓝图没法做到
事件简要作用
OnPerceptionUpdated感知列表整体发生变化时触发,可能一次返回多个 Actor
OnTargetPerceptionUpdated某一个目标的感知状态变化时触发,最常用,比如看到玩家 / 丢失玩家
OnTargetPerceptionForgottenAI 彻底忘记某个目标时触发,通常用于清空追击目标
OnTargetPerceptionInfoUpdated某个目标的详细感知信息更新时触发,比 OnTargetPerceptionUpdated 信息更细
OnComponentActivatedAI Perception 组件被启用时触发
OnComponentDeactivatedAI Perception 组件被禁用时触发
设置黑板装饰器,其使用新创建的黑板变量

案例6:NPC追击玩家

  • 为了给不同感官做区分,将之前的视力感知封装为一个函数
  • 将函数的输入参数的名称改为和函数前的输入节点相同(我觉得可选)
  • Get Sense Class For Stimulus 从刺激信息中获取感官类型
    • 它的作用是:判断这次 AI 感知事件是由哪种感官触发的
    • 比如 AI Perception 可能有多种感知:视觉 Sight、听觉 Hearing、伤害 Damage、触碰 Touch
    • OnTargetPerceptionUpdated 触发时,会传出一个 Stimulus,这个 Stimulus 里包含“这次感知的来源信息”。这个节点就是从 Stimulus 里取出它对应的感官类。
  • 新增枚举类型表示AI的行为,将其作为行为树的切换条件
  • 通过设置Loop装饰器,当玩家丢失视野时AI会以自身为中心随机巡逻3次
  • 然后新增一个Task_EditAction用于在3次结束后将枚举值设置为巡逻

案例7:预知感知(原视频有错误,我已修正)

  • 设计问题:现在有一个问题是当玩家跑出视野范围后,AI马上就原地巡逻,这不合理,应该追到最后看到玩家的位置再进行巡逻,而不是立刻不管了
  • 中断AIStimulus中有一个选项是stimulus Location,其实就是” + 4测试中AI看到玩家时的位置
  • 并增加功能在玩家消失在视野之外时,调用预测感应,尝试预测一段时间后玩家的位置作为最终的Target
  • 在AI Controller中添加预测感知
  • 参数意思是:
  • Requester
    • 谁发起预测,一般是当前 AI 自己的 Pawn 或 Controller
  • Predicted Actor
    • 要预测谁的位置,一般是玩家。
  • Prediction Time
    • 预测多久之后的位置。
      比如 1.5,就是预测玩家 1.5 秒后可能到达的位置。

案例8:NPC攻击玩家

  • 在NPC Actor蓝图中声明一个自定义事件 Custom Event和一个攻击范围的变量(多个NPC攻击范围可能不同,故声明该变量而不是在任务中硬编码
攻击动作暂时没有配置
  • 判断玩家与NPC之间的距离,依此决定是否触发攻击自定义事件
  • 由于案例中的攻击动作是上半身动画,因此用平行节点,但如果需求是停下来再攻击,就不要用 Simple Parallel 让它边移动边攻击,而是改成 Sequence 顺序执行
    • 进入攻击分支→ 停止移动→ 面向玩家→ 播放攻击动画→ 等动画结束→ 结束任务

案例9:听力感知

  • 在AIController中选择AIPerception组件,在Detail面板中添加Hearing感知
  • 启用全部阵营的检测
  • 设置主导感知为AISense_Sight
  • 设置测试模块
  • 同SightPerception模块,将玩家最后出现位置设置为发声源即可,其他逻辑相同
  • 解决在该模块内部逻辑冲突问题,现在在范围内随机移动时再次听到声音依旧会进行巡逻
    • 其实老师的解决方案我觉得很不优雅,首先是修改视觉感知将isListening设置为true才能与后面的逻辑不冲突

头顶行为枚举状态显示

创建接口 Blueprint Interface

  • 为什么要创建这个接口?
    • AIController / 行为树 / 其他蓝图想通知 NPC 当前行为状态变化了
  • StatePrompt 这个函数的作用是什么?
    • 把 AI 当前行为状态通知给 NPC 本体
    • AIController 判断 NPC 当前行为

      调用 StatePrompt Message

      把 NPC_Action 传给 NPC

      NPC 蓝图里的 Event State Prompt 被触发

      NPC 根据 Patrol / Search 执行对应表现

第二步:创建一个Widget BluePoint蓝图

创建UI蓝图,命名很重要,因为要在NPC中添加该名称的引用变量

UI组件中的文字需要启用Is Variable,如果不勾选,你在 NPC 蓝图里就拿不到这个控件。

缩放组件

Scale To Fit = 根据容器大小缩放内容
Down Only = 只允许文字变小,不会把文字放大得很夸张

第三步:NPC的Event Graph中:首先创建一个Widget BluePoint蓝图类型的引用变量

  • 打开你的 NPC 蓝图,在左上角 Components 面板里:搜索 Widget
  • 这个 UI 蓝图里面应该有一个 TextBlock,用来显示状态文字。
  • 并在ViewPort中将该组件位置调整到NPC头顶

案例10:伤害感知

没有子弹和攻击动画,暂时搁置

案例11:触摸感知

新建一个碰撞体组件,修改其碰撞预设
意思是:这个碰撞体遇到 Pawn 类型的对象时,不阻挡它,而是产生重叠检测。
Pawn = 可以被玩家或 AI 控制的 Actor

RMC组件 – 添加事件 – OnComponentEndOverlap 是组件的结束重叠事件

当某个 Actor / Component 离开这个组件的 Overlap 范围时触发

通过Report Touch event触发感应

学习笔记如有侵权,请提醒我,我会马上删除
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇