为什么你开发的 Unity 游戏越玩越烫?(系列 · 第 1 篇)

一部手机,玩你的游戏前 5 分钟丝般顺滑,10 分钟后烫得像暖手宝,帧率还从 60 掉到 30。 你打开 Profiler 看了半天,CPU 不高、GPU 也不算高------到底是谁在"烧电"? 这篇文章从根源讲清楚:手机发烫,烧的往往不是"算力",而是"搬运"。

本文是《Unity 游戏为什么发烫》系列的第 1 篇(共 8 篇)。本篇讲清楚整个系列的底层逻辑,后面 7 篇逐个收拾"惯犯"------完整目录在文末。


一、先说结论:热量的大头,来自"搬数据"而不是"做计算"

很多开发者对功耗的直觉是:计算越多越耗电 。这个直觉在 PC 上大致成立,但在手机上,真正的耗电大户常常是另一件事------内存搬运(带宽)

原因很简单:

  • 计算是在芯片内部完成的------数据进了寄存器和缓存,运算本身效率很高;
  • 搬运要跨芯片------CPU/GPU 每读一次内存、每写一次帧缓冲,电流都要在芯片和 LPDDR 之间跑一个来回,这是实打实的功耗。

所以业界才有一句老话:一次内存访问的耗电,够 ALU 算几十次。 你的游戏之所以烫,很可能不是 GPU 在疯狂计算,而是它在疯狂地"搬砖"。

这不是我瞎说。Arm 官方在《Mali-G51 Performance Counters Reference Guide》里给过一个可以直接拿来用的硬数字:

外部 DRAM 访问的功耗大约是每 GB/s 消耗 80--100 毫瓦。 假设 DRAM 访问的典型功耗预算是 650mW,那么一款 60fps 的游戏,每帧可持续使用的总数据量只有约 100MB

翻译成人话:带宽每多用 1 GB/s,功耗就多付 80--100mW。"降带宽"和"降功耗"在移动端基本是同义词。这条数字也解释了为什么本文把"搬运"而不是"计算"当作发烫的主叙事------它是整个系列的物理基础。

二、带宽:被大多数独立开发者忽视的指标

先补一个基础概念。带宽(Bandwidth) 指 GPU 与内存之间数据通道的吞吐量,单位是 GB/s------它不是"存储空间",而是"搬运速度"。

概念 说的是什么 类比
内存容量 能存多少数据 仓库面积
带宽 每秒能搬多少数据 马路每小时能过多少车
延迟 一次往返要多久 单程耗时

手机是 SoC 集成芯片,CPU 和 GPU 共享一块 LPDDR,带宽天生就窄,而且每一次搬运都直接转化成电量和热量。这就是"越玩越烫"的物理基础。

那么,你的游戏里是谁在疯狂搬运数据?三个惯犯:

惯犯 1:Overdraw(过度绘制)

全屏半透明遮罩、粒子特效、全屏后处理------每一个都会让同一块像素被反复着色。而画一个透明像素 = 从帧缓冲读出旧颜色 + 混合计算 + 写回新颜色,全是搬运。Overdraw 叠 4 层,这块搬运量就乘 4。

最典型的就是"新手村弹窗"式的 UI:

  • 全屏黑色遮罩(整屏 ×1)
  • 遮罩下还有一张全屏渐变背景(整屏 ×2)
  • 弹窗面板本身半透明(×3)
  • 文字底下又垫半透明高亮底(×4)
  • 再叠一层全屏 Color Grading(×5)

肉眼看是一幅画,GPU 实际画了五遍。 每一遍,都是整屏像素的读写搬运。

惯犯 2:未压缩 / 过大的纹理

片元着色器每输出一个像素,基本都要去内存读纹理。一张 2048×2048 的 RGBA32 纹理,一个像素 16 字节------如果压成 ASTC 6×6,能降到约 1/9。也就是说,同一张图,未压缩版本的"搬运功耗"是压缩版的 9 倍,而画面在手机屏幕上未必看得出区别。

很多团队纹理导入设置用默认值一把梭,等于让手机全程扛着最重的砖。

惯犯 3:过长的后处理链

Bloom、景深、Color Grading、暗角......URP 里随手挂一堆 Volume 效果很爽,但每加一个全屏 pass,整屏像素就多读写一遍。在 PC 上这叫"画面高级",在手机上这叫"电热丝"。

这三个惯犯是"搬运型"发热的代表,也是 GPU 侧问题的主力------但别误会,发热的锅 GPU 只背一半。完整的账本见下一节。

