How to Keep Spritekit Scene Paused When App Becomes Active

Keep SpriteKit scene paused after app becomes active

This code makes no sense

GameViewController().pause(true)

because you are creating a new instance of GameViewController rather than accessing the current one.

Rather than pausing the whole scene you should just pause the nodes that you would liked paused. Usually you create some kind of worldNode in your game scene (Apple also does this in DemoBots)

class GameScene: SKScene {

let worldNode = SKNode()

// state machine, simple bool example in this case
var isPaused = false

....
}

than add it to the scene in DidMoveToView

override func didMoveToView(view: SKView) {
addChild(worldNode)
}

Than all nodes that you need paused you add to the worldNode

worldNode.addChild(YOURNODE1)
worldNode.addChild(YOURNODE2)

Than your pause function should look like this

 func pause() {
worldNode.paused = true
physicsWorld.speed = 0
isPaused = true
}

and resume like this

  func resume() {
worldNode.paused = false
physicsWorld.speed = 1
isPaused = false
}

Lastly to make sure the game is always paused when in paused add this to your update method. This ensures that you game does not resume by accident e.g due to app delegate, iOS alerts etc.

  override func update(currentTime: CFTimeInterval) {

if isPaused {
worldNode.paused = true
physicsWord.speed = 0
return
}

// Your update code
...
}

To call these from your AppDelegate you should use delegation or NSNotificationCenter as has been mentioned in one of the comments.

In gameScene create the NSNotifcationObserver in didMoveToView

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(pause), name: "Pause", object: nil) // in your app put the name string into a global constant property to avoid typos when posting 

and in appDelegate post it at the correct spot

NSNotificationCenter.defaultCenter().postNotificationName("Pause", object: nil)

The main benefit with the worldNode approach is that you can easily add pause menu sprites etc while the actual game is paused. You also have more control over your game, e.g having the background still be animated while game is paused.

Hope this helps.

Keeping the game paused after app become active?

I think a better way is instead of pausing the whole scene you could create a worldNode in your GameScene and add all the sprites that need to be paused to that worldNode. Its better because if you pause the scene you cannot add pause menu nodes or use touches began etc. It basically gives you more flexibility pausing a node rather than the whole scene.

First create the world node (make global property if needed)

 let worldNode = SKNode()
addChild(worldNode)

Than add all the sprites you need paused to the worldNode

 worldNode.addChild(sprite1)
worldNode.addChild(sprite2)

Create an enum for your different game states

enum GameState {
case Playing
case Paused
case GameOver
static var current = GameState.Playing
}

Than make a pause func in your game scene

 func pause() {
GameState.current = .Paused
//self.physicsWorld.speed = 0 // in update
//worldNode.paused = true // in update

// show pause menu etc
}

And call it like you did above using NSNotification or even better using delegation.

I prefer this method way more than pausing the scene from the gameViewController and also pausing the whole scene.

Create a resume method

 func resume() {
GameState.current = .Playing
self.physicsWorld.speed = 1
worldNode.paused = false

// remove pause menu etc
}

and finally add this to your update method

override func update(currentTime: CFTimeInterval) {

if GameState.current == .Paused {
self.physicsWorld.speed = 0
worldNode.paused = true
}

Spritekit sometimes tends to resume the game when the app becomes active again or when an alert such as for in app purchases is dismissed. To avoid this I always put the code to actually pause the game in the update method.

Hope this helps.

SKAction resumes when appDidBecomeActive() even when game is paused before appDidResignActive()

Change pauseGame() and resumeGame() methods to look like this:

private func pauseGame() {

node.speed = 0.0
gameState = .paused
pauseLayer.run(SKAction.fadeAlpha(to: 0.5, duration: 1))

}
private func resumeGame() {
pauseLayer.run(.fadeOut(withDuration: 1))
node.speed = 1.0
gameState = .inProgress
}

Note that I am using speed property instead of isPaused property... That is all the difference you need to make. Can't really say why this happens because this behaviour is not documented, but I have seen it already, and been writing about it probably more in detail in some of my previous posts.

pausing spritekit game on app launch / exit .. iOS8

Here's a way to keep the view paused after returning from background mode.

Xcode 7 (see below for Xcode 8 instructions)

In the storyboard,

1) Change the class of the view to MyView

In the View Controller,

2) Define an SKView subclass with a boolean named stayPaused

class MyView: SKView {
var stayPaused = false

override var paused: Bool {
get {
return super.paused
}
set {
if (!stayPaused) {
super.paused = newValue
}
stayPaused = false
}
}

func setStayPaused() {
if (super.paused) {
self.stayPaused = true
}
}
}

3) Define the view as MyView

4) Add a notifier to set the stayPaused flag

class GameViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
// Configure the view.
let skView = self.view as MyView

NSNotificationCenter.defaultCenter().addObserver(skView, selector:Selector("setStayPaused"), name: "stayPausedNotification", object: nil)

In the App Delegate,

5) Post a notification to set the stay paused flag when the app becomes active

func applicationDidBecomeActive(application: UIApplication) {
NSNotificationCenter.defaultCenter().postNotificationName("stayPausedNotification", object:nil)
}

Xcode 8

In the storyboard,

1) Change the class of the view from SKView to MyView

In the View Controller,

2) Define an SKView subclass with a boolean named stayPaused

class MyView: SKView {
var stayPaused = false

override var isPaused: Bool {
get {
return super.isPaused
}
set {
if (!stayPaused) {
super.isPaused = newValue
}
stayPaused = false
}
}

func setStayPaused() {
if (super.isPaused) {
self.stayPaused = true
}
}
}

3) Define the view as MyView

4) Add a notifier to set the stayPaused flag

class GameViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

if let view = self.view as! MyView? {
NotificationCenter.default.addObserver(view, selector:#selector(MyView.setStayPaused), name: NSNotification.Name(rawValue: "stayPausedNotification"), object: nil)

In the App Delegate,

5) Post a notification to set the stay paused flag when the app becomes active

func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "stayPausedNotification"), object: nil)
}


Related Topics



Leave a reply



Submit