美术资源优化

从网格、动画、纹理到 AssetBundle,降低美术资源对内存与加载的影响。

学习目标

  • 能根据用途调整网格、纹理、动画的导入设置,并解释每项对内存与 CPU 的影响
  • 能说出 Resources 文件夹的三大陷阱,并改写成 AssetBundle 或 Addressables 的按需加载写法
  • 能回答:一张 4096×4096 的 UI 图标以 RGBA32 导入、未限制 Max Size,在 1024 屏幕上实际占多少内存?你会改哪三项设置?

原书边界:Chapter 4

Packt 第三版把本章命名为 Optimizing Your Art Assets。官方目录列出的核心范围如下;这些条目共同构成本章的一对一边界:

  • Audio:纳入本章实验与验收,不拆到别章重复计数。
  • Texture files:纳入本章实验与验收,不拆到别章重复计数。
  • Mesh and animation files:纳入本章实验与验收,不拆到别章重复计数。
  • Asset bundles and resources:纳入本章实验与验收,不拆到别章重复计数。

从源资源、导入设置、平台压缩、运行时驻留到卸载建立完整资产生命周期,而不是只看包体。Addressables 可替代部分旧式 AssetBundle/Resources 工作流,但压缩格式、Read/Write、副本与依赖生命周期仍需逐项验证。 因此,本章既保留 2019/2020 语境,也明确标出迁移到现代 Unity 时哪些是稳定原理、哪些只是版本相关接口。

包体不大,内存却爆了

上一章你把画面提交开销压下去了,帧率却还是在切场景时卡住。打开内存统计一看——贴图占了几百兆,模型又占了一大块,一切关卡就要黑屏两三秒。问题往往不在玩法逻辑,而在资源进门时没瘦身:源文件很大,进游戏仍是原尺寸;每个模型都多留了一份可改副本;启动时把整柜东西全搬进客厅。

这章要解决的,是美术资源从进门到搬入整条链上的浪费:模型、动作、贴图、声音怎么瘦身;为什么「随手塞进万能抽屉」是个坑;怎么把加载尖峰摊到多帧。测量工具已经能看内存曲线——接下来需要的是在导入面板里改对开关,而不是再堆一个后处理脚本。

没有这些优化会怎样?手机上先因内存不足闪退,电脑上则是加载黑屏、切关卡顿、安装包膨胀——玩家还没见到你的合批成果,就已经被资源拖垮了。

像搬家:箱子越大,车越难装

想象你要把一屋子家具搬进新房。原样搬(不压缩、不拆箱)每辆车只能装一件大床;提前折叠、打木架能一车装十件;把暂时不用的家具锁在仓库、进门再拉,比全堆客厅省地方得多。

美术资源优化就是在装车前决定:这件家具体积能不能缩小、要不要留一份可改的副本、是不是进门才从仓库拉出来。

网格:压缩精度,关掉可写副本

把顶点位置、法线等精度从 32 位降到 16 位或更低——肉眼难辨的差异,换显著的显存节省。移动平台常用 Medium 或 High;主角高精度模型可 Off 或 Low。

是更隐蔽的内存杀手:默认应关闭。只有你要在代码里改 mesh.vertices、做运行时变形、或 MeshCollider 烘焙前需要可读网格时才开。

Model Import Settings:网格内存与 CPUMesh CompressionOff12 MB原始精度Medium/High~4 MBGPU 解压,略增 CPURead/Write Enabled✗ 关闭只读一份默认推荐✓ 开启×2 内存仅运行时改 mesh 时何时需要 Read/Write?Mesh.vertices / RecalculateNormals / 运行时变形 → 开;纯渲染 / 烘焙碰撞 → 关开错 = 每个网格多占一份可写副本,移动平台内存先爆
Mesh Compression 用略增 CPU 换显存;Read/Write 只在运行时改顶点时开启,否则白白翻倍内存。
Model Import Settings:网格内存与 CPURead/Write Enabled✗ 关闭只读一份默认推荐✓ 开启×2 内存仅运行时改 mesh 时何时需要 Read/Write?Mesh.vertices / RecalculateNormals / 运行时变形 → 开;纯渲染 / 烘焙碰撞 → 关开错 = 每个网格多占一份可写副本,移动平台内存先爆
Mesh Compression 用略增 CPU 换显存;Read/Write 只在运行时改顶点时开启,否则白白翻倍内存。

导入设置对照

// Model Import Settings(Inspector)
// Mesh Compression: Off
// Read/Write Enabled: ✓
// 结果:12MB 网格 + 12MB 可写副本 = 24MB/模型

动画:关键帧精度与压缩

对角色海尤其重要。Keyframe Reduction 去掉肉眼不可见的冗余关键帧;Rotation Error / Position Error / Scale Error 控制允许误差(通常 0.5° / 0.5cm 级即可)。

