很多人在做基于Unity的移动游戏开发时,对很多细节的忽略导致了最终游戏性能的低下,并且在最终需要做优化的过程中也并不觉得很多问题是需要被优化的。
这些问题在性能强劲的PC机上没问题的,但在移动硬件如此低下的今天还是会产生较大问题。现在将问题罗列如下:
1 将需要使用的属性查询缓存起来
在Unity中transform.position并不是一个简单的属性查询,Unity会调用附加函数来返回其值,所以回来带额外开销。
当平凡在Update中使用时,请缓存该属性。
2 不要频繁使用Instantiate和Destroy
这个建议来至Unity官方的移动开发优化建议,对于GameObject的创建开销Unity并没有自信,对于比较复杂的Object会产生较大的开销,因此建议使用缓存池的方式来初始化GameObject。
3 不要建立过于庞大的缓存池
依然来至于Unity官方的建议,这个问题主要是为了提醒开发者不要因为上一条建议而滥用了缓存池。正确的缓存池建立应该基于真实应用中进行调节,比如:有一个sprite最多可能需要用到100个,但80%的情况下只需要用到20个,此时初始化建立的缓存池中只需要创建20个,其余的在使用后创建并放入缓存池。在缓存池的建立时,设置一个最大值,当需要缓存的物件超过池子大小时,删除使用频率较少的物件。
4 习惯性的将暂时不用的GameObject设置为非激活
5 从项目的第一天开始就开始用Profiler来监控
很多时候,可能优化的只是一个“bug”。
容易忽略的美术资源的优化:
优化的美术制作真是一种感觉和经验的积累,能看出制作水平的不是做的效果多么犀利,而是得看制作的效果与对机器的要求等的性价比。
- 关于合并: 100个三角形的MESH,在渲染时与1500个面数的物体是没太大差别的,最佳的渲染设置应该在每个模型大约1500-4000个三角面。
- 材质共享: 如果需要通过脚本来访问复用材质属性,改变Renderer.material将会造成一份材质的拷贝。应该使用Renderer.sharedMaterial来保证材质的共享状态。
- 批处理动态物体需要在每个顶点上进行一定的开销,也有一些约束:
-
- 对VB的显存大小也有一定限制,如果着色器使用顶点位置,法线和UV值三种属性,那么只能批处理300顶点以下的物体;如果着色器需要使用顶点位置,法线,UV0,UV1和切向量,那只能批处理180顶点以下的物体了。
- 拥有lightmap的物体含有额外(隐藏)的材质属性,比如:lightmap的偏移和缩放系数等。所以,拥有lightmap的物体将不会进行批处理(除非他们指向lightmap的同一部分)。
- 使用不同缩放(scale)的物体将不能批次。分别拥有缩放尺度(1,1,1)和(2,2,2)的两个物体将不会进行批处理。
- 另外一个值得吸收的经验是非均匀缩放动画在unity中非常的慢,均匀缩放会快很多。
- 骨骼数量控制:一般来说游戏中的骨骼数量为15-60个。骨骼越少运行速度越快,一般来说30块骨骼就可以让角色动的很舒服了。建议每个角色30个骨骼,就按照这个规范吧。
- 尝试用压缩贴图格式,或用16位代替32位。
程序开发层面的注意点:
- 垃圾回收器收集垃圾内存时负载较大,对移动设备是个大问题,因此要从代码层面减少临时内存的生成,
1) 移除代码中的任何字符串连接,因为这会给GC留下大量垃圾。
2) 用简单的“for”循环代替“foreach”循环。由于某些原因,每个“foreach”循环的每次迭代会生成24字节的垃圾内存。一个简单的循环迭代10次就可以留下240字节的垃圾内存。
3) 更改我们检查游戏对象标签的方法。用“if (go.CompareTag (“Enemy”)”来代替“if (go.tag == “Enemy”)”
- 使用#pragma strict 简单的添加#pragma strict在脚本顶部,之后Unity将禁用脚本的动态类型,强制你使用静态类型。
- 缓存各种组件、Object查找
- 尽量使用固定的内置数组:内置数组是非常快的。ArrayList或Array类很容易使用,更方便使用。但是他们有完全不同的速度。内置数组还有好处是,内存连续,元素对齐。
- 不要使用System,System.Xml以及其他系统自带的DLL,会多出几兆空间,找找开源的。
- 尽量采用内置的高效的shader吧,例如(built-in)Shader mobile,如果自己写,复杂的数学计算函数别用,alphatest慎重(美术几何挖洞来实现吧)。shader中注意float/half/fixed的使用。