UWA

每天一点UWA:第十三周

Posted by 蔡华的博客 on October 30, 2017

Gameplay

求教一个屏幕后处理的问题。我们期望角色不受屏幕后处理影响,所以目前采用双相机的方案,根据Layer划分,但是这种情况下角色的影子也就没有办法投影到地表上,请问这种需求有什么好的实现方式?

  • 可以尝试根据地表模型的局部细节来动态生成接受阴影的网格(比如Fast Shadow Receiver插件),这样既可以保证地表模型进行屏幕后处理操作,同时也可以生成相关角色的动态阴影。

Texture

UGUI用Resource来动态加载图片,有什么好的方法? 我的理解是:Resource目录下的图片都不能被打包成图集,而且会增加DrawCall和包大小。

  • 需要打图集的Sprite确实不建议放置在Resources下,如果需要动态加载,并且不希望使用AssetBundle,则可以尝试把需要动态加载的Sprite统一引用到Prefab上进行管理(类似于NGUI的管理方式,一个图集对应一个Prefab),然后动态加载Prefab并查找其管理的Sprite即可。
  • 另一种方式是,可以直接关闭Unity的Sprite Packer功能,通过第三方的工具来进行图集的打包,导入Unity时转为Multiple类型的Sprite资源,那么即使放在Resources文件夹下,也不会造成DrawCall无法合并的问题。

纹理图片通过文件流形式加载到内存,这样的资源还可以使用Resources.UnloadUnusedAssets()和Resources.UnloadAsset(m_Asset)进行资源卸载吗?

  • 如果是自己载入内存并初始化为Unity的Texture2D,则还是要重点查看Texture2D对象的创建方式。一般来说,会用new Texture2D的方式创建,并用LoadImage的接口将一块内存载入,那么这样就要用DestroyImmediate来销毁这个纹理对象了。

UI

请问UGUI的重绘是针对于拥有Canvas组件的Canvas物件,还是针对于拥有CanvasRenderer组件的Panel物件?

  • UGUI中,Canvas组件可以认为是容器,Image,Text等组件(都需要CanvasRenderer组件)可以认为是元素。在Unity5.2版本之前,在网格重建时,会以Canvas为单位,即一个Canvas中所有的元素最终都是合并到一个Mesh中的,而其中可以被合并渲染的元素则被合在同一个SubMesh中。
  • 在Unity5.2版本之后由于引入了多线程的网格合并方式,据我们所知,目前并没有官方的原理解释。

工具

对于Animators.DirtySceneObjects这个参数,它是和哪些因素有关,以及如何优化?

image

  • 该参数是更新场景中受Mecanim动画系统影响的每个GameObject的Transform,所以当这类的GameObject数量越多时,其CPU占用也会越高。对于它的优化方式,主要有如下两种:
    • 如果是蒙皮网格物体,则可以开启“Optimize GameObject”选项来对其进行优化;
    • 如果是非蒙皮网格(比如具有动画的UI界面、2D Sprite等),则只能建议研发团队尽可能减少同一时刻运动的GameObject数量(一般都不会太多),如果是被缓存的屏幕外的物体,则切记要在移出时关闭其Animator组件。

编辑器模式下,Prefab用Select Dependencies选项找依赖资源的时候,会把以前旧Shader引用的贴图也给关联上,怎么刷新这种引用关系?

  • 在切换Material所使用的Shader时,其上的纹理引用确实是不会自动清除的(除非被覆盖)。因此,我们的建议是,在编辑Material时,如果要切换Shader,那么在切换好之后,进行一次Reset的操作(如下图所示,该操作会重置所有使用中的属性,同时去掉未使用的属性),然后再开始编辑其属性。

请输入图片描述

  • 如果希望在不改变当前使用属性的前提下,去掉未使用的属性,那么据我们所知,只能启用Editor的Force Text模式,打开对应的.mat文件进行手动去除。