// Animation Import Settings
// Anim Compression: Off
// Rotation Error: 0
// 结果:Mocap 原样保留,.anim 数 MB,运行时曲线占内存

Humanoid 角色还可勾选 Optimize Game Objects,去掉运行时不需要的骨骼节点,减少 Avatar 与绑定开销——适合远处 NPC 与过场里不特写的手指骨骼。

纹理:格式、尺寸与 mipmap

决定每像素占多少字节。RGBA32 未压缩约 4 B/px;ASTC 6×6 约 0.36 B/px——同分辨率可差一个数量级。

是 UI 图标最常见的浪费:源图 4096×4096,屏幕上只显示 128×128,却仍按 4K 占显存。

对 3D 场景纹理应开启;UI、Sprite Atlas、法线贴图(部分管线)常关闭。

Texture Import SettingsFormat(平台)RGBA324 B/pxASTC 6×6~0.36 B/pxMax Size4096源图 4K1024UI 图标够用Generate Mip Maps远处用低级 · +33% 内存1024² 纹理内存(含 mipmap ≈×1.33)RGBA32 ~5.3 MBASTC ~0.5 MB规则:按用途设 Max Size · 按平台选压缩格式 · 3D 场景纹理开 mipmap,UI/Sprite 常关源文件 4K 不代表运行时必须 4K——导入设置才是内存真相
纹理内存由格式 × 分辨率 ×(mipmap 约 1.33)决定;按平台选 ASTC/ETC2,按用途限制 Max Size。
Texture Import SettingsGenerate Mip Maps远处用低级 · +33% 内存1024² 纹理内存(含 mipmap ≈×1.33)RGBA32 ~5.3 MBASTC ~0.5 MB规则:按用途设 Max Size · 按平台选压缩格式 · 3D 场景纹理开 mipmap,UI/Sprite 常关源文件 4K 不代表运行时必须 4K——导入设置才是内存真相
纹理内存由格式 × 分辨率 ×(mipmap 约 1.33)决定;按平台选 ASTC/ETC2,按用途限制 Max Size。
一摞逐级减半的纹理Level 0(原图)Level 1Level 2Level 3…更小按物体远近就近取一级近(物体大)远(屏幕上才几像素)近 → 用大的高清级远 → 用小的低清级缩小时就近取小级——既不闪烁,又省显存带宽;放大时仍用原图。
多级渐远纹理(mipmap)是一摞「逐级减半」的同图副本。物体离得近就用大的清晰级,离得远、在屏幕上只占几个像素时就用小的级——避免远处纹理闪烁,还省显存。`generateMipmap` 一行就能把这一摞全生成出来。

按用途分档

用途Max SizeMipmap格式建议
3D 场景漫反射1024–2048ASTC/BC7
UI 图标256–512ASTC 或 RGBA Crunched
字体图集512–1024Alpha8 / ASTC
远景天空盒2048压缩格式

音频:压缩格式简介

影响包体与运行时内存,但比纹理简单:

在 Memory Profiler 里搜 AudioClip,核对是否有意外解压的大文件。

Resources 陷阱 vs AssetBundle

曾是快速原型手段,现代项目应极力避免膨胀使用。

(或 Unity Addressables 封装层)才是可控的按需加载方案。

Resources vs AssetBundleResources/构建期:全部打进 Player启动时:生成资源索引表Resources.Load → 无法卸载包体↑ 启动内存↑ 隐藏依赖难查AssetBundle构建期:按包输出 .bundle运行时:LoadFromFileAsyncUnload(false/true) 可释放按需下载 · 热更 · 内存可控Resources 三大陷阱① 文件夹越大启动越慢 ② 字符串路径无编译期检查 ③ 卸载不了 → 内存只涨不降现代项目:Addressables 或 AssetBundle;Resources 仅极少量启动必需资源
Resources 全量进包且无法卸载;AssetBundle(或 Addressables)支持按需加载、依赖管理与释放。
Resources vs AssetBundleResources/构建期:全部打进 Player启动时:生成资源索引表Resources.Load → 无法卸载包体↑ 启动内存↑ 隐藏依赖难查Resources 三大陷阱① 文件夹越大启动越慢 ② 字符串路径无编译期检查 ③ 卸载不了 → 内存只涨不降现代项目:Addressables 或 AssetBundle;Resources 仅极少量启动必需资源
Resources 全量进包且无法卸载;AssetBundle(或 Addressables)支持按需加载、依赖管理与释放。
// Assets/Resources/Prefabs/Enemy.prefab
var go = Resources.Load<GameObject>("Prefabs/Enemy");
// 无法卸载;文件夹越大启动越慢;字符串路径易拼错

异步加载:把尖峰摊开

