上一篇按屏幕占比裁剪局部光动态阴影记的是同一个密集城市街道场景里,针对光的那一刀。这篇记另一条杠杆:那个场景里 30,518 个独立 StaticMeshComponent 直接推高的 draw-call 体量。我写了个编辑器工具把同 mesh 的散摆静物折成 HISM,全图跑通一次把 21,483 个 SMC 合进了 494 个 HISM。这篇讲为什么要合、怎么用、以及一路上崩过的几次和绕不开的坑。
做存档调试时经常要改档:改角色数值、改背包物品、换装备武器。但 .sav 是不透明的二进制,没法直接看、直接改。这篇记录一个把 .sav 导出成可读 JSON、手工编辑后再导回 .sav 的工具,以及做无损往返时踩到的几个坑——它们都和 UE 的 SaveGame 序列化机制细节相关。
整体只有两条控制台命令,在编辑器或 PIE 里输入:
SaveSystem.ExportSlotToJson <完整.sav路径>
SaveSystem.ImportJsonToSlot <完整.json路径>
玩家砍断僵尸肢体时,断口处那块"断骨"伤口网格会被拉扯成一根又细又长的尖刺,从蜷缩的躯干里穿出来,伴着被拉长的红色血肉,严重穿模。
这块伤口网格挂在一个 UPoseableMeshComponent 上,和宿主僵尸共用同一套骨架,骨骼名一一对应。问题就出在"共用骨架但只摆正了一根骨头"。
先说清楚:蒙皮和骨骼的关系
要看懂这个 bug,得先分清骨骼和网格这两件事。
骨骼(Skeleton / Bone) 是一套层级化的坐标系——父子变换树。骨头本身不可见,只是空间中的一组变换(位置、旋转、缩放)。动画每帧改变这些骨头的变换。
一个密集城市街道场景抓的 trace 里,我碰到一个不太常见的瓶颈形态:GameThread / RenderThread / RHIThread 三条线并列卡住,各约 88 ms,彼此在 ±9 ms 内;GPU 只忙 53 ms,有约 36 ms 余量。这篇记一下我针对其中一条杠杆——局部光动态阴影——做的运行时裁剪,包括为什么这么做、实现上几个绕不开的点,以及关掉/打开 CVar 的 A/B 数据。
同一个 trace 里针对另一条杠杆(30k 散摆静物的 draw-call)的处理,见把近场散摆静物合成 HISM 削 draw-call。两条线一起压,帧时才真正掉得下来。
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 倍差距明显不正常。