# 前言
注:该问题可能仅适用于项目,或者公司该项目衍生版本 (该是没有了)
在给对面出了新包之后,由于我方测试都只有低端机,连刘海都没有,因此没有测出该问题。
后续由运营同步过来,显示效果如下:
描述的服务器问题可以忽略,那个是因为后台没开外网测试服导致。
然后我就看了下以前上一个项目组出给对面的白包效果:
这旧版本的刘海更大了!所以还是感觉新版如果把刘海也渲染进去,表现效果会更好一点,稍微查了一下,就得知刘海没有被渲染的原因是因为出包工程 Unity2018.4.36 这个版本没勾 『Render outside safe area』选项导致的,于是勾上重新出包。
# 问题
这时候真的问题就来了:
在我自己的刘海屏 (红米 K50G) 真机安装后,会正常执行初始的 Loading 界面,后续立刻白屏!
表现效果如下:
看着这个表现,最开始怀疑过是 Unity 的问题:因为我拿到项目时,版本虽然是 Unity2018,但并不是 Unity2018 最新版本,后来给它升级到 Unity2018.4.36 了,就猜测是不是 Unity 出啥事了?
项目设置如下:
2018.4 官方说明文档:
如上图所示,查询文档显示 Unity2018 文档上并没有 Render outside safe area 这选项,跟项目实际设置表现并不一致,该选项是否是 Unity2018 后续才更新上去的?导致出现了什么兼容性问题。
不过后边根据表现情况一想,也不对。
毕竟最开始 Loading 都能正常显示出来,那么说明 Render outside safe area 并没有让我们的真机整体的渲染出现问题。
根据发生情况地点:初始 Loading 界面完毕,UI 框架接管之后 —— 问题恐怕只可能出现在我们自己的 UI 框架作适配性的地方。
还有一个信息可以使得 100% 确定是项目的适配而非 Unity 的问题:
—— 可以打开日志界面!
日志界面是可以正常显示的,因此出问题的只可能是项目 AorUISystem 中适配问题。
# 尝试解决
对于项目中适配的地方,根据代码引用一步一步查询,大概可以找出两个可能会比较影响点:
- Main.cs 中 AdapteFullScreen () 判断刘海方法
- AorUIManager.cs 中 ScaleMode->changeScaleMode ()->updateStageSize ()
开始虽然基本可以定位是这两块的问题,但是并没有更详细的头绪。
并猜测:
- 是否是 Stage 缩放大小被错误设置为 0?因为 Stage scale 其实很小,而且根据分辨率不同还可能更小
- 是否由于读取到刘海,Stage 发生了严重的错误偏移?
- 是否是 MainLayer 层级有遮挡?
# 出包测试
为了对上述猜测进验证,且由于是真机、特定机型问题,为了测试,基本上都是改一次代码,出一个包,导致时间消耗更加拉长。
# Injectfix 远程热更测试
后来想着这样可不是办法,于是我把 Injectfix 远程热更调试功能给它整合了进去,然后才变得方便起来。
找到游戏点击屏幕处理方法,直接热更输出需要的详细信息,并操作对猜测进试验:
Loading 结束,进入正式 UI 管理后,其基本结构如下:
尝试使用排除法,操作结果反馈如下:
- 隐藏 MainLayer:无变化
- 隐藏 AorUIStage# 所有子节点:无变化
- 隐藏 AorUIStage#:无变化
- 隐藏 AorUICanvas#:有明显变化,白屏变成黑屏
- 隐藏 AorUICanvas# 所有子节点:同隐藏父类,有明显变化,白屏变成黑屏
- 在 AorUICanvas#、AorUIStage# 子节点动态创建色块,无变化
- 隐藏 AorUICanvas# 所有子节点,创建动态色块,能看到色块显示
# 原因
结合上面一通测试结果,以及输出信息。
可以基本确认,是 AorUICanvas# 下的某个子节点出了问题。
于是尝试:
- 隐藏 AorUICanvas# 除 AorUIStage# 以外的所有子节点,登录界面正常展示!
在 AorUIStage# 层级更高的节点中,ipx 这个节点就比较可疑,再次进行测试,仅隐藏 ipx,登录界面也正常显示,白屏消失。
此时基本可以定位问题原因。
于是查看代码,我很快在 AdapteFullScreen () 方法中发现了这么些处理方法:
if (Application.platform == RuntimePlatform.Android && Screen.width == 2400 && Screen.height == 1080) | |
{ | |
isTest = true;// 关键就这里!!!!!! | |
YKApplication.Instance.IsIphoneX = true; | |
CommonModel.Inst.IsLandscapeLeft = true; | |
mIsFullAdapter = true; | |
} | |
//===== 代码省略 ======= | |
// 编译器测试 | |
if (isTest && null == uiManger.mCanvasT.Find("ipx")) | |
{ | |
// 创建 ipx 组件,该组件模拟 iphone 边框进显示 | |
} |
大家看到了吗?
# EditorOnly!
也就是说,这张图片根本不会打进真机包里 —— 然而代码却在实例化它?!
导致图片丢失,直接整个屏幕就是一块白色覆盖。
问题就是这样:代码执行了不应该真机生效的代码,错误地创建了一个根本不应该创建的东西,导致出现了这么一个很难让人一眼看出问题的问题。
# 总结
问题是解决了,但是花了一天多时间调试。
造成的根本原因是,项目代码里面直接强行判断若『当前为安卓平台,且分辨率为 2400x1080』,就会实例化测试边框,看起来完全是为了测试用 (特别是注释都写了编辑器测试的),且图片又是被标记为 EditorOnly —— 而真机执行,测试边框的图通常情况下根本不会打出去,那就坏了!
注:我拿到项目这张图片就是 EditorOnly,代码也是这个样子,出包工程也并不存在 Resources 中图片的软链接
看注释还写着『编译器测试』,测试代码,如果只是想测试生效,就不应该这样加入正常代码流程,再不济用完也应该注释吧?或者再加个编辑器生效判断也好。
折腾这么大一通,倒也也不算没有收获:为守望项目整合了远程代码热更调试工具用,并详细测了一波热更代码,拿到守望项目代码之后,热更结构我也是做过改动的,因此测试一波也是也有必要。
# 另一种解决方案
但实际上还有另一个方法,要是使用的话,一开始就能很快定位并解决问题:
# FrameDebugger
虽然 Unity2018 的 FrameDebugger 连接真机并不怎么好用,渲染对象跟随真机老是闪来闪去。
不过使用 FrameDebugger 连接真机后,还是可以大概查询渲染目标,可以清晰看出来:原本登录界面是正常渲染了的,只是登录界面上更上一层,被白色的东西直接盖了个彻彻底底。
进而定位渲染没问题,而是覆盖问题导致。
可惜我也是在查出问题后,才想起用这个连接真机试试的,要不然肯定会少许多麻烦了。
除此之外 Renderdoc 应该也可以。