解决切场景、拉大 AssetBundle 时的卡顿——同步 LoadSceneResources.Load 大资源会在一帧内完成反序列化,Profiler 出现 CPU 尖峰。

同步 vs 异步加载时间线同步单帧尖峰异步分摊到多帧同步 Load发起 Async分帧推进激活/实例化
主线程阻塞 · 一帧卡死——异步加载把 IO 与反序列化摊到多帧,避免切换场景时单帧尖峰。
// 一帧内完成 — Memory Profiler 尖峰 + 掉帧
SceneManager.LoadScene("HugeLevel");
var bundle = AssetBundle.LoadFromFile(path);
var prefab = bundle.LoadAsset<GameObject>("Boss");

场景激活前用 allowSceneActivation = false 停在 90% 进度,让你播完 loading 动画再切入——避免「进度条满了还黑半秒」。

动手:美术资源审计四步

下面把「内存高 / 加载卡」从测量到改设置走通。每步示意图对应当前关注点——建议打开 Memory Profiler 与一个 Batches 已优化但仍内存告急的场景同步操作。

猜一猜:把一百个敌人的 Read/Write 全关掉、纹理 Max Size 从 4096 改 1024,Memory Profiler 里 Mesh 与 Texture2D 谁降得更多?

分步1 / 4

① Memory Profiler:谁占大头?

Texture Import SettingsFormat(平台)RGBA324 B/pxASTC 6×6~0.36 B/px1024² 纹理内存(含 mipmap ≈×1.33)RGBA32 ~5.3 MBASTC ~0.5 MB规则:按用途设 Max Size · 按平台选压缩格式 · 3D 场景纹理开 mipmap,UI/Sprite 常关源文件 4K 不代表运行时必须 4K——导入设置才是内存真相
纹理内存由格式 × 分辨率 ×(mipmap 约 1.33)决定;按平台选 ASTC/ETC2,按用途限制 Max Size。

打开 Memory Profiler(或 Profiler Memory 模块),按 Texture2D / Mesh / AnimationClip / AudioClip 排序。记下前三名类型与单个最大资源——通常是未压纹理或未关 Read/Write 的网格。

容易踩的坑

小结

  • Mesh:Compression 省显存;Read/Write 默认关,仅运行时改 mesh 时开
  • Animation:Optimal 压缩 + 合理 Error 阈值;NPC 可 Optimize Game Objects
  • Texture:按平台选 ASTC/ETC2;按用途限 Max Size;3D 开 mipmap、UI 常关
  • Audio:长音频 Streaming + Vorbis;短音效 ADPCM,忌大文件 Decompress On Load
  • Resources 全量进包且不可卸;用 AssetBundle/Addressables + 异步加载 控内存与尖峰

练习

问题 1(改设置型) 在项目中找一张大于 2048 的纹理和一个大 FBX,记录改前 Memory Profiler 中对应条目大小。将纹理 Max Size 改为用途合理值、格式改为平台压缩;网格关 Read/Write 并开 Medium Compression。Reimport 后复测并填表。

问题 2(问答型) Resources 与 AssetBundle 在「能否卸载」「启动影响」「包体控制」上各差在哪?

问题 3(计算型) 1024×1024 RGBA32 含 mipmap 约多少 MB?若改为 ASTC 6×6 同尺寸含 mipmap 大约多少?UI 图标只需 256 显示,Max Size 设 256 对 RGBA32 约多少 MB?

名词解释

本章出现的专业名词,用大白话再讲一遍。

Mesh Compression(网格压缩)

降低顶点精度省 GPU 内存,略增解压 CPU。详见 MeshImportDiagram。

Read/Write Enabled

保留 CPU 可写网格副本,内存约翻倍;仅运行时改 mesh 时开启。详见 MeshImportDiagram highlight readwrite。

Animation Compression(动画压缩)

Optimal 键减少 + 误差阈值缩小 .anim。详见动画 CodeTabs。

Texture Format(纹理格式)

平台压缩格式决定每像素字节。详见 TextureImportDiagram highlight format。

Max Size(最大尺寸)

导入时限制最大边长。详见 TextureImportDiagram highlight maxsize。

Generate Mip Maps(生成多级渐远纹理)

预生成逐级减半副本供远处采样。详见 MipmapPyramidDiagram。

Audio Compression

按长短音频选 Streaming/Vorbis 或 ADPCM。详见音频 Callout。

Resources 文件夹

全量进包、启动索引、不可卸载。详见 ResourcesAssetBundleDiagram mode resources。

AssetBundle

按包加载与卸载的资源包。详见 ResourcesAssetBundleDiagram。

异步加载(Async Loading)

LoadSceneAsync 等分帧加载。详见 AsyncLoadDiagram 与 CodeTabs。

资料与写作方式声明

本章以Packt《Unity Game Optimization》第3版权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…