Unity性能优化之代码优化

对于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. 使用对象池

操作目标相对较少,可简化对象池,实现效果

如果目标对象较多,就需要写一个正经的对象池了,因为之前写过,这里就直接给链接啦

对象池(重复资源的循环利用)_008

5. 音效播放时,为避免频繁创建、销毁播放器,可以对音效统一管理,同样之前写过,上链接~

全局音效管理器_001

6. 场景中经常需要动态生成GameObject,当一次创建的数量较多时,在不影响使用的情况下,可以使用协程,在多帧内完成创建,可参照:

动态创建地图及随机资源位置_003

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

 

猜你喜欢

转载自blog.csdn.net/alone_ws/article/details/84580991