0
votes

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()
        }
    }
}
1

1 Answers

1
votes

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.