移动开发优化

正如 PC 一样,iOS 和安卓 (Android) 等移动平台也存在各种性能级别的设备。您可以轻松找到一部渲染效果比其他手机好 10 倍的手机。 缩放方法非常简单:

确保它在基准配置上正常运行

在高性能配置上使用更多特效: ?分辨率

后处理

多重采样抗锯齿

各向异性

着色器

特效/粒子密度,打开/关闭

注重 GPU

图形性能与填充率、分辨率和几何体复杂度(顶点数)息息相关。如果找到方法剔除更多渲染,那么这三个要素的值都会降低。此时,遮挡剔除可起到有效作用。Unity 会自动剔除视锥外的对象。

在移动平台上,基本上会受到填充率的约束(填充率 = 屏幕像素 * 着色器复杂度 * 透支度),着色器过于复杂是导致问题出现的最常见原因。因此,请使用 Unity 自带的移动着色器或自己设计,但要尽可能简单。如果可行,请将代码移动到顶点着色器中,简化像素着色器。

如果降低质量设置 (Quality Settings) 中的纹理质量 (Texture Quality) 后游戏运行速度加快,那么很可能是受内存带宽所限。此时可压缩纹理、使用贴图细化,降低纹理大小等。

LOD(细节等级)使对象更简单,或移向远方后完全消除。主要目的为减少绘制调用的次数。

出色实践

移动 GPU 对于自身产生多少热量、使用的电量以及尺寸大小或噪音大小有严格限制。与台式机部件相比,移动 GPU 的带宽较小、运算器 (ALU) 性能和纹理功率也较低。GPU 的架构也进行了调整,以尽量使用最小带宽和功率。

Unity 针对 OpenGL ES 2.0 进行了优化,使用 GLSL ES(类似于高级着色语言 HLSL)着色语言。内置着色器大多使用高级着色器语言(即 HLSL,也称之为 Cg)编写。针对移动平台,其被交叉编译成 GLSL ES。如果您想要,也可以直接编写 GLSL,但这样会限制您访问 OpenGL 平台(如移动 + Mac),因为目前还没有 GLSL->HLSL 转换工具。在 HLSL 中使用浮点/半浮点/固定 (float/half/fixed) 类型时,它们在 GLSL ES 中会以 highp/mediump/lowp 精度限定符结尾。

以下列出了一些出色实践:

尽量使材质数目减至最低,便于 Unity 轻松进行批处理。

使用纹理精灵(大贴图包含多个子贴图),而非许多单个纹理。使加载速度更快,状态转换更少,批处理简单方便。

如果使用纹理精灵和共享材质,请用 Renderer.sharedMaterial 代替 Renderer.material。

向前渲染像素灯比较耗性能。 ?尽量使用光照贴图代替实时灯光。

?在质量设置中调整像素灯数目。基本上只有方向灯为逐像素,其他都为逐顶点。当然,这取决于游戏本身。

在质量设置 (Quality Settings) 中尝试调整灯光的渲染模式 (Render Mode of Lights),以获得正确优先级。

避免使用抠图(透明度测试)着色器,除非真正有必要。

将透明(alpha 混合)屏幕的覆盖范围保持在最小。

努力避免多个灯光照亮任何给定对象的情况。

努力降低着色器通道(阴影、像素灯、反射)的总数目。

渲染顺序很关键。一般情况下: 从前往后完全不透明的对象。

大致从前往后经 alpha 测试的对象。

天空盒。

alpha 混合对象(若需要,从后往前)。

在移动平台上进行后处理性能消耗大,请谨慎使用。

粒子:减少透支,尽量使用最简单的着色器。

每一帧都要修改的网格使用双缓存:


void Update (){
  // flip between meshes
  bufferMesh = on ? meshA : meshB;
  on = !on;
  bufferMesh.vertices = vertices; // modification to mesh
  meshFilter.sharedMesh = bufferMesh;
}


着色器优化

检查填充率绑定是否容易:如果降低显示分辨率,游戏运行速度是否加快?若是,即受到了填充率的限制。

尝试使用以下方法降低着色器的复杂度:

避免使用alpha 测试着色器,而是采用 alpha 混合版本。

使用简单、已优化的着色器代码(如 Unity 附带的“移动 (Mobile)”着色器)。

避免在着色器代码中使用性能消耗大的数学函数(如指数表达式、指数、对数、余弦、正弦、正切等)。考虑使用预计算的查找纹理代替。

为实现最佳性能,尽可能选择最低数值的精度格式(Cg 中的浮点、半浮点和固定 (float, half, fixed))。

注重 CPU

游戏受到 GPU 像素处理限制的情况时有发生。导致最终出现 CPU 未充分使用的情况,多核移动 CPU 尤其如此。将 GPU 中的一些工作移出来,放到 CPU 中处理往往是比较明智的做法(Unity 处理所有这些操作):网格蒙皮、小对象的批处理、粒子几何体更新。

慎用,不要盲目地全放到 CPU 中处理。如果不受绘制调用限制,那么随着剔除效率的降低和更多对象受到灯光影响,批处理的实际性能会变差!

出色实践

在移动设备上每帧的绘制调用不要超过数百次。

FindObjectsOfType(以及 Unity 的一般 getter 属性)非常慢,慎用。

设置非移动对象的静态 (Static) 属性,允许静态批处理等内部优化。

消耗大量 CPU 周期进行遮挡剔除和更好的排序(利用 Early Z-cull)。

物理 (Physics)

物理 (Physics) 占用 CPU 的比重较大。可以通过编辑器 (Editor) 分析器对其进行分析。如果物理似乎占用了太多 CPU 时间,请:

