- Home /
Render 1 more frame after OnApplicationPause on iOS
On iOS it seems that after applicationWillResignActive is received and then calls OnApplicationPause all rendering stops. Is there a way to render one more frame after the OnApplicationPause is called?
As far as I can tell the order of operations is:
Render Frame
Process OnApplicationPause(true)
Pause Execution until applicationDidBecomeActive
Process OnApplicationPause(false)
Render Frame
I'm making a puzzle game with a time based component so I need some way to hide game elements when the iOS notification bar is opened (since that triggers applicationWillResignActive). Currently a player can enter a time based puzzle and then open the notification bar to pause Unity and then solve the puzzle at their own pace.
I've tried calling Camera.Render(), setting active to false on gameObjects, moving the camera, and swapping materials but nothing seems to change the behavior. My frame is well under 30ms and I'm limiting what I'm doing within OnApplicationPause to ensure that I'm not running up against an iOS timer.
I know that OnApplicationPause(true) is being called and that I'm handling it correctly as PC does the correct thing and if I don't restore state when OnApplicationPause(false) is called then my state looks like it should when paused on iOS.
Having the exact issue on iOS. Where's our extra frame? Are there no pro's who have tackled this and can help us hide our screen? Is it not possible on iOS?
Anyone?
Answer by OP_toss · Oct 29, 2013 at 06:56 PM
I figured it out!
- (void)applicationWillResignActive:(UIApplication*)application
{
printf_console("-> applicationWillResignActive()\n");
UnitySendMessage( "<GAMEOBJECT>", "OnApplicationWillPause", "" );
[self repaintDisplayLink];
UnityPause(true);
...
So I send an explicit message to unity, which according to the docs has a 1 frame delay. Then I force an update loop with the repaint (calls UnityPlayerLoop,etc). This allows my camera to run, and my code to process before the render.
Calling repaint after the UnityPause did nothing, as I believe UnityPause is what sleeps the thread or disables rendering or something.
Hope this helps someone!
Got this to work by adding UnityPlayerLoop(); between the send message and the repaint. Otherwise the sendmessage function doesn't get handled.
Answer by Alexey · Mar 27, 2013 at 09:25 AM
- (void)applicationWillResignActive:(UIApplication*)application
{
printf_console("-> applicationWillResignActive()\n");
UnityPause(true);
try adding [self Repaint];
here, or just UnityPlayerLoop();
Didn't work for me... Tried UnityPlayerLoop after the UnityPause call, but no luck :/ Also Repaint doesn't exist in the current UnityAppController.mm. I'm running Unity 4.2.2.
Any other solutions to this problem?
Also tried creating a new camera at same location, disabling it as per the docs, and manually called Render() with a depth 100. OnPreRender and OnPostRender get called BEFORE the thread sleeps on iOS, but I stillll don't get a rendered frame to display...
I tested in the editor by perfor$$anonymous$$g this action then immediately setting UnityEditor.EditorApplication.isPaused = true, which works perfectly!
iOS seems to prevent the actual buffers from being swapped, or maybe pausing the editor is doing some magic drawing before pausing the thread...
Help?
Answer by mrdoktor1974 · Jan 07, 2014 at 09:51 AM
I have the same problem in Android. Any ideas here?
Answer by thinkyhead_ · Mar 10, 2015 at 09:44 PM
Pause the game when it resumes instead.
On iOS I noticed this behavior, and I was going to patch my game, but then I noticed that when going to background an iOS app immediately pauses anyway. The game freezes and iOS does a shrinking animation back to the desktop. When the game resumes, it receives OnApplicationPause(false)
. So that is when I tell the game to pause. In my case, I didn't feel the need to do the one-frame change before the game resigns foreground (it also feels strange), and I notice that most apps —games included— do not do a frame change when user hits the Home button.
/**
* OnApplicationPause
* Pause the game when the app is resumed.
* Only affects devices that pause apps
*/
void OnApplicationPause(bool pauseStatus) {
if (!pauseStatus && gameUnderway) PauseGame(true);
}
Answer by Digital_Alex · Mar 12, 2020 at 10:17 AM
For me this worked:
Overwrite the WillResignActive in UnityAppController.mm
-(void)applicationWillResignActive:(UIApplication*)application { ::printf("-> applicationWillResignActive()\n");
if (_unityAppReady)
{
UnitySetPlayerFocus(0);
_wasPausedExternal = UnityIsPaused();
if (_wasPausedExternal == false)
{
// Pause Unity only if we don't need special background processing
// otherwise batched player loop can be called to run user scripts.
if (!UnityGetUseCustomAppBackgroundBehavior())
{
// Force player to do one more frame, so scripts get a chance to render custom screen for minimized app in task manager.
// NB: UnityWillPause will schedule OnApplicationPause message, which will be sent normally inside repaint (unity player loop)
// NB: We will actually pause after the loop (when calling UnityPause).
UnityWillPause();
UnityPlayerLoop();
[self repaintDisplayLink];
UnityPause(1);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (!_didResignActive)
{
return;
}
_snapshotView = [self createSnapshotView];
if (_snapshotView)
[_rootView addSubview: _snapshotView];
});
}
}
}
_didResignActive = true;
}