Unity项目优化总结 联系客服

发布时间 : 星期四 文章Unity项目优化总结更新完毕开始阅读7742c47cf61fb7360a4c650a

Unity项目优化总结

1、IOS闪退优化

A:IOS上C#代码通过AOT(Ahead of time)编译,而不是JIT(Just in time)。AOT实现反射机制需要预先分配一块内存存类的元数据,当过度使用泛型时导致类元数据过大,预分配内存不够就闪退抛出“trampoline out of memery”异常。默认nrgctx-trampolines=1024,nimt-trampolines=1024,ntrampolines=512,建议优化为nrgctx-trampolines=2048,nimt-trampolines=2048,ntrampolines=1024。 B:逻辑上有一条路径出现无限递归导致栈溢出闪退。 C: IOS上不支持字符集”GBK”导致闪退,改成“UTF-8”。 D: 程序逻辑出现两个空指针异常导致闪退。

2、纹理优化

目前纹理峰值占用能够到110M,纹理普遍压缩率不够或没有压缩,而且场景切换一些资源未卸载,导致内存高。纹理包括 UI 、场景、特效、人物纹理等等,优化后保证纹理在50M以内,优化方式如下:

A、纹理压缩格式: 目前纹理格式要么没压缩,要么普遍采用Automatic Compressed,这个格式将RGBA 32bits压缩为RGBA16 bits,压缩率不高。建议纹理压缩格式如下:

Android 平台: RGBA Compressed ETC1+alpha bits(25%压缩率) IOS平台: RGBA Compressed PVRTC 4 bits(1/8压缩率)

B、纹理缓存的问题, 目前程序之中设置的当机器可用内存大于50M时切换场景不释放资源,导致一些常驻内存没有释放。建议调大这个阀值或者去掉这种策略。 C、禁止纹理的MipMaps生成。

D、纹理打图集的问题,建议装备图标和技能图标都打图集,降低UI DrawCall和IO次数。采用纹理压缩格式,这部分内存占用不会太大。 总结:纹理优化后,纹理内存从72M降低到30M。

3、特效优化

目前战斗特效,特别是BOSS的特效,在粒子个数、粒子范围、特效Shader等方面做得比较耗,导致帧率下降很厉害,测试机器在BOSS放特效时从60帧降到了20多帧。单个粒子面数建议50左右,粒子的总面数400左右,尽量少用UV动画和粒子旋转。粒子属于半透,需要做Aphla Blend,如果范围过大会导致像素填充率(Fill Rate)低,每一帧的垂直等待时间长,建议粒子范围不要太大,不要搞全屏特效,一些比较夸张的效果可以通过图像后处理(PostEffect)实现。 粒子特效公用Particles/Addtive Shader 优化建议如下:

