对于Unity性能优化,目前接触到的大概有这几个方面:
1. Draw Call
2. 资源(模型、贴图、粒子)
3. 渲染(相机、光照、Shader)
4. 网络
5. 代码(代码编写、资源加载、物理系统)
可以在Unity自带的Profiler窗口查看项目性能消耗主要在哪几个地方,然后有针对性的进行优化。
作为一个程序,这里跟大家分享一下最近常用到代码方面的一点技巧,如有不足之处,欢迎大大们提出宝贵意见~
1. 在场中有大量物体频繁的激活或隐藏时,不使用SetActive(),在需要隐藏时移到屏幕外 ,显示时再移到屏幕内,即修改transform.position。还有一个方法,把需要隐藏的物体设为一个已隐藏的物体的子物体,因为父物体是未激活状态,子物体会自动隐藏,不过这种方法消耗与SetActive()差不多,不推荐使用。
下面是测试代码:
输出结果:
SetActive(true)消耗最大,SetActive(false)与transform.parent其次,transform.position消耗最小,占用的时间可以忽略不计。
2. 动态实例化到场景的物体,名字都会有一个后缀(Clone),有时候为了方便识别,会修改其名字,同样会产生性能消耗。比如:
3. 在不影响正常运行结果的情况下,减少Update或FixUpdate的调用次数
假设现在将所有的Updata刷新逻辑写在DoUpdata中
3.1 每隔一定数量帧,执行一次DoUpdata
3.2 使用协程While(true)循环,每次循环间隔一定时间,调用DoUpdata,需要在Start函数中开启协程
3.3 使用InvokeRepeating循环调用DoUpdata
4. 使用对象池
操作目标相对较少,可简化对象池,实现效果
如果目标对象较多,就需要写一个正经的对象池了,因为之前写过,这里就直接给链接啦
5. 音效播放时,为避免频繁创建、销毁播放器,可以对音效统一管理,同样之前写过,上链接~
6. 场景中经常需要动态生成GameObject,当一次创建的数量较多时,在不影响使用的情况下,可以使用协程,在多帧内完成创建,可参照:
7. 部分简单的物理计算可以不使用Unity提供的物理系统,简化物理计算量,下面是关于向量计算的一个栗子:
线段(向量)的计算(判断线段重叠、相交,合并线段,点与线的关系)_004
8. 选择合理的数据结构存储数据。
在数据结构转换时,可以避免如下的for循环,利用C#提供的方法
C#中 Array / List / Dictionary之相互转换_014
9. 其他
9.1 尽量避免在Update和for循环内创建临时变量。
9.2 尽量避免创建临时字符串。
9.3 可以使用for循环的情况,就不用foreach。
9.4 每个继承MonoBehaviour的类,都会自动生成Update方法,但很多类是用不到Update的,这时候需要将其删除,毕竟实时调用空方法,多少还是有消耗的。
9.5 数值计算中使用乘法而不用触发,比如 a / 2, 可以写成 a * 0.5f。
9.6 比较他Tag值时,使用if(gameObject.CompareTag("Tag")),而不是if(gameObject.tag == "Tag")。
9.7 开发过程中会各种Debug,这也会有一定的消耗,可以对Debug进行封装,设置一个bool值,不过这样console面板双击不会跳转到调用Debug的代码行,最好的做法是将其做成dll文件。
10、务必删除脚本中为空或不需要的默认方法;
11、只在一个脚本中使用OnGUI方法;
12、避免在OnGUI中对变量、方法进行更新、赋值,输出变量建议在Update内;
13、同一脚本中频繁使用的变量建议声明其为全局变量,脚本之间频繁调用的变量或方法建议声明为全局静态变量或方法;
14、不要去频繁获取组件,将其声明为全局变量;
15、数组、集合类元素优先使用Array,其次是List;
16、脚本在不使用时脚本禁用之,需要时再启用;
17、可以使用Ray来代替OnMouseXXX类方法;
18、需要隐藏/显示或实例化来回切换的对象,尽量不要使用SetActiveRecursively或active,而使用将对象远远移出相机范围和移回原位的做法;
19、尽量少用模运算和除法运算,比如a/5f,一定要写成a*0.2f。
20、对于不经常调用或更改的变量或方法建议使用Coroutines & Yield;
21、尽量直接声明脚本变量,而不使用GetComponent来获取脚本; iPhone
22、尽量使用整数数字,因为iPhone的浮点数计算能力很差;
23、不要使用原生的GUI方法;
24、不要实例化(Instantiate)对象,事先建好对象池,并使用Translate“生成”对象;
二、模型方面
01、合并使用同贴图的材质球,合并使用相同材质球的Mesh;
02、角色的贴图和材质球只要一个,若必须多个则将模型离分离为多个部分;
02、骨骼系统不要使用太多;
03、当使用多角色时,将动画单独分离出来;
04、使用层距离来控制模型的显示距离;
05、阴影其实包含两方面阴暗和影子,建议使用实时影子时把阴暗效果烘焙出来,不要使用灯光来调节光线阴暗。
06、少用像素灯和使用像素灯的Shader;
08、如果硬阴影可以解决问题就不要用软阴影,并且使用不影响效果的低分辨率阴影;
08、实时阴影很耗性能,尽量减小产生阴影的距离;
09、允许的话在大场景中使用线性雾,这样可以使远距离对象或阴影不易察觉,因此可以通过减小相机和阴影距离来提高性能;
10、使用圆滑组来尽量减少模型的面数;
11、项目中如果没有灯光或对象在移动那么就不要使用实时灯光;
12、水面、镜子等实时反射/折射的效果单独放在Water图层中,并且根据其实时反射/折射的范围来调整;
13、碰撞对效率的影响很小,但碰撞还是建议使用Box、Sphere碰撞体;
14、建材质球时尽量考虑使用Substance;
15、尽量将所有的实时反射/折射(如水面、镜子、地板等等)都集合成一个面;
16、假反射/折射没有必要使用过大分辨率,一般64*64就可以,不建议超过256*256;
17、需要更改的材质球,建议实例化一个,而不是使用公共的材质球;
18、将不须射线或碰撞事件的对象置于IgnoreRaycast图层;
19、将水面或类似效果置于Water图层
20、将透明通道的对象置于TransparentFX图层;
21、养成良好的标签(Tags)、层次(Hieratchy)和图层(Layer)的条理化习惯,将不同的对象置于不同的标签或图层,三者有效的结合将很方便的按名称、类别和属性来查找;
22、通过Stats和Profile查看对效率影响最大的方面或对象,或者使用禁用部分模型的方式查看问题到底在哪儿;
23、使用遮挡剔除(Occlusion Culling)处理大场景,一种较原生的类LOD技术,并且能够“分割”作为整体的一个模型。
三、其它 场景中如果没有使用灯光和像素灯,就不要使用法线贴图,因为法线效果只有在有光源(Direct Light/Point Light/Angle Light/Pixel Light)的情况下才有效果。
2.1渲染
1.不使用或少使用动态光照,使用light mapping和light probes(光照探头)
2.不使用法线贴图(或者只在主角身上使用),静态物体尽量将法线渲染到贴图
3.不适用稠密的粒子,尽量使用UV动画
4.不使用fog,使用渐变的面片(参考shadow gun)
5.不要使用alpha –test(如那些cutout shader),使用alpha-blend代替
6.使用尽量少的material,使用尽量少的pass和render次数,如反射、阴影这些操作
7.如有必要,使用Per-Layer Cull Distances,Camera.layerCullDistances
8.只使用mobile组里面的那些预置shader
9.使用occlusion culling
11.远处的物体绘制在skybox上
12.使用drawcall batching: 对于相邻动态物体:如果使用相同的shader,将texture合并 对于静态物体,batching要求很高,详见Unity Manual>Advanced>Optimizing Graphics Performance>Draw Call Batching 规格上限
1. 每个模型只使用一个skinned mesh renderer
2. 每个mesh不要超过3个material
3. 骨骼数量不要超过30
4. 面数在1500以内将得到好的效率
2.2物理
1.真实的物理(刚体)很消耗,不要轻易使用,尽量使用自己的代码模仿假的物理
2.对于投射物不要使用真实物理的碰撞和刚体,用自己的代码处理
3.不要使用mesh collider
4.在edit->project setting->time中调大FixedTimestep(真实物理的帧率)来减少cpu损耗
2.3脚本编写
1.尽量不要动态的instantiate和destroy object,使用object pool
2.尽量不要再update函数中做复杂计算,如有需要,可以隔N帧计算一次
3.不要动态的产生字符串,如Debug.Log("boo" + "hoo"),尽量预先创建好这些字符串资源
4.cache一些东西,在update里面尽量避免search,如GameObject.FindWithTag("")、GetComponent这样的调用,可以在start中预先存起来
5.尽量减少函数调用栈,用x = (x > 0 ? x : -x);代替x = Mathf.Abs(x)
6.String的相加操作,会频繁申请内存并释放,导致gc频繁,使用System.Text.StringBuilder代替
2.4 shader编写
1.数据类型 fixed / lowp - for colors, lighting information and normals, half / mediump - for texture UV coordinates, float / highp - avoid in pixel shaders, fine to use in vertex shader for position calculations.
2.少使用的函数:pow,sin,cos等
2.4 GUI
1.不要使用内置的onGUii函数处理gui,使用其他方案,如NGUI