请问怎么优化下图这两者的GC Alloc?每次AddComponent 都会有这么多的开销。

image

  • 不可避免,只有减少AddComponent。

物理

请问Physics.Processing的占用过大一般是因为什么原因导致的?

  • 影响物理系统耗时的因素主要为Contacts数量(碰撞对数量)、Rigidbody API的使用情况和每帧的调用次数。
    • 第一种情况是最为常见的引发物理模块耗时较大的原因,因此,我们在UWA性能报告中对其进行了详细的分析,如果你的报告中Contacts数量较高,切记要验证其合理性。
    • 第二情况造成较大CPU开销的情况不多,不过如果你的项目是多角色游戏(比如MMO、MOBA、ARPG割草游戏等),那么你需要注意了。在我们优化过的一些项目中,通过Rigidbody API来移动GameObject位置(设置velocity、改变center等)确实会存在较高的性能开销。如果你的项目也有类似的做法,那么要时刻关注物理模块的开销了。
    • 第三种情况同样也是目前引发物理模块耗时较高的原因。因为Unity引擎默认情况下,物理的更新频率是0.02s,即每20ms更新一次,所以,当你的项目比较卡时(开发过程中的项目在中低端设备上恐怕没几个是不卡的),物理模块会让你的项目更卡。举个例子,如果上一帧CPU耗时为100ms的话,那么物理模块会执行5次,从而进一步加大物理系统的耗时。这种情况下,物理模块的耗时是很有欺骗性的,你花了好长时间去研究物理的耗时,最后发现原来这个“锅”不是它的…所以,如果你的项目也遇到了这种情况,切记不要再上当了。

性能综合

5.0后批处理修改

  • 从Unity 5.0开始,Static Batching的合批机制就已经出现了变化,不再进行索引数组的合批,因此并不会使得Draw Call降低,而是会降低Batches和SetPassCall,因此从图中来看,Static Batching 开启后的统计数据是没有问题的。也因此,UWA在统计时,使用的就是Batches的数值。具体的原因可见Unity官方在论坛中的回复: https://forum.unity3d.com/threads/regression-feature-not-bug-static-dynamic-batching-combining-v-buffers-but-not-draw-calls.360143/

尽量开启“Dynamic Batching”这个选项

image

匿名函数造成GCAlloc

  • 结论:当不使用外部变量的匿名函数时,编译器会把这个函数变成静态函数,在首次调用时初始化,之后就再也不会new新的对象。 当使用外部变量时,每次调用都会生成一个临时action变量,这个就是alloc的原因。
  • 解决方案:
public Action<int, int> pCall;
void Start()
{
  pCall = CallVariable;
  ... // 其他初始化代码
}
void FixedCall()
{
  table.Forecah(pCall);
}
void CallVariable(int k, int v)
{
  count = k + v;
}

渲染

请教Lightmap相关的优化问题。我现在的场景中有N个GameObject共用一个Prefab,烘培Lightmap时,会生成出N个Lightmap贴图。有没有办法把这些生成的Lightmap合成一张?

  • 如果场景中某些物件在Lightmap纹理中占据的空间较大,但实际上并不需要较高的精度,那么可以选中该物体,并在Lighting面板的Object子面板中将其Scale in lightmap数值调低,从而可以降低其在Lightmap纹理中的空间,甚至减少Lightmap纹理的数量。
  • PS:为何会有N个lightmap?并没有回答能不能合并的问题。

Unity里的Shader能不能用关键字#ifdef #endif把整个pass包起来?

  • #ifdef#endif 并不能写在Pass之外,如果有动态开关Pass的需求,可以通过Shader Lod来实现,即设置两个Level不同的SubShader分别包含1个和2个Pass,直接通过改变该Shader的局部Lod值,即可实现SubShader的切换。关于Shader Lod的细节,可见官方文档: http://docs.unity3d.com/Manual/SL-ShaderLOD.html