GOAP(Goal-Oriented Action Planning,目标导向行为规划)是一种让 NPC 自己「想办法」达成目标的决策技术。行为树把决策路径提前写死,GOAP 反过来:你只描述角色想要什么(Goal)和能做什么(Action),至于先做哪一步、再做哪一步,交给规划器(Planner)在运行时算出来。
这套思路并不新,源头是 1970 年代的自动规划(STRIPS)。真正让它在游戏圈出名的是 2005 年的《极度恐慌》(F.E.A.R.)——敌兵会绕后、找掩体、喊队友合围,很大程度上就是 GOAP 规划出来的。更早在 2003 年的《杀出重围:隐形战争》里就试过一次,但那一代的 AI 因为行为不一致、战术偏简单、算力吃紧挨了不少批评。这段历史其实提前把 GOAP 的代价摆到了台面上:规划是要花 CPU 的,用之前得想清楚值不值。
在 VS 里装个 P4 插件,写代码时直接 checkout/revert 比来回切 P4V 顺手很多。Perforce 官方的 P4VS 插件用起来容易让 VS 卡顿甚至崩溃,不推荐。这里用的是第三方的 P4EditVS——轻量、不挂 VS。
装好后:
- VS 的选项(Options)里会多出 P4EditVS 页,每个配置项都带详细说明。
Extensions菜单下的 P4EditVS 子菜单,可以对当前编辑的文件执行 P4 操作。- 报错时去 VS 的 Output 窗口看 P4EditVS 的日志,排查用。
适用引擎:UE 5.4。平台以 Windows(dev/editor)为主,主机同理。
这是一份通用操作方法论,不针对某一次具体分析。单次分析的原始数据建议另存成一份带日期的记录,别和方法论混在一起。文中路径/类名均已脱敏,技术细节原样保留。
内存排查有两套互补工具,各有强项。我的经验是两套一起用、相互印证——单看任何一套都容易把「换血」误判成「泄漏」。
| 工具 | 强项 | 弱项 |
|---|---|---|
| memreport | 快、离线可读、给出各类对象/贴图/RT 的绝对占用,能判断「池子总量是否封顶」 | 只是快照,看不到分配来源/调用栈 |
| Unreal Insights 内存 trace | 逐条分配、时间轴、按 A/B/C 时间点筛「存活增长」、可到调用栈 | 文件巨大;调用栈需额外开启 + 配符号;区分不了「池内换血」和「真泄漏」 |
在 Windows 上用 fxc 编译 shader,默认会把调试信息嵌进 shader 二进制里,包体会因此变大。可以用 fxc /Qstrip_debug 剥掉调试信息,但这样一来 RenderDoc 里就没法调试了。这篇讲怎么两全:把调试信息单独导出成 pdb,剥离后的二进制照常发布,同时让 RenderDoc 仍能加载到调试信息。
原理
- 在 RenderDoc 里设置 shader 调试信息的搜索路径;
- 给剥离后的 shader 二进制写一段特殊的 private data,告诉 RenderDoc 对应的 pdb 在哪。
这是一篇调试记录:某主机平台上点光源阴影渲染莫名变得极慢、还伴随显示破损,最后查出根因是 Vertex Shader 的输出结构和 Geometry Shader 的输入结构不匹配,导致 GS 阶段生成了大量无效图元。方法本身和平台无关,记下来当案例。
问题表现
特定区域 GPU 消耗异常升高、帧率明显下降。GPU trace 显示瓶颈在渲染点光源(PointLight)ShadowMap 上——渲染某个角色风格体时单这一项就超过 50ms,而在另一台对照主机平台上同样的内容只花 1ms。两台平台跑同一套引擎和 shader,50 倍差距明显不正常。
这篇整理一个 ARPG 项目(PS5 主机 + Windows 开发,UE5.4 Chaos 后端)在 DefaultEngine.ini 里对物理的全套配置——不是逐字段翻译文档,而是把**「为什么这么调」和「踩过的坑」**写清楚,下次遇到「ragdoll 飘」「瓶子落地慢」「ragdoll 一接触就嗖飞」这类问题能直接对照排查。
1. 核心物理参数 [/Script/Engine.PhysicsSettings]
1.1 重力与运动学基础
| 配置项 | 推荐值 | 说明 |
|---|---|---|
DefaultGravityZ |
-980.0 |
默认重力(cm/s²,向下),相当于地球 1g |
DefaultTerminalVelocity |
4000.0 |
自由落体封顶,防止数值爆炸 |
DefaultFluidFriction |
0.3 |
水 / 流体默认摩擦 |
MaxAngularVelocity |
7200.0 |
单刚体最大角速度(度/秒)。从默认 3600(10 rev/s)抬到 7200(20 rev/s),让 ragdoll 被击中时保留更多旋转动量 |
MaxDepenetrationVelocity |
750.0 |
穿透解算时的最大反推速度(cm/s)。不能设为 0(无限反推)——会把穿透刚体瞬间弹飞,典型表现是 ragdoll 一接触就「嗖」飞出场景。500 是稳妥起点,为改善 ragdoll 落地「软陷」可放到 750,仍远离失效域 |
BounceThresholdVelocity |
50.0 |
相对速度低于此阈值时不计入回弹。从 200(2m/s)降到 50(0.5m/s),让 ragdoll 落地的小冲击能正常传递 |
FrictionCombineMode |
Average |
两物体摩擦取均值 |
RestitutionCombineMode |
Average |
两物体回弹取均值 |
0. 为什么需要这份规范
在大多数因 Niagara 导致性能问题的项目中,常见现象并不是"GPU 模拟太慢",而是:
- 渲染线程长时间阻塞在等待 Niagara 系统创建渲染缓冲(
WaitForGatherDynamicMeshElements); - 单帧内大量
CreateRHIBuffer调用; - 关卡里同一资产被复制粘贴几十上百个实例;
- 视野外、远距离的 Ambient 系统没有被正确剔除,每帧仍在渲染线程上分配显存缓冲。
继前一篇 Significance 预算与布娃娃 / PhysicsControl 的冲突修复 之后,我们在角色基类上加了一层「离屏即 suspend PhysicsAsset / Collision」的优化(ApplyPhysicsVisibilitySuspension),通过缓存「是否处于战斗」状态把战斗中的敌人豁免,让 weapon trace / AOE 仍能命中离屏目标。
这篇记录在 Unreal 里从零搭一套 GOAP 框架的做法。GOAP 的概念、规划为什么是反向图搜索,见 GOAP:目标导向行为规划;这里只谈落到 UE 上的实现——数据结构怎么设计、规划器怎么写、以及项目跑起来之后撞上的两个坑。
整体结构
一套可用的 UE GOAP 框架大致是这些类: