Unity优化小总结

【参考】网上资源汇总


一、优化部分总结

1、更新不透明贴图的压缩格式为ETC4bit,因为Android市场的手机中的CPU有多种,每家的CPU支持不同的压缩格式,但他们都兼容ETC格式。

2、透明贴图,选择RGBA16bit或者RGBA32bit。

3、减少FPS,在ProjectSetting -->Quality中的VSync Count参数会影响FPS。选项EveryVBlank相当于FPS = 60 ,EverySecondVBlank 相当于FPS= 30 ,Don’t Sync 表示取消垂直同步,对FPS不做限制,可以在Awake方法里手动设置FPS(Application.targetFrameRate = 45)。

降低FPS的好处:省电,减少手机发热的情况;稳定游戏FPS,减少卡顿情况

水平同步:决定了CRT(阴极射线管(CathodeRay Tube)的显示器)画出一条横越屏幕线的时间。

垂直同步:决定了CRT从屏幕顶部画到底部,再返回原始位置的时间。垂直同步代表着CRT显示器的刷新率水平。

为什么关闭垂直同步信号?如果垂直同步信号打开,那么在游戏中或许强劲的显卡迅速的绘制完一屏的图像,但是没有垂直同步信号的到达,显卡无法绘制下一屏,只有等信号到达,才可以绘制,垂直同步的刷新率固定了屏幕刷新时间,限制了FPS数值。

4、设置FPS后,再调整ProjectSetting -->Time 下 Fixed timestep参数,目的是减少物理计算的次数,提高游戏性能。