三、完整的敌人名单:七大热源

把一款 Unity 手游的发热原因全部摊开,可以归成七类(本篇只展开 GPU 侧,其余各篇逐一收拾):

# 热源 一句话 详细拆解
1 GPU 渲染与带宽 Overdraw ×N、未压缩纹理、后处理链 本文 + 第 3、4 篇
2 实时光照与阴影 实时光源数、阴影分辨率、实时反射------静态场景全烘焙常能砍掉一半 GPU 功耗 第 6 篇
3 CPU 逻辑与 GC Update 空转、每帧分配、GC 尖峰全核扫描 第 5 篇
4 Draw Call 与物理 DC 提交成本、FixedUpdate 50Hz、无 LayerMask 的 Raycast 第 5 篇
5 动画与 UI 重建 Animator 满屏评估、Canvas 动静不分触发整块重建 第 5 篇
6 帧率策略缺失 不锁帧、菜单满帧跑、切后台不降载 第 7 篇
7 外设与使用场景 网络 modem、GPS/传感器、屏幕亮度、边充边玩 第 8 篇

其中第 6 类最冤枉也最值钱------它不是"哪里慢",而是"根本没打算让它省" 。这类问题的治理成本几乎为零(改一行 targetFrameRate),却是很多团队从没做过的。

四、为什么是"越玩越烫",而不是"一直这么烫"?

这是最有迷惑性的部分。游戏负载从头到尾没变,为什么热量是逐渐累积的?

因为手机有一个自我保护机制:热节流(Thermal Throttling),俗称降频

完整链条是这样的:

复制代码
游戏负载高(带宽/功耗大)
    → 芯片持续发热,温度超过阈值
    → 系统下调 CPU/GPU 频率(自我保护,防烧毁)
    → 频率降了,同样一帧要花更长时间
    → 帧率下降,掉帧
    → 如果负载不变,温度继续顶着阈值
    → 继续降频......

这形成了一个恶性循环 :帧率从 60 掉到 45,再掉到 30,机身越来越烫。很多玩家反馈"你这游戏优化不行,越玩越卡",开发者拿 Profiler 一查:CPU 不高啊?GPU 也不高啊?------因为你测的是冷机状态,玩家烫的是热机状态

这里有个关键的认知转换:降频是"原因",掉帧是"结果"。 掉帧的原因有很多(Draw Call、GC、加载卡顿......),但"越玩越卡"这种随时间劣化的掉帧,十有八九指向降频,而降频的根源又指向功耗,功耗的根源指向带宽。一条线串到底:带宽 → 功耗 → 发热 → 降频 → 掉帧。

五、三步定位你的"发烫源"

第 1 步:先分清是哪种烫

  • 冷机启动就烫、帧率稳定地低 → 普通性能瓶颈,直接开 Profiler 查热点(Draw Call / GC / 加载);
  • 前几分钟流畅,之后越来越烫、越来越卡 → 降频问题,从功耗下手。

第 2 步:判断是不是带宽瓶颈(5 分钟土办法)

把 Render Scale 降到 0.5 跑一遍(URP 里改一个参数的事):

  • GPU 帧时间大幅下降 → 基本是带宽瓶颈(像素数是平方关系,分辨率减半,像素量降到 1/4,带宽需求随之大降);
  • GPU 帧时间几乎不变 → 瓶颈在别处(Draw Call 或 CPU 逻辑)。

第 3 步:看 Overdraw

Scene 视图左上角切到 Overdraw 模式,画面越红的区域,像素叠加越严重。全屏一片深红?恭喜,你找到电热丝了。

六、降温清单:按性价比排序

优先级 优化项 做法 降温原理
★★★ 合并全屏半透明叠加 遮罩和背景让美术合成一张图;不可见 UI 用 SetActive(false) 而不是把 alpha 设成 0 全屏透明层每砍一层,整屏搬运少一遍
★★★ 纹理统一压缩 移动端默认 ASTC,UI 大图可适当选低档位 搬运量直接降到零头
★★★ 砍后处理链 移动端只留 1--2 个真正提升观感的全屏效果 每删一个 pass,整屏读写少一遍
★★★ 静态场景光照全烘焙 能烘的光全烘进 Lightmap,实时光只留必要的 GPU 计算功耗常能直接砍半
★★ 降低 Render Scale 中低端机 0.75--0.85,配合升采样 像素数平方级下降,带宽和功耗同步降
★★ 3D 纹理确保开 Mipmap 检查导入设置(UI 纹理才需要关) 采样局部性好,缓存命中率高
★★ 主动锁帧 Application.targetFrameRate = 45(或 30) 牺牲峰值帧率,换发热可控、全程稳定
粒子减量 全屏粒子特效限流、合并粒子贴图 减少透明像素的重复着色

