【博观约取】《艾尔战记》案例精讲
继上期《六龙争霸》性能测评精讲, 本期我们为大家带来由昆仑游戏研发的3D横版动作手游《艾尔战记》的性能测评分析。该游戏上线即获得苹果官方推荐,并领跑免费、畅销双榜,证明其作为一款优秀游戏的实力毋庸置疑。今天,我们就来对该款游戏的性能进行深度剖析。
CPU性能
该游戏在CPU占用方面的性能较为不错,下图为该游戏在 小米4 和 三星S6 上的CPU占用情况,可以看出,在小米4上运行的14920帧中,超过33ms的帧数占比为23.8%,超过50ms的帧数占比为8.6%;在三星S6上运行的20982帧中,超过33ms的帧数占比为5.7%,超过50ms的帧数占比为2.6%。
小米4:
三星S6:
下面,我们就详细讲解一下该游戏的各个模块在CPU性能方面的具体表现情况。
1. 渲染模块
该游戏在小米4设备上运行时的渲染模块CPU开销如下图所示。通过统计,半透明物体渲染的CPU消耗均值为 6.0 ms,主要集中在 2.2~11.6ms 范围内(5%~95%)。不透明物体渲染的CPU消耗均值为 4.6 ms,主要集中在 0.3~8.0ms 范围内(5%~95%)。Draw Call峰值为 222,且主要集中在 36~153 范围内(5%~95%),该值处于合理范围之内。
从上述分析可以看出,该游戏在类似小米4这样的中端设备上很好地控制渲染模块的性能开销。对此,我们进行了进一步分析,如果该游戏可以在以下几点进一步优化,那么将会达到更棒的运行性能。
(1)蒙皮网格渲染
通过测评报告进行分析,我们发现蒙皮网格数量与Draw Call、Triangles和不透物体渲染耗时的走势较为一致,如下图红框中所示。这说明蒙皮网格的渲染开销占据了不透明物体渲染的绝大部分耗时。同时,可以看出,战斗副本中当蒙皮网格有变化时,Draw Call和Triangle并存在明显变化,如蓝框所示,这说明副本中的所用的蒙皮网格所包含的面片数较少,蒙皮网格数量与Draw Call增加数量几乎为1:1。而红框为主场景中,蒙皮网格与Draw Call和Triangle之间的关系。可以看出,虽然蒙皮网格的变化极大地影响了后两者的变化,且蒙皮网格数量与Draw Call在1:5~1:10之间。我们推测这其中的变化是由角色、NPC等造成的,且每个角色均具有较多的面片数和SubMesh(换装)数量。
同时,我们进一步研究了蒙皮网格渲染在小米4设备上CPU端的具体开销。下图中紫色线条表示不透明蒙皮网格的渲染开销,黄色线条表透明蒙皮网格的渲染开销。从而可以看出,场景中蒙皮网格大多为不透明材质模型,且CPU占用较高。
那么,到底是哪些蒙皮网格导致了较高的CPU占用呢?为此,我们在 具体资源信息 中查找相关的蒙皮网格,其中 含有三角形面片数量Top10 的蒙皮网格使用情况如下表所示。对此,我们建议研发团队可以进一步对三角面片较多的蒙皮网格进行进一步的简化。
相信不少团队也遇到了蒙皮网格面片数较高的情况。对此,我们建议如下:
对于网格简化,可以尝试使用Asset Store中的SimpleLOD插件;
对于降低蒙皮网格的Draw Call,可以尝试使用Asset Store中的MeshBaker插件。
(2)粒子系统渲染
粒子系统的渲染同样占据了较高的性能开销,下图为红米2设备上粒子系统渲染在CPU端的占用情况。
对于这种情况,我们的建议如下:
在低端设备上尽可能降低粒子系统的复杂程度,可对粒子系统根据设备的档次来进行分级,从而降低中低端设备上的开销;
对于5.x项目,可尝试升级到5.3以后版本,因为Unity在5.3版本后对粒子系统底层进行了深入的优化。
2. 物理模块
该游戏在小米4设备上运行时的物理模块CPU开销如下图所示。通过统计,物理模块总体的CPU占用均值为0.2 ms,主要集中在0.1~0.3ms范围内(5%~95%),该值处于合理范围之内(一般建议在3ms以下)。
物理模块的低效能开销主要得益于Contacts和Rigidbody数量控制得当。在《艾尔战记》游戏中,Rigidbody和Contacts数量一直为0,即说明游戏中并未用到任何物理碰撞,同时也说明研发团队对Physics Matrix进行了相当合理的设置,避免不必要的碰撞检测发生。
重要提示:不少项目中,Contacts数量较高的原因是由于UI的不当使用造成的,即UI widgets摆在同一深度并存在相互叠加的情况,从而形成了较多不必要的Contacts,进而造成了不必要的物理碰撞开销。建议大家通过UWA对物理模块中Contacts的数量进行详细地检测。
3. 动画模块
该游戏在小米4设备上运行时的动画模块CPU开销如下图所示。通过统计, 蒙皮网格每帧Update更新的CPU占用均值为1.7 ms,主要集中在0.1~2.7ms范围内(5%~95%),该值处于合理范围之内(一般建议在3ms以下)。
重要提示:对于使用Mecanim动画系统的团队来说,建议尽量开启动画系统的“Optimize GameObject”选项,从而来得到更为高效的动画性能。
内存模块
《艾尔战记》在内存上的表现非常优异,如下图所示。总内存峰值为188MB,且绝大部分时间保持在165MB以下。Mono堆内存峰值为32.1MB,且内存的升降较为一致。
1. Mono堆内存
从下图可知,该游戏的总体Mono堆内存控制得很好,在14920帧中,Mono的堆内存峰值仅为 32.1MB。该值属于合理范围之内(<40MB)。
2. 资源内存
经过统计,该游戏的纹理资源数量峰值为324个,内存占用峰值63.3MB。在全部纹理资源中,ETC1格式纹理占有175个,ARGB32和RGBA32格式资源数共有99个,其余为RGBA16格式。
项目中ARGB32和RGBA32格式的纹理数量较多,我们建议在视觉效果可以保证的情况下,尽可能使用ETC1格式纹理进行替换,从而达到更小的内存占用。
3. 其他资源的内存占用情况如下:
Mesh资源:
AnimationClip资源:
AudioClip资源:
以上则为《艾尔战记》游戏在CPU性能和内存管理方面的具体使用情况。优秀的CPU性能、较低的内存分配,足以说明该研发团队具备非常深厚的技术功底和对于引擎相当优秀的把控能力。
同时,该游戏在资源加载和实例化方面仍有一定的提升空间。在此,我们对其进行罗列,希望同样可以帮助到大家的研发项目。
性能优化、进无止境
1. Instantiate实例化
目前,游戏的战斗副本中Instantiate实例化的频率较高,如下图所示。较高频次的Instantiate/Destroy操作会造成一定的内存碎片,从而造成GC的加速到来。
我们对游戏的Instantiate调用进行进一步分析,下图则为Instantiate的具体分配情况。通过此图,Instantiate的调用位置、具体耗时、频率等,大家一目了然!
对于频繁的Instantiate调用,我们一般建议如下:
对于一般的GameObject(比如技能特效、怪物角色等),可将其放入缓存池并通过Active/Deactive来进行切换;
对于使用频率较高的UI界面,则可通过直接改变Transform的方式来移进移出相机视域体,可以得到更加高效的性能。
2. GC调用
在测试过程中,GC的调用频率较高。游戏在小米4上运行14000+帧时,共检测到GC调用45次,平均331帧/次。
GC的调用频率主要由代码的堆内存分配情况相关。下图则为该游戏运行过程中,堆内存分配Top10的函数列表。正是这些函数分配的大量堆内存,才造成了较高频次的GC调用。
注意:建议大家一定要多关注代码的累积堆内存分配情况,因为它基本上决定了GC的调用频率。如果你的代码中存在分配10MB以上堆内存的函数,切记一定要详细检测,检测其堆内存分配是否合理。
3. 动画资源冗余
在测试过程中,我们发现该项目的动画资源在使用上存在一定的冗余现象,如下图所示。图中红框内的数量峰值即表示该资源在测试过程中存在不同程度的冗余情况。点击资源,图表中即可显示该资源在项目测试阶段的具体使用情况,从而大家可以直接通过该曲线来定位资源出现冗余时的具体位置。
注意:一般情况下,这种情况是由AssetBundle没有进行完全依赖性打包所致。当你的项目出现资源冗余情况时,建议对AssetBundle的打包机制进行进一步的梳理,定位问题并完善相关的资源打包步骤。在此,我们建议开发者在UWA上进行资源检测,该工具能高效地协助解决资源依赖性打包和冗余的问题。
4. 粒子系统
在测试过程中,粒子系统的更新在中低端设备上占有较高的CPU开销。下图为三星S3设备上的粒子系统CPU占用情况。可以看出,在战斗副本中的粒子系统占用较低,但在主场景中的的粒子系统开销较高,这种情况较为不合理。
对于这种情况,我们的建议如下:
尝试关闭离当前视域体或当前相机较远的粒子系统,离近后再进行开启,从而避免不必要的粒子系统Update开销;
根据机型的不同档次设定粒子系统不同的复杂程度,以降低粒子系统在中低端手机上的开销。
以上则为该项目在后续研发中可进一步提升性能的主要方面。在我们测评过的项目中,实例化频率过大,GC调用频率过高、资源冗余等问题也是绝大多数研发团队遇到的共性问题。希望以上的讲解对大家的相关问题有所帮助。
最后,非常感谢《艾尔战记》研发团队对 UWA 的认可和支持。感谢他们乐于将项目性能数据与大家一起分享,让更多的研发团队了解到一款性能优秀的动作手游在各个模块上应该做到怎样的程度。同时,也希望更多的开发团队可以与我们一起来分享他们的性能数据,让更多的游戏开发者受益!