大家好,我叫Jack冯; 本人20年硕士毕业于广东工业大学,于2020年6月加入37手游安卓团队;目前主要负责海外游戏发行安卓相关开发。
一、背景
二、分析及解决
1、生命周期分析
(1)黑屏情况
(2)解决方法
(3)正常显示
2、涉及方法解析
(1)onWindowFocusChanged (boolean hasFocus)
(2)Android生命周期
(3)对比Android原生工程
(4)unity脚本生命周期
(5)分析脚本生命周期
三、结论
在Unity游戏工程中,经常遇到这样的问题:打开登录弹框时,点击Home键先处理其他事宜再返回,发现屏幕黑屏;或者打开了其他接受输入焦点的对话框或弹出窗口,点击返回键时发生屏幕黑屏,需要触摸屏幕(获得焦点)才能正常显示。
具体情形见下图:
其中,生命周期顺序如下:
在游戏主活动UnityPlayerActivity中,重写onStart()方法,添加获取焦点的方法,可避免黑屏。
@Override
protected void onStart() {
super.onStart();
this.mUnityPlayer.resume();
onWindowFocusChanged(true);
}
其中,生命周期顺序如下:
由上可见,二者生命周期的异同在于,是否在调用onStart后调用一次onWindowFocusChanged:true,来获取当前窗口的焦点,实现正常交互。
当activity的当前窗口获得或失去焦点时调用,hasFocus == true表示当前窗口获得焦点,false则表示失去焦点。用法:
onWindowFocusChanged (true);
onWindowFocusChanged方法提供了有关全局焦点状态的信息,该状态独立于活动生命周期进行管理。因此,虽然焦点更改通常与生命周期更改有某种关系(停止的活动通常不会获得窗口焦点),但您不应依赖此处回调与其他生命周期方法(如onResume()中的回调)之间的特定顺序。
但是,一般来说,前台活动具有窗口焦点。除非它显示了其他接受输入焦点的对话框或弹出窗口,在这种情况下,当其他窗口有焦点时,活动本身就没有焦点。同样,系统可能会显示系统级窗口(例如状态栏通知面板或系统警报),这些窗口将暂时获得窗口输入焦点,而不会暂停前台活动。
从Android Q开始,在多窗口模式下,可以同时有多个恢复的活动,因此即使上面没有覆盖,恢复状态也不能保证窗口焦点。如果目的是要知道一个活动何时是最活跃的,即用户在所有活动中与之交互的最后一个活动,但不包括非活动窗口(如对话框和弹出窗口),则应使用OnTopheMedActivityChanged(Boolean value)。
图为原生工程的AndroidDemo。对比UnityDemo,生命周期方法执行虽一致、焦点丢失情况则不相同。
为了进一步对比,下面引入unity脚本的常见生命周期方法。
unity脚本的常见生命周期方法如下:
-- Awake:始终在任何 Start 函数之前并在实例化组件之后调用此函数。(如果游戏对象在启动期间处于非活动状态,则在激活之后才会调用 Awake。) -- OnEnable:(仅在对象处于激活状态时调用)在启用对象后立即调用此函数。在创建 MonoBehaviour 实例时(例如加载关卡或实例化具有脚本组件的游戏对象时)会执行此调用。 -- OnLevelWasLoaded:场景全部加载完成后 -- Start:仅当启用脚本实例后,才会在第一次帧更新之前调用 Start。
-- FixedUpdate:调用 FixedUpdate 的频度常常超过 Update。如果帧率很低,可以每帧调用该函数多次;如果帧率很高,可能在帧之间完全不调用该函数。 -- Update:每帧调用一次 Update。这是用于帧更新的主要函数。 -- LateUpdate:每帧调用一次 LateUpdate__(在 Update__ 完成后)。 -- OnGUI:每帧调用多次以响应 GUI 事件。首先处理布局和重新绘制事件,然后为每个输入事件处理布局和键盘/鼠标事件。
-- OnApplicationPause:一帧最后时调用,调用后会再触发一帧以刷新图像和切换暂停状态 -- OnApplicationQuit:在退出应用程序之前在所有游戏对象上调用此函数。在编辑器中,用户停止播放模式时,调用函数。
-- OnDisable:行为被禁用或处于非活动状态时,调用此函数。 -- OnDestroy:对象存在的最后一帧完成所有帧更新之后,调用此函数(可能应 Object.Destroy 要求或在场景关闭时销毁该对象)。
这里将生命周期方法在UnityDemo中打印出来,主要对比黑屏情况下的生命周期情况。
具体日志如下:
//android生命周期
E/UnityPlayerActivity: onCreate
E/UnityPlayerActivity: onStart
E/UnityPlayerActivity: onResume
E/UnityPlayerActivity: onWindowFocusChanged:true
//unity脚本生命周期
I/Unity: UnityPlayerActivity Awake
...
I/Unity: UnityPlayerActivity OnApplicationPause:False
I/Unity: UnityPlayerActivity OnApplicationFocus:True
I/Unity: UnityPlayerActivity start
//点击home键
E/UnityPlayerActivity: onWindowFocusChanged:false
I/Unity: UnityPlayerActivity OnApplicationFocus:False
E/UnityPlayerActivity: onPause
I/Unity: UnityPlayerActivity OnApplicationPause:True
...
//再次返回app
E/UnityPlayerActivity: onRestart
E/UnityPlayerActivity: onStart
E/UnityPlayerActivity: onResume
//***注意***,这里unity脚本生命周期方法并没有继续执行!
综合分析,从桌面返回游戏App时,由于unity丢失焦点(I/Unity: UnityPlayerActivity OnApplicationFocus:False ),脚本没有执行,即无法渲染游戏画面对象,致使黑屏。
如果根据第二点添加获取焦点方法后,由下图可以看到继续执行的unity脚本生命周期方法,先获取到焦点、中止pause状态并绘制页面进行正常显示。即工程重新获取焦点后才会绘制图像。
I/Unity: UnityPlayerActivity OnApplicationFocus:True
I/Unity: UnityPlayerActivity OnApplicationPause:False
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。