关于锁帧多说一句 :与其让 GPU 满血跑 60fps 十分钟,然后降频一路跌到 25,不如直接锁 45fps------机身温度稳得住,帧率曲线是平的,玩家体感反而更好。"稳定的中帧率"永远优于"先高后崩"。这也是为什么很多大厂手游默认锁 30 或 60 的背后逻辑:帧率是可以买"发热余量"的货币 。Arm 官方优化指南对这一点也有明确背书:以更高的渲染效率运行、再把帧率限制在较低水平,GPU 能更早进入空闲,反而更省电

七、写在最后:移动端优化的隐藏货币是电量

PC 时代我们优化的是"时间":帧时间、加载时间。到了移动端,你会发现多了一个隐藏货币------电量。每一个多余的像素着色、每一张未压缩的纹理、每一层叠加的后处理,玩家都在用电池和掌心的温度替你买单。

所以下次真机测试时,除了盯帧率,也摸一摸手机背面、看一眼电量掉得多快------你的玩家,是真的在"用体温评价你的优化水平"


系列目录(持续更新)

  1. 为什么你开发的 Unity 游戏越玩越烫? (本篇)------ 热节流恶性循环与"搬运≈功耗"的总纲
  2. 带宽:GPU 的粮道在哪里堵住了 ------ DRAM/带宽/延迟,以及 5 分钟定位带宽瓶颈的土办法
  3. Overdraw:两幅一模一样的画面,帧率差一倍 ------ 新手村弹窗 A/B 实战
  4. 纹理与后处理:搬运量最大的两个惯犯 ------ ASTC 压缩、Mipmap 与缓存命中
  5. CPU 不是无辜的:GC、Draw Call 与 Canvas 重建 ------ 另一半功耗的账
  6. 光照烘焙:降温性价比之王 ------ 一招常砍半 GPU 功耗
  7. 锁帧的智慧:拿帧率换发热余量 ------ 为什么"锁帧"不是偷懒而是策略
  8. 收官:七大热源排查清单 + 功耗测量实战 ------ 带走的 Checklist

参考资料

  • Arm《Mali GPU OpenGL ES Developer Optimization Guide》(developer.arm.com)------锁帧省电、TBDR 架构与带宽关系
  • Arm《Mali-G51 Performance Counters Reference Guide》(developer.arm.com)------DRAM 访问功耗 80--100mW/GB/s、60fps 每帧约 100MB 预算
  • Android Open Source Project《power_profile.xml》文档(source.android.com)------系统级功耗组件模型(屏幕/网络/GPS)

注:文中未标注出处的功耗占比类数字均为量级参考,具体随机型、亮度、分辨率浮动,请以自己测试机的实测为准。


一句话总结:越玩越烫,是因为你的游戏在疯狂搬运数据(Overdraw、未压缩纹理、后处理链),搬运 = 耗电 = 发热,发热触发降频,降频引发掉帧------降温要从"省搬运"下手,而不是只盯着"省计算"。下一篇,我们把这个系列的物理地基------带宽------彻底讲透。

相关推荐
陈言必行5 小时前
Unity开发实战技巧:脚本优化与性能提升
unity3d
SmalBox9 小时前
【高级着色器】Unity实现-卡通风格水着色器
unity3d·游戏开发·图形学
_zhourui_h_1 天前
Unity WebSocket全平台极限兼容:浏览器禁掉原生 Socket 后,WebGL 到底怎么连服务器?
unity3d
SmalBox1 天前
【高级着色器】Unity实现-彩虹泡泡着色器
unity3d·游戏开发·图形学
fujisheng6612 天前
从 GetTypes() 到强类型 Route:FUI Source Generator 的设计演进
unity3d
SmalBox2 天前
【动态着色】Unity实现3D扫描线
unity3d·游戏开发·图形学
SmalBox3 天前
【动态着色】Unity 实现消融效果
unity3d·游戏开发·图形学
_zhourui_h_3 天前
我以为 ECSList 已经够快了,直到我直接拿到了 Column
unity3d
SmalBox4 天前
【动态着色】Unity 实现全息投影着色器
unity3d·游戏开发·图形学