资源核查(Asset auditing):
在实际项目中,许多资源问题都是由于我们的一些小失误造成的。比如我们一些对资源的临时测试的修改,或者在在导入资源的时候状况不佳,没有注意到要点,都默默添加了低性能的资源。
在大项目的开发中,为了避免出现这个问题,我们就需要设置一道防线。例如写一段代码来确保没有人向项目中导入4K未压缩的纹理。
然而,这是一个非常令人惊讶的普遍问题。一张4K未压缩的贴图占用了60MB内存。对于一个低端设备,使用超过200M的内存就已经非常危险了。
如果不经意间添加了一张4K未压缩的贴图,那么这张贴图可能就会占用四分之一到三分之一的内存预算,就容易发生难以诊断的内存溢出的错误。
虽然我们现在可以用一些工具(比如Memory Profiler)来追踪这些问题,但最好好事要确保这些问题一开始就不会发生。
使用AssetPostprocessor:
Unity编辑器中有一个叫AssetPostprocessor类,Unity项目中,该类可以用来实行某些最低标准。当资产被导入时,该类就会被回调。使用方法是:继承AssetPostprocessor,并且实现一个或者多个OnPreprocess方法。重要的几个包括:
- OnPreprocessTexture
- OnPreprocessModel
- OnPreprocessAnimation
- OnPreprocessAudio
更多内容可以参考Unity官方的介绍:
AssetPostprocessordocs.unity3d.com这是一个AssetPostprocessor执行的简单的例子。当项目中导入模型或者模型的导入设置发生改变时,这个类就会被调用。上面的代码只是检查了Read/Write enabled标志(代表了isReadable属性 )是否被设置成true。如果设置成true的就改成false然后保存,再重新载入。
SaveAndReimport方法会使这段代码会被再次执行。但是现在isReadable已经是false了,所以不会造成无限重新加载的死循环。
下面的“模型”小节中也会继续讨论。
资源格式配置的一些要点:
Textures:
- 关闭read/write enabled标签。
打开read/write enabled标签会造成贴图会在内存中被保留两份。一份在GPU显存中,一份在CPU可寻址内存(CPU-addressable memory)中。(提示:这是因为,CPU读取GPU显存是非常慢的。CPU读取显存中的一张贴图到一个临时的缓存中也不怎么现实)。在Unity中,read/write enabled的设置在默认情况下是关闭的,但可能会被意外打开。
Read/Write Enabled只有在Shader以外操作贴图时,才需要被打开(比如Texture.GetPixel和Texture.SetPixel APIs),但最好避免这些操作。
- 关闭Mipmaps
游戏中的一些对象,相对于相机,它们的Z轴深度不变的话,最好关闭它们的mipmaps。这样在加载贴图的过程中,可以节省三分之一的内存。如果一个对象会改变Z深度,禁用mipmaps会降低GPU对贴图的采样性能。
通常,对一些UI贴图和在屏幕中显示的大小不变的贴图来说,禁用Mipmaps是非常有用的。
- 贴图压缩
在目标平台上,我们选择一种合适的压缩格式,对节约内存来说是非常重要的。
如果我们选择了一种不恰当的压缩格式,Unity在解压时,会占用大量的CPU时间和内存。这在安卓设备上是一个很常见的问题。
- 贴图大小限制
我们在导入后,有时候就会忘记调整贴图的大小,或者会不小心改变贴图的设置。为了避免这个问题,我们需要为不同的贴图确定一个合理的大小,并通过代码来管理这个。
Models:
- 关闭read/write enabled标签。
模型中的read/write enabled标签和贴图中一样,唯一不同的是这个标签在模型中是默认开启的。
在游戏中,如果我们需要通过代码改变模型的Mesh,或者需要把Mesh赋值给MeshCollider时,Unity就会用到read/write标签。但是如果模型没有用到MeshCollider,也没有通过脚本去改变Mesh时,关闭这个标签,将会节省一半的内存。
- 非角色模型(non-character)关闭Rig
在默认情况下,Unity会给非角色的模型导入rig。这回导致当模型被实例化时,Animator组件就会被添加,而且这个组件每帧都会被调用。如果模型不需要动画,那么这就增加了不必要的开销。
- 有动画的模型打开Optimize Game Objects选项
对于有动画的模型,Optimize Game Objects选项对于性能有一定的影响。如果这个选项关闭了,那么在实例化模型的时候,Unity会创建许多Transform的层级来反映模型的骨骼结构。这些创建出来的Transform的Update开销是比较大的,尤其是当附有Particle System或者Colliders组件的时候。这同时也限制了Unity的多线程对皮肤Mesh和骨骼动画的计算。
如果模型的骨骼结构有一些地方需要暴露出来(比如说为了动态附上武器,模型的手就需要被暴露出来),那么这些部位需要在Extra Transforms上被添加。
更多细节内容可以参考Unity的Model Importer章节:
Unity - Manual: Rig tabdocs.unity3d.com- 尽量使用Mesh Compression
启用Mesh Compression可以减少用于代表模型数据不同通道的浮点数的位数。这会稍微的降低数据的精度,最后对模型产生的影响需要与美术确认。
请注意,对于不同的通道,可以使用不同级别的压缩,因此一个项目可以选择只压缩切线和法线,而不压缩UVs和顶点位置。
- Mesh Renderer设置
当给Prefab或GameObject添加Mesh Renderers的时候,注意一下Mesh Renderer的一些设置。默认情况下,Unity打开了投影和接收投影,Light Probe采样,Reflection Probe采样,和运动矢量计算。
如果我们的项目中不需要这些特性,我们可以写工具来自动关闭它们。运行时的代码如果添加了Mesh Renderer,也需要确保设置了这些选项。
对于2D游戏而言,如果添加Mesh Renderer并且打开了shadow选项。这将给渲染循环添加一个完整的阴影通道,会对性能造成浪费。
Audio:
- 适当的压缩设置
选择硬件支持的音频压缩格式。所有IOS设备都支持MP3压缩格式,许多安卓设备支持Vorbis压缩。
此外,将未压缩的音频文件导入到Unity中。在Build时,Unity总是会重新压缩音频。所以,在导入音频的时候没有必要对音频进行压缩,这也只会降低音频的质量。
- Force To Mono设置
一些移动设备有立体扬声器。在音频设置中,如果勾选了Force To Mono选项,内存消耗将会减半。但这只适用于那些没有立体效果的音效,比如UI音效。
- 降低音频码率
在确保音频质量的情况下,最小化音频的码率有利于节省内存和降低包体大小。
本文内容来自Unity官方文档:
Unity - Manual: Asset auditingdocs.unity3d.com作者水平有限,如有错误请多加指正。