2

I'm making some game with Swift and SpriteKit. When my app is going to background it calls a function pause but it automatically unpause when the game resumes.

func pauseTheGame()
{
self.scene?.isPaused = true
}

AppDelegate

func applicationWillResignActive(_ application: UIApplication)
{
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "goToBackground"), object: self)

GameScene

NotificationCenter.default.addObserver(self, selector: #selector(GameScene.pauseTheGame), name: NSNotification.Name("goToBackground"), object: nil)

How can I fix it?

Nexus S.
  • 135
  • 9
  • This may be what you're looking for (note: not tested with more recent versions of iOS) http://stackoverflow.com/questions/26317553/pausing-spritekit-game-on-app-launch-exit-ios8 – 0x141E Sep 24 '16 at 17:27
  • If it's unpausing immediately, it's possible that your pause game function isn't actually being called. In `SpriteKit`, scenes are automatic paused when the app is left and automatically unpaused when returned to game. Add `fatalError()` to your function to check if it's being run – Nik Sep 25 '16 at 00:03
  • there is a bug where the game unpauses regardless, Just add an empty method called `CBApplicationDidBecomeActive` to your scene class, this will remove the automatic unpausing – Knight0fDragon Sep 25 '16 at 02:27
  • Try this if you're targeting iOS 10 http://stackoverflow.com/questions/26317553/pausing-spritekit-game-on-app-launch-exit-ios8 – 0x141E Sep 25 '16 at 17:53

1 Answers1

2

I think its not ideal to pause the whole scene, its better to have a worldNode and pause that node. This will also make your life easier for overlaying Menu nodes etc. Apple also does this in their sample game DemoBots.

Create a world node in your scene and a isGamePause property

 var isGamePaused = false
 let worldNode = SKNode()

and add it in didMoveToView

 addChild(worldNode)

Than add all your sprites to that node

 worldNode.addChild(someSprite1)
 worldNode.addChild(someSprite2)

Than in your pause function you say

 func pauseTheGame() {
    isGamePaused = true
    worldNode.paused = true
    physicsWorld.speed = 0
    /// show pause menu
 }

Your resume function should say

 func resumeTheGame() {
    isGamePaused = false
    worldNode.paused = false
    physicsWorld.speed = 1
    // remove pause menu
 }

To make extra sure that your game does not resume when paused I add a check in the update method to keep the game paused.

override func update(_ currentTime: TimeInterval) {

     guard !isGamePaused else { 
         worldNode.paused = true
         physicsWorld.speed = 0
         return 
     }

     ...
}

As a tip you should always organise string keys into properties to avoid typos e.g Notification centre names, UserDefaults keys, SKAction keys etc. With Swift 3 for Notification Center names you can now create an extension and handle them in a very neat way.

 extension NSNotification.Name {
      static let goToBackground = Notification.Name(rawValue: "goToBackground")
 }

Now you can say

 NotificationCenter.default.post(name: .goToBackground, object: self)

 NotificationCenter.default.addObserver(self, selector: #selector(pauseTheGame), name: .goToBackground, object: nil)

Hope this helps

crashoverride777
  • 10,581
  • 2
  • 32
  • 56
  • 2
    There may be times when pausing the entire scene is necessary, mostly when dealing with the game elapsed time. BTW, there are Notifications already being fired for when the app goes to background/foreground, so adding another observer is not efficient. Unfortunately getting a list of these is tough, so you may have to just go into the api headers. Add `UIApplicationWillResignActiveNotification` into your code and option click it, it should take you to the header for all of them. – Knight0fDragon Sep 26 '16 at 14:57
  • 1
    I do the same for my games as well, you need to manually pause the game when entering background and keep it paused when resuming. Letting sprite kit do the pause and resume is not good. I have seen way too many games that do it and its a bad user experience. You want your game to go to pause menu when you are minimising the app while playing and having a NotificationObserver in applicationWillResignActive is a convenient way to call your pause method. Pausing the whole scene is not needed, and especially difficult when trying to than overlaying menu nodes or run SKActions. – crashoverride777 Sep 26 '16 at 15:06
  • Oh i agree on the worldnode, i am always promoting that, I may have told you about it LOL – Knight0fDragon Sep 26 '16 at 15:08
  • 2
    I have already informed apple engineers that their pause design is bad, there should be 2 pause variables, one internal to the API, and one external to the user. On the condition of both being false, the node runs. This allows apple to pause the node when they see fit while preserving the user intended pause state. Of course they just brushed me off and said they are not changing how pause works lol so we all have to deal with work arounds. – Knight0fDragon Sep 26 '16 at 15:10
  • 1
    Agreed on the pause design. The pause scene or skView methods are basically useless. I have been using the worldNode since the beginning, its was one of the first things I did because pausing the scene gave me so many headaches. It also tends to give you this unpleasant jerk and it doesnt look great. You have to call your pause menu code from willResignActive if your are playing. Notification centre or delegation is the easiest way to do that. I hate when a game auto resumes when you open the app. A popular game at the moment, Mars: Mars, does the same, at least on apple TV and its terrible. – crashoverride777 Sep 26 '16 at 15:14
  • 1
    Even if you just get a message or notification alert or for AlertControllers, you should always enter your pause menu and let the player resume when they are ready. – crashoverride777 Sep 26 '16 at 15:14
  • 1
    Using the update method thing is to make extra sure that your game does not resume. Apple does something similar in DemoBots, they exit early if paused. – crashoverride777 Sep 26 '16 at 15:18
  • btw there is another bug with pause that really jacks things up, if you pause the parent, it sets all the children pause variables to that value – Knight0fDragon Sep 26 '16 at 15:20
  • Isn't that kind of what you want tho? I assume they get reset on unpause. SpriteKit needs more polish in general, its better with xCode 8 but still not great. Things such as the ios 9 camera bug they had for 1 year, where the camera was misplaced on iPhones, is unforgivable. – crashoverride777 Sep 26 '16 at 15:24
  • no absolutely not, It should never change the pause variable. If the parent is paused, the child is paused, but the variable should not be changed – Knight0fDragon Sep 26 '16 at 15:25
  • otherwise, if you have a child in a paused set, then tell parent to pause and unpause, child will fire. This is bad lol – Knight0fDragon Sep 26 '16 at 15:26
  • what camera bug? I never had a problem with ios 9 camera – Knight0fDragon Sep 26 '16 at 15:26
  • lol, maybe it was a bug depending on your scene settings and it didn't affect you. With the default xCode game settings it was misplaced on iPhones and you had to take this into account. http://stackoverflow.com/questions/39551231/spritekit-camera-position-correction-in-ios10 – crashoverride777 Sep 26 '16 at 15:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/124228/discussion-between-knight0fdragon-and-crashoverride777). – Knight0fDragon Sep 26 '16 at 15:43