技术分享连载(十八)

技术分享连载(十八)

【技术分享】是UWA推出的技术交流栏目,我们会定期将开发团队中反馈的常见问题加以总结并梳理在此,以供大家参考。

这是侑虎科技第54篇原创文章,欢迎转发分享,未经作者授权请勿转载。同时如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)


图形渲染

Q1:我看到Unity 5.3.5版本中恢复了对粒子系统的合批功能,但是我尝试下来并没有达到这个效果。是粒子系统合批有什么要求吗,实例化会不会对粒子系统的合批造成影响呢?
1.png

经过我们验证,Unity 5.3.5 版本中确实恢复了对粒子系统的动态合批功能(可尝试添加多个默认的粒子系统,观察Batches的数量变化)。同时我们并未发现实例化会对粒子系统的合批产生影响。

需要注意的是,粒子系统合批的前提是渲染顺序上相邻,且材质相同。默认情况下,粒子系统的渲染与一般的半透明渲染一样,必须从后向前渲染。通常一个特效可能由多个不同材质的粒子系统组成,因此在大多数情况下,当屏幕上出现多个相同的特效时,由于粒子系统的排序问题并不会生成合批。所以,目前只能通过人为指定粒子系统的渲染顺序来绕过该问题,具体做法可见该博客地址:
http://blog.csdn.net/github_32062421/article/details/49203501
但该做法的缺点也显而易见,当两个特效部分重叠时,粒子系统的排序是不正确的(会出现后方的粒子系统遮挡前方的粒子系统的现象),因此也只能酌情采用。


资源管理

Q2:我用的是Resource的加载方式,并且已经预加载好了材质所在的Prefab ,但是为什么在第一次显示材质的时候还要Load这个材质?

通过 Resources 加载和通过 AssetBundle 加载是有所区别的。Resources.Load 和 Instantiate 操作都不会立刻加载其依赖的材质,因此在loadPrefab = Resources.Load之后,其依赖的材质(包括相关的 Shader 和纹理)实际并没有被加载到内存中,在实例化后也是一样,直到某个 Camera 需要对其进行渲染时(调用了 Material.SetPastFast),才发现该材质还没进内存,此时才开始进行加载。

因此,在使用 Resources 加载的情况下,如果希望提前加载该 Material 以及相关的 Shader 和纹理,可以尝试通过调用 Resources.Load 直接加载该材质和纹理,并通过 Shader.Find 来加载 Shader。也可以尝试通过 Player Settings 中的 Preload Shaders(配合 Graphics Settings 下的 Preloaded Shaders) 和 Preloaded Assets 来批量预加载。


资源管理

Q3:我们的游戏使用 Spine 插件,因为要用到裁切动画,所以修改了Shader,但在使用的时候出现异常: Shader wants normals, but the mesh Skeleton Mesh doesn't have them,可能是什么原因?

开发团队需要注意:Surface Shader在生成代码中默认会处理normal(即 Spine/Skeleton 实际上是需要 normal 的),而对应的Mesh并没有包含normal,所以在预览窗口里渲染的时候会检查 Mesh 是否包含 normal 信息,没有的话会报这个错误。

开发团队可以尝试编写一个Vertex & Fragment Shader 从而避免处理normal,也可以尝试创建带normal的Mesh,来避免该问题。


资源管理

Q4:我在第一次执行GameObject.Instantiate一些资源的时候会卡(当时加载当时就实例的情况),有的复杂资源甚至在第一次GameObject.Instantiate的时候会卡70多毫秒,造成明显的卡顿,请问有什么好的解决方案吗?

Instantiate的卡顿与三部分开销相关:相关资源加载、脚本组件的序列化和构造函数的执行,并且绝大部分原因均是相关资源加载导致。所以,我们的建议如下:

1、通过 Profiler 查看 Instantiate 具体的CPU分配情况;
2、如果是资源加载导致的性能瓶颈,则一方面通过简化资源来缓解CPU耗时压力,另一方面通过 AssetBundle 依赖关系打包将资源预先加载,即将此处 Instantiate 的总体耗时拆分,平摊到之前帧进行执行(比如切换场景处等),从而让 Instantiate 实例化操作的局部耗时更加平滑;
3、如果是脚本组件序列化导致的性能瓶颈,则可尝试减少脚本中的序列化信息;
4、如果是构造函数的执行导致的性能瓶颈,一般只能在策略上进行规避,比如降低 Instantiate 的调用频率等。

针对资源加载部分,我们近期正以专题的形式连载中,后续会对其Instantiate实例化的调用进行更为详细的分析,敬请期待。


资源管理

Q5:同一个纹理,有多个Prefab生成的实例,会有多份这个纹理的copy吗?

该问题需要查看 Prefab 的具体加载方式。如果仅是通过 Resources.Load 进行加载,那么纹理是不会存在多份的,但如果是通过AssetBundle加载,每个 Prefab 均为一个 AssetBundle 且纹理没有进行依赖关系打包的话,那么纹理资源确实会在内存中存在多份。如果你发现了内存中存在多份相同纹理,且是通过 AssetBundle 文件来加载资源的,则建议将 AssetBundle 上传到UWA网站(www.uwa4d.com)中,其资源检测工具能协助开发团队高效检测并定位AssetBundle中的冗余资源。