# 对 Unity 3D 手游优化技术的调研

学习自网络以及蛮牛教育公开课

与 PC 平台相比,移动平台有其瓶颈:体积小,功耗小,带宽小;因此在开发移动平台的游戏时,可以借助一些性能优化方法来提高游戏性能,改善画面质量。

用户可在以下方面进行优化:资源方面、引擎方面、代码方面;

资源方面 #

动态物体:

对游戏主角,怪物,NPC 控制面片数量 300-2000 面片;
控制 Skinned Mesh Render 为 1 个;
控制材质数量 1-3 个;
控制骨骼数量 < 30 根;

静态物体:

一般静态物体控制网格顶点数 < 500 个;
标记为 Static;
Static Batching 优化;

美术资源方面:

自带地形 控制地形的分辨率 地形高度图尺寸小于 257;地形纹理中尽量使用少的混合纹理数目,尽量不要超过 4 个;

纹理数据 纹理格式尽量采取压缩格式;纹理尺寸 长宽小于 1024,同时应该尽量小,够用即可;建议使用 Mipmap,虽然有可能会增加程序的大小,但会提高渲染效率;

音频数据 播放时间长的音乐如背景音乐 可以使用 .ogg 或 .mp3 的压缩文件格式;播放时间短的音乐如枪声 可以使用 .wav 或 .aif 格式的未压缩格式;

引擎方面 #

光源设置:

在满足效果的前提下,控制场景中光源的个数,不要添加冗余光源;
控制 Important 光源的数目,尽量 1 个或干脆没有,个数越多 DrawCall 越多;
Pixel Light Count 1~2 个;

相机设置:

设置合理的剪切平面,可根据不同的场景进行不同的修改;

粒子效果:

屏幕上的粒子总数建议小于 200 个粒子,每个粒子发射器所发射的粒子总数建议不超过 50 个;
粒子大小 size 应该尽量的小,对于非常小的粒子,在其粒子纹理中可以去除 Alpha 通道;

物理引擎优化:

尽可能使用 Sphere Collider 和 Box Collider,尽量避免使用 Mesh Collider,因为希望计算量比较小;

渲染优化:

尽可能的避免使用 Alpha Test 和 Alpha Blend;
移动平台上 Alpha Test 的性价比比较低,可能的话,使用 Alpha Blend 来替代 Alpha Test;
将使用 Alpha Test 和 Alpha Blend 的像素数将至最低。

DrawCall Batching:Unity 在渲染时,可以将一些物体合并,从而可以使用一个 DrawCall 来渲染他们,这称为 DrawCall Batching;理论上讲,Unity Batching 的物体越多,则会得到更好的渲染性能。

Static Batching 针对静态物体进行 Batching,对几何数据的大小没有限制;原理:静态 VertexBuffer + 动态 IndexBuffer,将同种材质的物体合并在一个大的 VertexBuffer 中,在运行时,通过视锥剪切来动态的改变 IndexBuffer;注意:使用 Static Batching 后会额外的增加内存开销来存储 Batch 后的数据。

Dynamic Batching:对于相同材质的动态物体,Unity 会自动对其进行 Batching;原理:动态 VertexBuffer + 动态 IndexBuffer;注意:目前仅支持小于 900 顶点的网格物体;如果 Shade 里使用 Position、Normal、UV 三种属性,则只能 Batch 300 顶点以下的物体,如果使用 Position、Normal、UV、UV1、Tangent 等属性,则只能 Batch 180 顶点以下的物体; 缩放物体无法与非缩放物体进行 Batch。

纹理合并 Texture Patching

遮挡剔除 Occlusion Culling

代码方面 #

通过编程的方式提高代码运行性能;

举几个例子

对于有的函数,可以每隔几帧执行一次

1
2
3
4
5
6
7
8
void Update()
{

// 每隔 6 帧执行一次 DoSomething()
if (Time.frameCount % 6 == 0)
{
DoSomething();
}
}

通过使用 InvokeRepeating 函数实现定时重复调用,比如,启动 0.5 秒后每隔 1 秒执行一次 DoSomething 函数:

1
2
3
4
void Start()
{

InvokeRepeating("DoSomething", 0.5f, 1.0f);
}

尽量少使用临时变量,特别是在 Update OnGUI 等实时调用的函数中

不好的案例:

1
2
3
4
5
void Update()
{

Vector3 pos;
pos = transform.position;
}

应该写成:

1
2
3
4
5
private Vector3 pos;
void Update()
{

pos = transform.position;
}

定时进行垃圾回收

1
2
3
4
5
6
7
void Update()
{

if(Time.frameCount % 50 == 0)
{
System.GC.Collection();
}
}

优化数学运算,尽量避免使用 float,而使用 int,特别是在手机游戏中,尽量少用复杂的数学函数,比如 sin、cos 等函数;除法 / 改用成乘法 :使用 x 0.5f 而不是 x / 2.0f;

End