适用引擎:UE 5.4。平台以 Windows(dev/editor)为主,主机同理。
这是一份通用操作方法论,不针对某一次具体分析。单次分析的原始数据建议另存成一份带日期的记录,别和方法论混在一起。文中路径/类名均已脱敏,技术细节原样保留。
内存排查有两套互补工具,各有强项。我的经验是两套一起用、相互印证——单看任何一套都容易把「换血」误判成「泄漏」。
| 工具 | 强项 | 弱项 |
|---|---|---|
| memreport | 快、离线可读、给出各类对象/贴图/RT 的绝对占用,能判断「池子总量是否封顶」 | 只是快照,看不到分配来源/调用栈 |
| Unreal Insights 内存 trace | 逐条分配、时间轴、按 A/B/C 时间点筛「存活增长」、可到调用栈 | 文件巨大;调用栈需额外开启 + 配符号;区分不了「池内换血」和「真泄漏」 |
这是一篇调试记录:某主机平台上点光源阴影渲染莫名变得极慢、还伴随显示破损,最后查出根因是 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 框架大致是这些类:
这是一份把 Unreal Insights trace 当成工程数据来读的指南。重点不是"怎么打开 Insights",而是抓到一份 trace 之后,怎么从里头逐层读出可执行的优化结论——以及一路上的坑。文中所有数据示例均来自一个 UE 5.4 的 ARPG 项目在 Win64 Development / Test 构建下的实测,已脱敏。
1. 为什么需要"方法论",而不是只看 Insights GUI
Insights GUI 直观,但有两个固有问题:
ARPG 战斗里经常会同时存在十几到几十个敌人,常见的优化做法是引入 Significance/Budget 系统:按距离、可见性给角色分桶(bucket),根据档位降低 tick 频率甚至关闭 PhysicsControl。这套系统在常态下表现良好,但只要扩大 Physics Control 被关闭的阈值范围,就会暴露出两个典型现象——它们来自同一个根因:Significance 不知道角色当下是不是 ragdoll。
1. 两个现象
| # | 现象 | 触发场景 | 关闭 Significance 后 |
|---|---|---|---|
| 1 | 远处敌人一帧布娃娃、一帧 T-Pose 反复切换 | 敌人数量多、布娃娃状态(击飞 / 击倒 / 死亡) | 消失 |
| 2 | 敌人缓慢"升天" | 击飞类技能(如 GA_KnockoutFly)的后半段(约 0.4–0.8s) |
消失 |
同一款游戏要同时上 Steam 和 Epic,两边的 SDK、插件、部分逻辑都不一样。最干净的做法是拆成两个独立的 game project,但移植项目往往没有这个自由度,只能在一份工程里做兼容——这篇记录这套兼容方案怎么落地。
三个拆分点
1. 用宏拆分逻辑代码。 给 Steam / Epic 各自的逻辑加编译宏区分。
一个坑:不要把这个宏定义写在 Game.Build.cs 里,然后又在引擎工程里用它。一旦引擎代码依赖了定义在 game 侧的宏,引擎工程就没法脱离 game 工程单独编译了。宏的定义位置要和使用位置的模块边界对齐。