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 间是可以合批的。得出以下结论:

感谢你如暖阳@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(原群已满员)


↙↙↙阅读原文可查看相关链接,并与作者交流