5、尽量少使用Update、LateUpdate、FixedUpdate,提升性能和节省电量,多使用事件(不是SendMessage,使用自己写的委托事件,或者c#中的事件委托)

6、待机时,调整游戏的FPS为1,节省电量。

7、图集大小不要高于1024,否则游戏安装后,低端机直接崩溃,原因是手机系统版本低于2.2,超过1000的图集无法读取导致。

8、合并材质球,合并Mesh网格(一个网格只用一个材质,不用或禁用多维子对象材质,四边形网格导入时会自动变为三角形)。

合并材质球:

---采用TexturePacking
(1)遍历gameobject,取出material,并根据shader来将material分类
(2)调用Unity自带的PackTextures函数来合并每个shader分类中的material所对应的textures(PackTextures函数有缺陷,不过可以将就用)
(3)根据合并的大的texture来更新原有模型的texture、material 、uv坐标值。

---合并Mesh网格

(1)自带合并,勾选静态。

(2)脚本合并Mesh

参考:

using UnityEngine;
using System.Collections;
 
public class Combine : MonoBehaviour
{
     voidStart()
     {
            //MeshFilter网格过滤器,使用这个作为一个程序的网格接口
            MeshFilter[]meshFilters = GetComponentsInChildren();
            //CombineInstance合并实例,用来描绘网格合并的结构
            CombineInstance[]combine = new CombineInstance[meshFilters.Length];
           
            for(int i = 0; i < meshFilters.Length; i++)
            {  
                   //.mesh合并网格方法
                   combine[i].mesh= meshFilters[i].sharedMesh;//返回网格过滤器的共享的网格。
                   //.transform合并之前,网格变换的矩阵。
                   combine[i].transform= meshFilters[i].transform.localToWorldMatrix;//返回对象的世界坐标位置
                   meshFilters[i].gameObject.active= false;
            }
            //新建网格
            transform.GetComponent().mesh= new Mesh();
            //组合多个网格到同一个网格
            transform.GetComponent().mesh.CombineMeshes(combine);
            transform.gameObject.active= true;
     }
}


9、角色Material数量在2~3个;骨骼数量在30个之内;面片数量在300~1500之间(游戏质量和性能的均衡值);一般角色应该没有IK节点(因为角色的动作大多数都是事先设定好的,不需要经过IK操作来进行实时计算,所以模型导入时,不要讲IK结点一起导入)。

10、静态实体。不要附加Animation组件,虽然对结果没有影响,但却会增加一定的CPU开销来调用这一组件;网格顶点数小于500;UV值范围尽量不要超过(0,1)区间(利于纹理合并优化)。

11、地形。长宽尽量小于257,因为地形太大,会造成大量顶点数据,给内存带宽造成一定影响,在IOS设置中,内存带宽是非常有限的;如果使用Unity自带地形,一定使用Occlusion Culling,因为Unity的刷地形工具虽然方便,但却是frameKiller,刷过之后,会发现DrawCall增加的非常多;混合纹理数量不要超过4个,地形的混合操作是很耗时的,应该尽量避免,能合并的纹理尽量合并。

12、纹理。建议png或tga,不用转成ios硬件支持的PVRTC格式,因为Unity发布时会自动转的;纹理尺寸长宽小于1024,同时尽可能的小,够用就好,以保证纹理对内存带宽的影响达到最小;支持MipMap,虽然会增加一些应用程序的大小,但在游戏运行时,系统会根据需求应用MipMpa来渲染,从而减少内存带宽;检测Aplha值,如果纹理通道为1,则用RGB的24位纹理来代替RGBA的32位纹理。

13、光源。“Important”光源个数建议1个,一般为方向光,如果光源为“Important”,则会进行像素渲染,否则进行顶点渲染;Pixel Light数目1~2个。

14、粒子特效。屏幕上最大粒子个数建议小于200个粒子;每个粒子发射器发射最大粒子数建议小于50个;粒子大小Size应该尽可能的小,因为Unity的粒子系统的shader无论是alpha test还是alpha blending都是一笔不小的开销,对于非常小的粒子,建议粒子纹理去掉alpha通道;尽量不要开启粒子的碰撞功能,非常耗时。

15、音频。游戏中播放时间较长的音乐(背景音乐)使用.ogg 或.mp3的压缩格式;较短音乐(枪声)使用.wav和.aif的未压缩音频格式。

16、相机。裁剪平面,将远平面设置成合适的距离,远平面过大会将一些不必要的物体加入渲染,降低效率,根据不同的物体设置不同的远裁剪平面;unity提供了可以根据不同的layer来设置不同的view distance,所以我们可以实现将物体进行分层,大物体层设置的可视距离大些,小物体层可以设置的小些,另外一些开销较大的实体(如粒子系统)可以设置得更小些等等。

17、碰撞。尽量不用MeshCollider,尽量要减少Mesh的面片数,或用较少面片的代理在代替。

18、DrawCall。

IOS设备上不建议超过100(20个最ok),减少方法主要有如下几种:FrustumCulling(视锥体剔除),OcclusionCulling(遮挡剔除),Texture Packing(近距离,同种shader纹理拼合)。Frustum Culling是Unity自带剔除,只需我们设置合理的远裁剪面;Occlusion Culling,确定好场景是否适合使用OC,密集场合使用OC;Texture Packing或者叫Textures Atlasing,将同种shader的纹理进行拼合,根据Unity的static batching 的特性来减少DrawCall,建议使用,限近距离模型使用,否则可能会增加每帧渲染所需的纹理大小,加大内存带宽的负担,这就是“draw call降低了,渲染速度也变慢了”原因。

非运动物体尽量打上Static标签,unity会自动对static物体进行优化处理。

场景中尽可能的使用Prefab,尽可能的使用Prefab的实例化物体,以降低内存带宽的负担,检测实体的PrefabType,尽量将其变成PrefabInstance(在Hierarchy编辑后,拖回Project中),而不是ModelPrefabInstance(美工直接给的模型,不在Hierarchy下进行编辑,拖回Project中的模型)。

19、材质方面。Android上使用ETC1,苹果上使用PVRTC;UV控制在0~1之间;角色模型面数控制在1500之内,骨骼控制在30个以内;场景中使用一个主光(不能再多);尽量减少alpha test 和alphaBlend材质的使用,很杀效率。

20、骨骼动画方面。动画方面考虑不使用插值,固定帧率的动画;如果用插值,考虑使用四元数(表示旋转)和向量(表示位移)来做插值,四元数做插值速度比矩阵来的快,Slerp提供了平滑插值。

 

二、优化的常规技巧

1、剖析游戏。不要花费时间来优化代码或者缩减图形文件的大小,除非这是你游戏的瓶颈。

2、第一次剖析,会使你发现游戏的瓶颈,Apple’s Shark是一个很好的用来剖析基于OpenGl程序的工具。

第二次剖析,可以检查优化是否达到了预期效果,或许会发现更多的瓶颈。

3、在 Scene View中测试场景。这样做将会使你清楚了解这个场景中的物体或者附加在物体上的脚本是否降低了游戏性能;如果Scene View反应迟钝,那么有可能是图形方面的原因,如果Scene View反应不迟钝,那么瓶颈可能出在脚本或者物理系统上。

4、禁用指定游戏对象。在play模式下,尝试禁用并启用游戏物体来排查出游戏慢的原因。

5、光照。不要使用过多的像素光,可以使用质量管理器来调节像素光的数量来取得一个性能和质量的均衡点。

性能占用顺序(从小到大):聚光灯-->点光源-->平行光。一个好的点亮场景的方法就是先得到想要的效果,然后在保持光效的前提下,看哪些光可以去掉。

点光源和聚光灯只影响他们范围内的网格,一个网格在有8个以上光源影响的时候,只响应前8个最亮的光源。

6、贴图。在外观不变的前提下,贴图大小越小越好;不要使用低质量的图片,在发布游戏的时候,引擎会自动压缩这些图片,多重压缩和解压将会降低图片的质量,所以最好保持贴图文件的分辨率为原始分辨率,这样就会减少多重压缩和解压缩导致的图片失真现象。

7、粒子系统。unity中,在摄像机范围外的粒子系统虽然不会被绘制,但是update是一直持续的,这也就意味着,无论粒子是否可见,它都在更新,为了不必要的update开销,主要用到2个函数OnBecameInvisible()当变为不可见和OnBecameVisible()当变为可见,前提是该对象,绑定了MeshRender组件,如果不可见把active设置为false,反之ture。

8、Draw CallBatching (绘制调用批处理)。

Unity内建的批处理机制所达到的效果要明显强于使用几何建模工具(或使用Standard Assets包中的CombineChildren脚本)的批处理效果,因为Unity引擎的批处理操作时在物体的可视裁剪操作之后进行的,Unity先对每个物体进行裁剪,然后再进行批处理,这样可以使渲染的集合总量在批处理前后保持不变,但是使用几何建模工具和拼合物体,会妨碍引擎对其进行有效的裁剪操作,从而导致引擎需要渲染更多的几何面片。

只有拥有相同材质的物体才可以进行批处理(要求程序中尽可能的复用材质和物体)。如果两个材质仅仅是纹理不同,可以对纹理进行拼合。

动态批处理,如果动态物体共用着相同的材质和相同网格,那么Unity会自动对这些物体进行批处理。动态批处理操作时自动完成的。

批处理动态物体需要在每个顶点上进行一定的开销,所以动态批处理仅支持小于900顶点的网格物体;如果着色器使用顶点位置,法线和UV值三种属性,那么只能动态批处理300顶点以下的物体;如果着色器需要使用顶点位置,法线,UV0,UV1和切向量,那么只能动态批处理180顶点以下的物体。

缩放尺度会影响批处理,使用缩放尺度(1,1,1)和(1,2,1)两个物体将不会进行批处理,但是使用缩放尺度(1,2,1)和(1,3,1)的两个物体将可以进行批处理。

静态批处理,允许引擎对任意大小的几何物体进行批处理操作来降低绘制调用(只要这些物体不移动,并且拥有相同的材质)。在静态批处理之前,如果一些物体共用了同样的几何数据,那么引擎会在编辑以及运行状态对每个物体创建一个几何数据的备份,这并不是一个好的想法,因为有时候,你将不得不牺牲一点渲染性能来防止一些物体的静态批处理,从而保持较少的内存开销,比如,将浓密森林里的树(同材质)设为static,会导致严重的内存开销,因为静态批处理缘故,一次性要加载一个非常大的静态对象,还有因此产生的备份数据。

9、Shader。

Unity官方文档中讲,由于硬件原因,在ios设备上alpha test会造成很大的性能开销,应尽量使用alpha blend代替。如果同屏使用alpha blend的面数,尤其是这些面所占屏幕面积的大小,对性能也会造成很大的影响,原因是使用alpha blend的面会造成overdraw的增加,对低性能设备的影响很大。

Per pixel shader 即片段shader,对每个渲染到屏幕上的像素做处理的shader,如果per pixelshader 比较复杂且需要处理的像素很多时,也就是使用该shader的面占屏幕面积很大时,对性能的影响甚至要超过alpha blend ,因此复杂的per pixel shader 只适用于小物体。

Specular Map通常都是利用贴图的alpha通道来定义物体表面的光滑程度(反光度)。这个shader的特点是per vertex计算反光度的,有着相当不错的效果,同时比per pixel的shader性能高很多,这个shader适用于关卡环境等占很大区域的模型。

经过优化的动态角色光照和阴影(light probes 和BRDF Shader),传统的lightmap无法支撑动态物体,对此Unity提供了light probes技术,预先把动态物体的光照信息保存在代理对象(即light probes)中,运行时动态物体从距离最近的Probe中获取光照信息。

Unity本身还提供了一个效果非常棒的专为移动设备优化的角色shader,支持Diffuse、Specular和Normal maps,并通过一个特殊的脚本生成贴图用于模仿BRDF光照效果,最终产生的效果堪比时代大作中的角色光影效果。

雾和体积光(shader Blinking Godrays),目前在移动设备上要开启真正的雾效果基本不可行,通过“简单的网格+透明贴图(称为雾面)”shader来模拟雾效。在玩家靠近时,屋面逐渐变淡,同时fog plane的顶点也会移开(即使完全透明的alpha面也会消耗很大渲染时间)。这个shader的网格需要经过处理,顶点的alpha值用于决定顶点是否可以移动(0为不可动,1为可动),顶点法线决定移动的方向,然后shader通过计算与观察者的距离来控制屋面的淡入/淡出。这个shader还可以用来做体积光和其他一些alpha效果。

飞机坠毁的浓烟效果(shader Scroll 2 layersSine Alpha- blended)。通过粒子产生浓烟的代价太高,使用“网格+贴图动画”shader来制作这个效果,通过混合两层贴图并让他们交错移动来产生动画效果,其中顶点alpha值用于让网格的边缘看起来比较柔和,同时使用顶点颜色来模拟从火焰刀烟雾的过渡效果。

动态效果的天空盒(Shader Scroll 2 layersMultiplicative),通过两张贴图的混合和移动产生云的动态效果。

旗帜和衣服的飘动效果(Shader Lightmap +wind),利用每个顶点alpha值决定哪些顶点可以移动,然后shader的参数用于调整摆动的方向和速度。

 

三、优化分类总结

1、程序方面

务必删除脚本中为空或不需要的默认方法;

只在一个脚本中使用OnGUI方法;

避免在OnGUI中队变量、方法进行更新、赋值,输出变量建议在Update内;

同一脚本中频繁使用的变量建议声明为全局变量,脚本之间频繁调用的变量或方法建议声明为全局静态变量或方法;

不要去频繁获取组件,将其声明为全局变量;

数组、集合类元素优先使用Array,其次是List;

脚本在不使用时脚本禁用,需要则再启动;

需要隐藏/显示或实例化来回切换的对象,尽量不要使用SetActiveRecursively或active,而使用将对象远远移出相机范围和移回原位的做法;

尽量少用模运算和除运算,比如a/5f,一定写成a*0.2f;

对于不经常调用或更改的变量或方法建议使用Coroutines&Yield;

尽量直接声明脚本变量,而不使用GetComponent来获取脚本;

对于iphone,尽量使用整数数字,因为iphone的浮点数计算能力很差;

不要使用原生的GUI方法;

不要实例化(Instantiate)对象,事先建好对象池,并使用Translate“生成”对象;

2、模型方面

合并使用同贴图的材质球,合并使用相同材质球的Mesh;

角色的贴图和材质球只要一个,若必须多个则将模型分离为多个部分;

骨骼系统不要使用太多;

当使用多角色时,将动画单独分离处理;

使用层距离来控制模型的显示距离;

阴影其实包含两方面--阴暗和影子,建议使用实时影子时把阴暗效果烘焙出来,不要使用灯光来调节光线阴暗;

少使用像素灯和使用像素灯的shader;

如果硬阴影可以解决问题就不要用软阴影,并且使用不影响效果的低分辨率阴影;

实时隐形很消耗性能,尽量减少产生阴影的距离;

允许的话在大场景中使用线性雾,这样可以使远距离对象或阴影不易察觉,因此可以通过减小相机和阴影距离来提高性能;

使用圆滑组来尽量减少模型的面数;

项目中如果没有灯光或对象在移动那么就不要使用实时灯光;

水面、镜子等实时反射/折射的效果单独放在Water图层中,并且根据其实时反射/折射的范围来调整;

碰撞对效率的影响很小,但碰撞还是建议使用Box、Sphere碰撞体;

尽量将所有的实时反射/折射(如水面、镜子、地板等等)都集合成一个面;

假反射/折射没有必要使用过大分辨率,一般64*64就可以,不建议超过256*256;

需要更改的材质球,建议实例化一个,而不是使用公共的材质球;

将不需射线或碰撞事件的对象置于IgnoreRaycast图层;

将水面或类似效果置于Water图层;

将透明通道的对象置于TransParentFX图层;

养成良好的标签(tags)、层次(Hieratchy)和图层(Layer)的条理化习惯,将不同的对象置于不同的标签或图层,三者有效的结合将很方便的按名称、类别和属性来查找;

通过Stats和Profile查看效率影响最大的方面或对象,或者使用禁用部分模型的方式查看问题到底在哪儿;

使用遮挡剔除处理大场景,一种较原生的类LOD技术,并且能够“分割”作为整体的一个模型。

3、其他

场景中如果没有使用灯光和像素灯,就不要使用法线贴图,因为法线效果只有在有光源(Direct light/Point light/Angle light/Pixel light)的情况下才有效果。

你可能感兴趣的:(Unity)