技术分享连载(三十七)

技术分享连载(三十七)

本期话题: AssetBundle Hash值、蒙皮网格渲染优化、Instantiate实例化...精选5个性能优化问题,建议阅读时间15分钟,认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)


资源管理

Q1:打包AssetBundle的时候,我发现切换场景时,即使打同一个场景的AssetBundle,它们的Hash值都是不一样的,可能是什么原因造成的呢?

在不同的场景下打包同一个资源或场景时,如果出现AssetBundle的差异,目前很可能是Shader Stripping造成的,其原理可见文档:https://docs.unity3d.com/Manual/class-GraphicsSettings.html

简单来说就是根据当前场景对Shader进行简化,因此如果打包时包含的场景的Lightmap或Fog设置不同,打出来的AssetBundle包也有可能是不同的。可以尝试通过把Graphics Settings中的Shader Stripping设置进行修改来避免这个问题。


资源管理

Q2:当UI关闭后,Texture图片却还留在内存,是下次垃圾回收或者Resources.UnloadUnusedAssets调用的时候就会清除吗?如果想立即清除,该如何操作?

垃圾回收并不会卸载内存中的资源,而Resources.UnloadUnusedAssets是可以的,但前提是纹理资源已经不再被其他Object引用。如果要立即清除,可以尝试直接调用Resources.UnloadAsset来进行卸载。


资源管理

Q3:我们的游戏中,不透明渲染在总体渲染里占比较高,主要的开销在于 MeshSkinning.Render 部分,这部分的Draw Call过高,共有65个, 请问该如何优化呢?

默认情况下Skinned Mesh是无法合并Draw Call的,从而导致Draw Call过高的问题。 我们建议尝试通过 SkinnedMeshRenderer的BakeMesh接口,将蒙皮动画转为网格序列帧,同时确保顶点属性的数目符合动态拼合的条件,从而降低这部分的Draw Call。另外,这种做法也可以降低MeshSkinning.Update以及Animator.Update的CPU耗时,只是网格序列帧会占用较大的内存,研发团队可以尝试做一个评估。


性能优化

Q4:GameObject.Instantiate()每实例化一个GameObject到场景中,会造成卡顿,有什么办法可以优化吗?就算我采用了异步加载,仍然会有稍许的卡顿感。除了缓存池,是否还有别的方法?

建议研发团队先通过Unity Profiler来确定该性能卡顿的位置。如果只是一个空的GameObject,Instantiate实例化是很快的。一般来说,Instantiate实例化时间较长,主要由以下三个原因:

(1)与资源的加载有关:对于这种情况,研发团度需要精简资源,或者预加载资源来降低实例化的开销;
(2)序列化信息比较多:当GameObject上的Component比较多时,其Instantiate实例化性能会受到影响,比如说粒子系统,这种情况就只能通过分帧实例化,或者通过缓存池来避免;
(3)自定义组件的Awake:在Instantiate实例化时,其GameObject上挂载脚本的Awake函数也会被触发,其中产生的CPU占用,也会被计算在Instantiate实例化内。


资源管理

Q5:预设中的变量,拖拽到Inspector面板和Transform.find这两种方法对加载影响是一样的吗?

对加载性能有微小的不同。Transform.Find 是可以灵活控制调用时机的,可以真正要用的时候再进行Transform.Find,这样GameObject被实例化时效率会更高一些 。但如果拖上去,GameObject被实例化时,该变量就需要进行序列化。因此,加载和实例化时两者的性能会存在一些微小的变化。

今天的分享就到这里。也欢迎热爱进步的你加入UWA的QQ群(793972859),也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。 比起闭门造车,我们更乐意与大家各抒己见,畅所欲言;比起形而上的泛泛而谈,我们更乐意与大家直击痛点,对症下药。