Shader \Properties {

//_TintColor可以优化掉

_TintColor (\ _MainTex (\

_InvFade (\

}

SubShader {

Tags { \ Pass {

Tags { \ ZWrite Off Cull Off

Fog {Color (0,0,0,0)} Blend SrcAlpha One

//Alpha Test 没必要,可优化掉 AlphaTest Greater 0.01 ColorMask RGB Program \

SubProgram \

Keywords { \\

#ifdef VERTEX

attribute vec4 _glesVertex; attribute vec4 _glesColor;

attribute vec4 _glesMultiTexCoord0; uniform highp mat4 glstate_matrix_mvp;

//控制UV动画的变量,特效中似乎一般也都没使用到,可以考虑优化掉 uniform highp vec4 _MainTex_ST; varying lowp vec4 xlv_COLOR;

varying highp vec2 xlv_TEXCOORD0; void main () {

gl_Position = (glstate_matrix_mvp * _glesVertex);

xlv_COLOR = _glesColor;

//UV动画可优化掉, 优化为:TEXCOORD0 = _glesMultiTexCoord0.xy

xlv_TEXCOORD0 = ((_glesMultiTexCoord0.xy * _MainTex_ST.xy) + _MainTex_ST.zw); }

#endif

#ifdef FRAGMENT

uniform sampler2D _MainTex; uniform lowp vec4 _TintColor; varying lowp vec4 xlv_COLOR;

varying highp vec2 xlv_TEXCOORD0; void main () {

lowp vec4 tmpvar_1;

// 2.0 * _TintColor默认等于Vector4.One 可优化掉,因此这里3次乘法可以优化成1次,优化为: //COLOR * texture2D (_MainTex, xlv_TEXCOORD0))

tmpvar_1 = (((2.0 * xlv_COLOR) * _TintColor) * texture2D (_MainTex, xlv_TEXCOORD0)); gl_FragData[0] = tmpvar_1; }

#endif\}

4、Mono堆程序优化

Mono Heap占用的内存都是C#代码申请的托管内存,其大小与 GameObject数量、资源量无关,仅是C#代码造成的。Mono向OS申请的内存在程序结束前不会还给OS,导致Mono Heap 无法收缩,查阅官方文档发现这个问题是由于Mono造成的,Unity没有解决。而GfxDriver 占用的内存是指资源占用的内存(Texture,Mesh、Vertex

buffer和 index buffer),当资源内存释放后,GfxDriver会将内存还给OS,从而收缩自

己。

在进入游戏后主场景Mono HeapSize =42M,其中UsedHeapSize = 33M, UnUsedHeapSize = 9M。切换几次场景后回到主场景HeapSize = 92.5M,其中UsedHeapSize = 45M,UnUsedHeapSize = 47.5M。当OS内存不够,而Mono Heap内存充足,申请新的资源内存会失败,抛出内存不足异常。因此,Mono Heap内存占用需要优化。

1、在场景切换的时候,由于采用SceneResRecords脚本保存场景自定义数据,该脚本被挂到资源Prefab上, 而其成员SceneResRecords.bytes[]是个大数组大概200K左右,并且解析这个数组的过程中每次差不多要申请600~1000个Data临时类对象,

可能导致大量碎片;极端情况下大数组和碎片会导致Mono Heap分配内存失败,从而向OS申请更多的内存。

建议将SceneResRecords数据从场景AssetBundle中分离出来放文件中,通过文件流的方式读取解析,从而避免大数组的内存申请。其次,建议将Data实例做池化缓冲,减少频繁小内存的分配操作。未进行UI操作只频繁切换场景的情况下,经测试此次优化,Mono Heap内存峰值从92.5M降低到70M,优化了23M左右。

2、AssetBundleLoad. LoadAssetEnd ()函数加载每一个AssetBundle都会分配该文件大小的byte[]临时数组,场景中有些AssetBundle会达到几兆,这也会导致大临时内存分配。建议将Byte[]数组优化为静态的Buffer数组进行复用,减少大数组的分配请求。StreamingAssetLoad.GetFileBytes()函数建议优化掉,该函数直接将整个文件读入内存,会申请文件大小的内存数组。未进行UI操作只频繁切换场景的情况下,经测试此次优化在第一步的基础上从峰值70M降低到54M,优化15M左右。

3、分析发现UI资源都存在此类问题,频繁的进行UI操作导致Mono Heap暴增到112M, 由于UI脚本中有大数组成员,并且UI操作频繁导致Mono堆的暴增,这个问题解决方法同第一个问题类似将UI脚本中大数组数据从AssetBundle中分离出来放入文件中,通过文件流的方式读取解析,从而避免大数组的内存申请。经过上述优化,频繁进行 UI操作时Mono Heap内存堆峰值大小为120M,优化后降低到64.8M。

总结:优化前Mono Heap内存堆在UI频繁操作极端情况下会占用120M,优化后降低到了64.8M。编写脚本代码时一定要注意脚本中包含的大数组成员,尽量减少大数组或分割大数组为小数组。

5、场景优化

1、先前做本项目优化时考虑不足,将场景中所有Mesh通过MeshBaker进行了合并以降低场景的Drawcall,虽然场景中DrawCall降到10个左右,但是引入了新的问题。大部分场景中的节点都有进行复用和Mesh数据共享,粗暴的进行进行材质和Mesh数据的合并,会生成一份大材质,并且每份Mesh被共享多少次就会生成多少Copy数据,导致顶点数据产生很多冗余的内存占用。本次优化只进行材质合并,不进行Mesh合并,让Unity引擎在运行过程中通过相同材质的Static Batch操作进行优化,从而即优化DrawCall又不造成额外内存占用。

2、少数场景做得比较大,如超级堡垒3,面数130k太多,Mesh定点在内存中占30M左右,其他一般在15M以下,大的场景模型建议优化到15M左右。一些场景的