将 Time.fixedDeltaTime(位于工程设置 (Project settings)-> 时间 (Time))调至可接受的最高值。如果游戏运行缓慢,需要的固定更新次数很可能比快动作游戏少。快节奏游戏须更频繁地计算,这样一来,fixedDeltaTime 就要降低,否则碰撞可能会失败。

Physics.solverIterationCount(物理管理器 (Physics Manager))。

尽量少使用布 (Cloth) 对象。

必要时才使用刚体 (Rigidbody)。

在首选网格碰撞器中使用原始碰撞器。

永远不要移动静态碰撞器(如不带刚体的碰撞器),否则可能对性能造成很大影响。 ?在分析器 (Profiler) 中显示为“Static Collider.Move”,但真正的处理过程是在 Physics.Simulate 中进行。

如有必要,添加一个刚体 (RigidBody) 并将 isKinematic 设为 true。

在 Windows 系统中,必要时可以使用 NVidia’s AgPerfMon 分析工具包获取更多细节信息。

Unity3D技术之移动开发优化详解 - 第1张 | 游戏开发网-最好的游戏编程开发技术网站! Android

GPU

以下是一些流行的移动平台体系架构。硬件供应商与 PC/控制台空间不同,GPU 架构与“常用” GPU 相比也有很大差异。

ImgTec PowerVR SGX – 基于平铺延迟:在小单元内渲染所有内容(如 16×16),只为可见像素着色。

NVIDIA Tegra – 经典:渲染所有内容。

Qualcomm Adreno – 平铺:在单元内渲染所有内容,在大单元内进行设计(如 256k)。Adreno 3xx 可切换到传统模式。

ARM Mali 平铺:在单元内渲染所有内容,在小单元内进行设计(如 16×16)。

抽出一些时间了解不同渲染方法,并相应设计游戏。特别注意排序问题。在开发周期前期定义支持的最低终端设备。设计游戏时开启分析器对其进行测试。

使用针对特定平台的纹理压缩。

屏幕分辨率

安卓 (Android) 版本

Unity3D技术之移动开发优化详解 - 第2张 | 游戏开发网-最好的游戏编程开发技术网站! iOS

GPU

只考虑 PowerVR 架构(基于平铺延迟)。

ImgTec PowerVR SGX。基于平铺延迟:渲染单元内所有内容,只对可见的像素着色。

ImgTec .PowerVR MBX。基于平铺延迟,固定函数 – iPhone 4/iPad 1 之前的设备。

这意味着:

贴图细化并非必须。

反锯齿和反向异性耗费的性能很少,有些情况下 iPad 3 上并不需要。

缺点:

如果每帧的顶点数据(顶点数 * 顶点着色器之后所需的存储空间)超过驱动器分配的内部缓存,那么场景须“分开”,这会耗费性能。之后,驱动器可能会分配更大缓存,或者您可能需要减少顶点数目。非常复杂的着色器上约有 10 万个顶点时,在 iPad2 (iOS 4.3) 上会趋于明显。

TBDR 须为平铺和延迟部分分配更多晶体管,理论上留给“原始性能 (raw performance)”的晶体管就较少。在 TBDR 上获得绘制调用的 GPU 时间非常难(几乎不可行),使分析更加困难。

屏幕分辨率

iOS 版本

动态对象

资源包

资源包可以在一定限度内保存缓存到设备上。

用编辑器 (Editor) API 进行创建

加载 ?使用 WWW API

资源:AssetBundle.CreateFromMemory or AssetBundle.CreateFromFile

卸载 ?AssetBundle.Unload ?有一个选项供卸载资源包,但会隔开已加载的资源。

即使是在场景中引用,也会关闭所有已加载资源。

Resources.UnloadUnusedAssets ?卸载场景中不再引用的所有资源。记住关闭不再需要的资源引用。

公共变量和静态变量永远不能回收利用。

Resources.UnloadAsset ?从内存中卸载一个特定资源。需要时可以从磁盘中重新加载。

iOS 上对同时下载资源包的数目有限制吗?(例如能否同时(或每个帧)安全下载 10 个资源包)?

通过 OS 提供的不同步 API 实施下载,所以 OS 决定需要创建多少线程以供下载。多个并行下载时应注意可以支持的设备总带宽和空余内存百分比。每个并行下载分配自己的临时缓存,应注意这些缓存未占用完内存。

资源

资源需要被 Unity 识别,以放入发布版中。

将 .bytes 文件扩展作为二进制数据添加到想要 Unity 识别的任何原始字节。

将 .txt 文件扩展作为文本资源添加到想要 Unity 识别的任何文本文件中。

发布时资源会转成平台适用的格式。

Resources.Load()

低级错误列表

纹理未经过适当压缩 ?不同情况下使用不同的分辨率,除非肯定不要压缩,否则一律压缩纹理

ETC/RGBA16 – 安卓 (android) 默认设置 ?可根据 GPU 供应商进行调整

可行时使用 ETC 是最佳方法

alpha 纹理可以使用两个 ETC 文件,其中一个通道用于 alpha

PVRTC – iOS 默认设置 ?适用于大多数情况

纹理启用了 Get/Set pixels – 加倍封装,除非需要 Get/Set,否则不要勾选

运行时从 JPEG/PNG 中加载的纹理不压缩

大 mp3 文件在加载时标记为解压

附加场景加载

未使用的资源在内存中保留为未清理 ?静态区域

非卸载的资源包

如果偶然崩溃,尝试软件开发工具包或带 2 GB 内存的设备(如 Ipad 3)。

有时控制台中未出现任何问题,仅为偶然性崩溃。


快速脚本调用和剥离可能导致在 iOS 上偶然崩溃。试试没有这两项会如何。