1)Lua全局变量代码规范
2)AssetBundle LockPersistentManager开销
3)Unity内置字体在资源检测报告中不算冗余资源
4)特定Android设备上,Adreno发生冻屏问题
5)Mask和RectMask性能上的区别
这是第238篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。
UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)
Lua
Q:使用Lua语言作为脚本辅助开发已经非常流行了,但是Lua语言中的全局变量是一个令人头疼的问题,因为无需声明就可以使用、编译器编译不会针对重命名和覆盖进行报错,稍不留神就会覆盖掉全局变量导致Bug,而且全局变量引用GameObject有可能会造成泄露。
大家在开发过程中,对于Lua全局变量会制定什么代码规范吗?例如:什么时候可以使用全局变量?如何声明?如何规避覆盖等问题,谢谢。
A:可以在Lua虚拟机启动以后,在适当的时机执行一下luaGlobalCheck.lua文件,这个文件里面会设置一下_G的元表和元方法,通过重写_newindex和 _index元方法的方式来做到禁止新建全局变量和访问不存在的全局变量时提示错误。这样可以做到避免随意新建全局变量污染环境和覆盖的问题。
luaGlobalCheck.lua代码如下:
setmetatable(_G, { -- 控制新建全局变量 __newindex = function(_, k) error("attempt to add a new value to global,key: " .. k, 2) end, -- 控制访问全局变量 __index = function(_, k) error("attempt to index a global value,key: "..k,2) end })
感谢马三小伙儿@UWA问答社区提供了回答
AssetBundle
Q:观察性能曲线,发现某一帧AssetBundle加载中,LockPersistentManager耗时比较大。请问这块是否能够优化?
A1:这说明当前帧或前几帧中存在较大量的资源在通过LoadAsync来进行加载,其本质是所加载的资源过大所致,对自身资源进行合理优化可降低Loading.LockPersistentManager的开销。另外,将异步加载换成同步加载,LockPersistentManager就不会出现了,但其总加载耗时是没有变化的,因为总加载量没变。关于主要资源的加载优化,可参考如下链接:
《Unity加载模块深度解析(纹理篇)》
《Unity加载模块深度解析(网格篇)》
《Unity加载模块深度解析(Shader篇)》
《Unity加载模块深度解析之动画片段》
《移动游戏加载性能和内存管理全解析》
该回答由UWA提供
A2:Unity 2019.4.1版本下,其实是bundle.LoadFromFileAsync在主线程的Integrate Asset中执行,和PreloadManager线程的LoadAssetAsync不能同时进行,必须要锁,也就出现LockPersistentmanager,一直锁到一方结束。本质还是这块实现不完善,可以用Spin Lock一直锁到Application.backgroundLoadingPriority规定的时间再到下一帧就行了,不用一直锁到一方释放。
Unity 2019.4.11和2019.4.16修改了主线程读Bundle和等锁的问题:
我在Unity 2020.1.17版本出的iOS上测试是基本解决了:
但是还有个别异步加载主线程等锁的现象,估计是资源太大收集依赖时间太长触发的:
感谢燃野@UWA问答社区提供了回答
AssetBundle
Q:我们的项目工程中返现两个界面的Prefab,都是用Unity自己的Arial字体生成的Bundle,上传到资源检测,但是在报告中并未看到内置的字体是冗余资源。
A1:这个Arial是属于Unity内置资源,打包APK的时候是会被打进unity default resources里面的,所以AssetBundle中使用到了这个Arial字体都是引用关系,并不会打包进对应的AssetBundle中,因此看不到冗余是正常的。使用AssetStudio打开APK包中的assets/bin/Data/目录下的unity default resources就可以看到了,如下图:
感谢Xuan@UWA问答社区提供了回答
A2:不建议使用Arial字体,在某些低端机上中文无法显示,主要还是默认字体在不同机型上的不一样。
感谢郑骁@UWA问答社区提供了回答
Rendering
Q:特定Android设备上,Adreno发生冻屏(GPU挂起)的问题。
现象是屏幕冻住不刷新了,但是音乐和点击UI的音效还可以播放。用Unity Profiler看CPU也没异常和闪退。冻屏时抓到的错误日志可戳原问答查看,麻烦大家帮看下有什么启发,谢谢!
A1:不确定堆栈是不是跟你一样。之前用Mono包在小米手机上出现过一样的情况,后面改成IL2CPP就没复现了。
感谢剑影蒙残@UWA问答社区提供了回答
A2:楼上这个办法对于部分机型确实有效,不过有些MTK的手机还是会有类似的问题。我们项目也有类似的,花屏或者屏幕画面卡死。感觉像是Unity合批出的问题,动态合批或者是GPU Instance。我们尝试着把单个批次的GPU Instance数量降低会有所缓解,这个数量最好根据手机GPU型号做个适配。
感谢Jony@UWA问答社区提供了回答
A3:使用了Unity官方提供的GPU Instance方案来实现的。
https://github.com/Unity-Technologies/Animation-Instancing开始使用的是这个方案默认的Shader渲染了战斗单位,导致了奇怪的闪退和崩溃,默认的Shader如下:
在这个Shader中的loadMatFromTexture函数调用了9次,loadMatFromTexture的实现如下:
可以确认这里的问题大概率是采样次数太多导致,然后修改了就使用当前帧,不进行Lerp操作,且每个顶点只受两根骨骼的控制,然后测试没有崩溃,目前还正在和美术确认效果损失的接受程度,后来的代码大概是这样:
感谢卢永平@UWA问答社区提供了回答
UGUI
Q:在Unity UI中,Mask和RectMask性能上有什么区别吗?
A1:RectMask2D:不需要依赖一个Image组件,其裁剪区域就是它的RectTransform的rect大小。
性质1:RectMask2D节点下的所有孩子都不能与外界UI节点合批且多个RectMask2D之间不能合批。
性质2:计算Depth的时候,所有的RectMask2D都按一般UI节点看待,只是它没有CanvasRenderer组件,不能看做任何UI控件的bottomUI。
Mask:组件需要依赖一个Image组件,裁剪区域就是Image的大小。
性质1:Mask会在首尾(首=Mask节点,尾=Mask节点下的孩子遍历完后)多出两个Draw Call,多个Mask间如果符合合批条件这两个Draw Call可以对应合批(Mask1的首和 Mask2的首合;Mask1的尾和Mask2的尾合,首尾不能合。)
性质2:计算Depth的时候,当遍历到一个Mask的首,把它当做一个不可合批的UI节点看待,但注意可以作为其孩子UI节点的bottomUI。
性质3:Mask内的UI节点和非Mask外的UI节点不能合批,但多个Mask内的UI节点间如果符合合批条件,可以合批。
从Mask的性质3可以看出,并不是Mask越多越不好,因为Mask间是可以合批的。得出以下结论:
- 当一个界面只有一个Mask,那么RectMask2D优于Mask;
- 当有两个Mask,那么两者差不多;
- 当大于两个Mask,那么Mask优于RectMask2D。
感谢你如暖阳@UWA问答社区提供了回答
A2:从多个Mask合批考虑楼上的结论是可以的,这里补充一下:Mask写Stencil会有额外的OverDraw,Mask2D没OverDraw,但每个Item都要和MaskRect比较,有一定的CPU开销(Mask适用于Item很多的情况,Item少建议Mask2D)。
感谢羽飞@UWA问答社区提供了回答
A3:从UI合批的角度看,在同一层级中:Mask是一个具有高优先级的特殊组件,会优先进行操作。Mask会改变自己和其中UI的Material,两者不同。
RectMask2D是一个裁剪器,没有特殊权限,其合批规则与其它普通组件相同。RectMask2D会改变其中UI的Clip Rect。故不在同一个RectMask2D下的UI组件不能合批。
在UI能否合批的原因中,猜测:
Different Rect Clipping是指从RectMask2D到普通组件,或反之Different Clip Rect是指两个RectMask2D之间切换。
感谢Tao@UWA问答社区提供了回答
今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。
官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)