# 前言

注:该问题可能仅适用于项目,或者公司该项目衍生版本 (该是没有了)

在给对面出了新包之后,由于我方测试都只有低端机,连刘海都没有,因此没有测出该问题。

后续由运营同步过来,显示效果如下:

描述的服务器问题可以忽略,那个是因为后台没开外网测试服导致。

然后我就看了下以前上一个项目组出给对面的白包效果:

这旧版本的刘海更大了!所以还是感觉新版如果把刘海也渲染进去,表现效果会更好一点,稍微查了一下,就得知刘海没有被渲染的原因是因为出包工程 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 应该也可以。