Luiz Luiz - 6 months ago 51
Swift Question

iOS unpausing game after didBecomeActive

Sometime ago I was having a problem with pausing my game when

didBecomeActive
, then I found a solution which I thought was working, until now.

I found out that iOS automatically pauses my game (all of it) when I leave, not terminate, a game; and when coming back (
didBecomeActive
), it unpauses. As my point was to pause a singular layer (
gameLayer
), I created a boolean variable and an if condition to check if my game is paused or not.

If
checkPause == false
(not paused) -> it'll call a pausing function (that works great) when coming back to the game (moments after?! being unpaused by the system)


If
checkPause == true
(paused) -> it'll will set
gameLayer.paused = false
(unpaused by the system) to
true
(once it was paused before leaving the game)

Basically
gameLayer
is not being paused when coming back. It looks like iOS unpauses my game after
didBecomeActive
function.



I made an example project with it's code below (it's all commented and the simplest it could get)

If you want, you can download here.

import SpriteKit

class GameScene: SKScene {

//Declarations
var gameLayer = SKNode()
var pauseLayer = SKNode()

var checkPause = Bool() //checkPause == true -> gameLayer is paused | checkPause == false -> gameLayer is not paused

var enemy = SKSpriteNode()

var pauseButton = SKSpriteNode()
var playButton = SKSpriteNode()



//"Cage" objects in the screen
func cageObjects(){

//"caging" every object
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
}

//Setup
func setupPauseButton(){

//Pause
pauseButton = SKSpriteNode (imageNamed: "pause")
pauseButton.setScale(1)
pauseButton.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 1.2)
}
func setupPlayButton(){

//Play
playButton = SKSpriteNode (imageNamed: "play")
playButton.setScale(1)
playButton.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 1.2)
}
func setupEnemy(){

//Enemy
enemy = SKSpriteNode(imageNamed: "enemy")
enemy.position = CGPointMake(self.frame.width / 1, self.frame.height / 2)
enemy.name = "enemy"
enemy.setScale(0.5)

enemy.physicsBody?.affectedByGravity = false
}

//Layers
func createGameLayer(){

//pauseButton
setupPauseButton()
gameLayer.addChild(pauseButton) //add pauseButton to gameLayer
}
func createPauseLayer(){

//playButton
setupPlayButton()
pauseLayer.addChild(playButton) //add playButton to pauseLayer
}

//Spawn
func spawnEnemy(){

//Start spawning, moving and removing
let spawnEnemy = SKAction.runBlock({
() in

//Spawn enemy
self.setupEnemy()
self.gameLayer.addChild(self.enemy)

//Move left and remove when go off screen
let frameWidth = CGFloat(self.frame.width)
let moveEnemy = SKAction.moveByX(-frameWidth - 50, y: 0, duration: NSTimeInterval(0.0028 * frameWidth)) //duration: faster or slower
let removeEnemy = SKAction.removeFromParent()

var moveAndRemove = SKAction()
moveAndRemove = SKAction.sequence([moveEnemy, removeEnemy])

self.enemy.runAction(moveAndRemove)
})

//Spawn enemy each 2 seconds
let spawnEnemyDuration = SKAction.repeatActionForever(SKAction.sequence([spawnEnemy, SKAction.waitForDuration(2.0)]))
gameLayer.runAction(spawnEnemyDuration)
}

override func didMoveToView(view: SKView) {
/* Setup your scene here */

print ("didMoveToView")

registerAppTransitionObservers()

cageObjects()

checkPause = false

createGameLayer()
createPauseLayer()

self.addChild(gameLayer)

spawnEnemy()
}

//Game states
func pauseState(){

//Pause game
pauseButton.hidden = true //hide pauseButton
gameLayer.paused = true //pause gameLayer
checkPause = true //game is paused

self.addChild(pauseLayer) //add pauseLayer
}
func playState(){

pauseLayer.removeFromParent() //remove pauseLayer

//Resume game
checkPause = false //game is not paused
gameLayer.paused = false //unpause gameLayer
pauseButton.hidden = false //show pauseButton
}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */

//When touch buttons/screen
for touch in touches{

let location = touch.locationInNode(self)
let node = nodeAtPoint(location)

if node == pauseButton{
pauseState()
}
else if node == playButton{
playState()
}
}
}



//Functions from AppDelegate
func registerAppTransitionObservers(){
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameScene.applicationDidBecomeActive), name: UIApplicationDidBecomeActiveNotification, object: nil)
}

//Just launched
func applicationDidBecomeActive(){
print("DidBecomeActive")

//gameLayer unpausing problem solving attempt
if checkPause == true{
gameLayer.paused = true
}
//Pause when game is not paused and user leave the screen OR when game is launched
else if checkPause == false{
pauseState()
}
}
}

Answer

Now I have your source, I see your problem.

You need to preserve your pause state:

class GameScene : SKScene
{
    ...
    override var paused: Bool
    {
        get{
            return super.paused;
        }
        set{
            let value = self.gameLayer.paused
            super.paused = newValue;
            self.gameLayer.paused = value;

        }

    }
}

For some reason, scene paused is deciding to unpause all nodes under it

Also, you should pause your game when you leave the app, not when you come back.

//Functions from AppDelegate
func registerAppTransitionObservers(){
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameScene.applicationWillResign), name: UIApplicationWillResignActiveNotification, object: nil)
}

func applicationWillResign(){
    print("WillResignActive")
    pauseState()
}

And you can get rid of that check paused variable, that is redundant bloat code.

Comments