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.

I figured it out!

- (void)applicationWillResignActive:(UIApplication*)application
{
    printf_console("-> applicationWillResignActive()

");

    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!

  • (void)applicationWillResignActive:(UIApplication*)application
    {
    printf_console("-> applicationWillResignActive()
    ");
    UnityPause(true);

try adding [self Repaint]; here, or just UnityPlayerLoop();

I have the same problem in Android. Any ideas here?

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);
}

For me this worked:

Overwrite the WillResignActive in UnityAppController.mm

-(void)applicationWillResignActive:(UIApplication*)application
{
::printf("-> applicationWillResignActive()
");

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;

}

On Android, you can’t draw another extra frame, but on iOS you can. Unfortunately, Unity checkbox PlayerSettings->iOS->Render Extra Frame On Pause has no effect. The problem is caused by checkbox PlayerSettings->iOS->Multithreded Rendering which is On by default. Switch it Off on iOS, keep PlayerSettings->iOS->Render Extra Frame On Pause unchecked (it will add only blinking effect which is not what you need). Drawing extra frame will work as you expected!

Multithreded Rendeting is not really necessary for iOS devices in most cases.

Keep Multithreded Rendeting On on Android. As I already mentioned it’s impossible to draw something before Pause, but on Android you don’t make a slide gesture, but press a button to go to background, so it looks good when you draw Pause menu only when you enter foreground.