主要介绍CPU优化、GPU优化、内存优化
一、 CPU优化
1. 对DrawCall的优化
DrawCall是CPU调用底层图像的接口函数,也就是渲染的次数,其实一个场景,渲染是由GPU做的,它的工作量不会改变,但是CPU发号指令(调用DrawCall)的次数,却大大影响了CPU的效率。为了减少DrawCall的调用次数,主要有以下三个方法:
a. 批处理(Draw Call Batch)
批处理是将有相同材质的物体一起打包(它们可能有不同的网格模型),只调用一次DrawCall,这样它们可以使用相同的纹理渲染。
(1)静态批处理(推荐)
如果场景中的一些物体不会移动,不会旋转,比如山、树、房子等,那么相同材质的物体就可以使用静态批处理,使用时,只需要在属性框中勾选static选项即可。
(2)动态批处理
动态批处理是引擎自动处理的,开发者不能设置。比如从prefab中生成500个物体时,引擎会自动进行动态批处理,只调用一次DrawCall。但是动态批处理有很多限制条件:
i. 动态批处理仅支持顶点数小于900个的网格物体
ii. 如果着色器使用顶点位置、法线、UV值三种属性,则仅支持300顶点以下的物体。如果着色器使用顶点位置、法线、UV0、UV1和切向量,那么仅支持180个顶点以下的物体。
iii. 不要使用缩放,统一缩放的物体与非统一缩放的物体不会一起进行批处理。
iv.不同材质的物体不会一起进行缩放
v.拥有lightmap的物体不会进行批处理
vi. 多通道的shader会妨碍批处理。
b. 通过把纹理打包成图集以减少材质的使用(在GPU优化中也提到)
c. 尽量少使用反光、阴影,因为那样会使物体多次渲染。
2. 对物理组件的优化
a. 设置合理的Fixed Timestep,这个值设置的是计算物理效果的频率。
b.尽量不要使用网格碰撞器(mesh collider)
3. 代码优化
代码的优化其实是为了不要频繁地触发GC,需要注意的有:
a. 尽量不要使用foreach,使用for,因为foreach会涉及到迭代器,每次循环对产生24Bytes的垃圾。
b. 不要直接访问gameobject的tag属性,用if(go.CompareTag("a"))来代替if(go.tag=="a")
c. 使用“池”,以实现空间的复用
d.最好不用LINQ的命令,因为它们会分配临时空间,而且它不能很好地进行AOT静态编译,这样在IOS平台上可能会出错。
e. 只获得一次transform,然后保留引用。
f.不要频繁使用GetComponent。
g. 善于使用OnBecameVisible()来控制update函数的执行次数。
h. 使用内建数组,比如使用Vector3.zero而不是Vector3(0,0,0).
i. 对于调用函数时参数中的值传递,如果值很“大”,则尽量使用ref关键字。
二、对GPU的优化
1. 减少顶点数
a. 保持材质数目尽量少
b. 使用纹理图集(一张大贴图里包含了很多子贴图)来代替一系列的小贴图,它们可以更快地被加载,具有很少的状态转换。
c. 如果使用了纹理图集与共享材质,使用Renderer.sharedMaterial来代替Renderer.Material
d. 使用光照纹理而非实时灯光
e. 使用LOD,远处的物体细节就可以忽略
f. 遮挡剔除
g. 使用moblie版的shader
2. 压缩图片
a. OpenGL ES2.0 使用ETC1格式压缩,在打包设置里有。
b. 使用MipMap,MipMap就是在一张图片上有原图各个尺寸大小的图片,虽然会占用内存,但是会优化显存带宽(就是牺牲CPU,成就GPU),因为GPU会选择适合的小图来渲染,比图片压缩质量好。
三、内存的优化
内存分为三个部分:
1. Unity 3d的内部内存:主要是资源(纹理、网格、音频等)、GameObject和各种组件、引擎内部逻辑需要的内存(渲染器、物理系统、粒子系统等)。
2. Mono的托管内存:主要是引用类型、值类型。Mono使用的是Boehm GC,它有以下一些特点:
a. 基于Mark/Sweep,无分代、并行
b. 执行时所有线程阻塞(stop the world)
c. 堆越接近满的状态,执行地越频繁
d. 每次标记都会扫描访问所有可到达的对象。
e. GC.Collect()不会处理栈、寄存器、静态变量
Mono托管堆中那些封装的对象,除了在Mono托管堆上封装类实例化后,还会牵扯到其背后对应的游戏引擎内部空间在Unity3D的内部内存上的分配。比如 使用WWW下载资源,除了www对象内存,还有待压缩文件、文件解压缩所需缓存、解压缩后的文件所需的内存。
3. 外部引用的dll文件所需的内存
总结一下: