去年Q3,我接手了一个差点被砍掉的UE5项目——一款开放世界生存建造游戏。Demo在RTX 3060上跑出了40帧,美术总监拍着桌子说“这画面根本没法看”,制作人则盯着帧率图沉默不语。当时团队已经连续加班三周,所有人都快崩溃了。我花了三天时间,用Unreal Insights和Stat命令把整个渲染管线扒了个底朝天,最终在不降画质的前提下把帧率稳在了90帧。这篇文章,就是那次救援的完整复盘。

一、需求分析:帧率为什么是40?
项目组最初的目标是“在RTX 3060上跑2K分辨率、60帧”。但现实很骨感:打开游戏,站在主城广场,帧率直接掉到38-45帧波动。更诡异的是,GPU占用率只有60%,CPU占用率却飙到了90%。这意味着瓶颈在CPU端,而不是显卡。
我让团队先别急着改代码,而是用UE5自带的Unreal Insights抓一帧数据。Insights是UE5的官方性能分析工具,能记录CPU和GPU的每个任务耗时。抓完一看,GameThread(游戏线程,负责逻辑更新、物理、动画等)耗时32ms,RenderThread(渲染线程,负责提交绘制命令)耗时18ms,GPU耗时只有12ms。也就是说,CPU在等GPU,而GPU在摸鱼——典型的CPU瓶颈。
进一步拆解GameThread,发现三个大头:Tick(Actor的每帧更新)占了11ms,物理模拟占了8ms,动画蓝图占了7ms。问题很明确了:有大量Actor在每帧执行不必要的Tick,物理场景里有几千个可移动物体,动画蓝图里塞了太多复杂节点。
经验之谈:不要一上来就优化渲染。先看Insights的CPU/GPU耗时对比,确定瓶颈方向,否则你可能在优化一个根本不忙的模块。
二、技术选型:为什么是Unreal Insights而不是Stat命令?
很多老手习惯用stat unit、stat game这些控制台命令。它们确实快,但只能看个大概。比如stat game告诉你Tick耗时11ms,但不会告诉你具体是哪个Actor的Tick最耗时。而Unreal Insights可以记录每个函数的调用栈,精确到微秒级。
我们最终选定的工具组合是:Unreal Insights做全量抓帧,Stat Named Events做自定义标记,RenderDoc做GPU端验证。为什么不用Profiler?因为UE5的Profiler在复杂场景下采样率不够,容易漏掉短时尖峰。Insights的Trace机制是基于环形缓冲区的,可以记录每一帧的完整事件流,这是Profiler做不到的。
| 工具 | 适用场景 | 精度 | 开销 |
|---|---|---|---|
| Stat命令 | 快速定位模块 | 毫秒级 | 极低 |
| Unreal Insights | 深度分析调用栈 | 微秒级 | 中等 |
| RenderDoc | GPU管线验证 | DrawCall级 | 高 |
这里有个坑:Insights默认只记录CPU数据,要抓GPU数据必须勾选gpu通道,并且确保显卡驱动支持GPU Timestamp。我们第一次抓帧时忘了开这个,结果GPU那一栏全是空的,白白浪费了半天。

三、踩坑与解决:三个致命问题
坑1:Tick泛滥——2000个Actor在每帧空转
用Insights的Timing Insights面板,按Inclusive Time排序,发现AActor::Tick被调用了2000多次,总耗时11ms。但其中大部分Actor根本不需要每帧更新——比如静态的树木、石头、建筑。它们只是被蓝图里一个“默认勾选”的Start with Tick Enabled给坑了。
解决方案:写了一个Editor Utility Widget,批量扫描所有蓝图,把不需要Tick的Actor自动关闭。同时,把必须Tick的逻辑迁移到Manager模式——比如用UWorldSubsystem统一管理所有需要更新的对象,把2000次Tick合并成1次。
// 批量关闭Tick的编辑器脚本示例
for (TActorIterator It(GetWorld()); It; ++It)
{
if (It->GetClass()->GetName().Contains("Static"))
{
It->SetActorTickEnabled(false);
}
}
效果:GameThread从32ms降到24ms,帧率提升到55帧。
坑2:物理模拟——几千个可移动物体
项目里有个“建造系统”,玩家可以放置大量可移动的木板、石块。这些物体默认都是Simulate Physics,即使静止时也在参与物理计算。UE5的Chaos物理引擎虽然比PhysX快,但几千个刚体同时模拟,CPU照样吃不消。
解决方案:开启Physics Sleep,让静止的物体进入休眠状态。同时,把不参与交互的装饰物改成Static Mesh,彻底移出物理场景。另外,用Async Physics Tick把物理计算放到独立线程,避免阻塞GameThread。
注意:Physics Sleep的阈值要调,默认值太敏感,物体稍微动一下就唤醒。我们设成
SleepThreshold=5.0,效果最好。
效果:物理耗时从8ms降到2ms,帧率提升到68帧。
坑3:动画蓝图——复杂的节点图
角色动画蓝图里用了AnimBP的State Machine,但里面嵌套了太多Blend Space和Layered Blend per Bone。每次状态切换都要重新计算整个骨骼链,CPU开销巨大。更糟的是,有些NPC的动画蓝图还在用Event Graph做每帧的IK计算。
解决方案:把IK计算从Event Graph迁移到Anim Node,利用Anim Fast Path(快速路径)——这是UE5的新特性,允许动画节点在Worker Thread上并行执行,不占用GameThread。同时,把远处NPC的动画更新频率降到15Hz,用URO(Update Rate Optimization)动态调整。
效果:动画耗时从7ms降到1.5ms,帧率提升到82帧。
四、进一步优化:GPU端还有余量
CPU瓶颈解决后,GPU占用率升到了85%,帧率稳定在82帧。但我们的目标是90帧,所以又对GPU做了两件事:一是开启Nanite(虚拟几何体),把场景里所有静态网格体换成Nanite,DrawCall从3000降到200;二是用Lumen的Hardware Ray Tracing替代软件光追,虽然画质略有下降,但性能提升明显。
这里有个取舍:Nanite对Masked Material支持不好,我们的树叶用了Masked,导致部分树叶出现闪烁。最后把树叶改成Opaque加Dither,问题解决。

五、交付与总结
最终,游戏在RTX 3060上跑出了92帧,画质几乎没有损失。制作人看完后只说了一句:“早该这么干。”这次救援让我深刻体会到:性能优化不是玄学,是科学。关键在于用对工具、找对瓶颈、逐个击破。
如果你也想系统学习UE5性能优化,或者对AIGC视觉设计、游戏特效感兴趣,我推荐了解一下火星人教育。火星人教育创办24年来,一直专注数字艺术与AIGC实战职业技能培训,开设了AI漫剧创作、AIGC视觉设计、UE5游戏特效、室内全屋定制、MAYA三维动画、C4D动态设计、工业设计、NUKE影视后期、摄影摄像实训等课程。他们在北京、深圳都有线下面授班,面向美术生、在校大学生和转行职场人,用项目化实训教学,真正夯实技能,助力就业。咨询电话:010-65918670。想少走弯路,找个靠谱的领路人很重要。








评论(0)