-1

When returning to my App after closing it the applicationDidBecomeActive(application: UIApplication) automatically fires in AppDelegate.swift. This fires a method that handles the paused status of the app: GameViewController().pause(true)

The method looks like this:

func pause(paused: Bool) {
    if paused == true {
        scene?.paused = true
        print("paused")

    } else if paused == false {
        scene?.paused = false
        print("unparsed")
    }
}

When first launching the app the Game is automatically paused which is exactly what should happen. When returning to the app it unpauses though. Still, the Console prints "paused".

I have also tried using scene?.view?.paused instead of scene?.paused. This does work, but leads to lag in the animations running on the scene.

Any help would be highly appreciated

EDIT

I managed to solve the problem by calling the pause() method in the update(currentTime: NSTimeInterval) function but I don't like this solution as it means the method is called once per frame. Other solutions would be highly appreciated

Gary
  • 1,020
  • 1
  • 8
  • 20
  • This question has been answered many times on SO, use the search feature – Knight0fDragon Jun 24 '16 at 12:31
  • I wouldn't have asked it if I had found anything useful. Every time the question is asked there is another solution and none of them have worked for me. – Gary Jun 24 '16 at 12:33
  • 1
    you need to override pause on your scene, because when scene pauses or unpauses, it flips the state of all of the children. Override pause, place all of the childrens pause into a temporary array, assign the pause value, then restore the children pause states. You may have to look at my answer on http://stackoverflow.com/questions/25351709/how-to-keep-spritekit-scene-paused-when-app-becomes-active if you are doing ios 8 – Knight0fDragon Jun 24 '16 at 12:37
  • As I said in the question, even when I set my scene to paused in the didBecomeActive function (which is what you say to do in your answer), the scene somehow unpauses again. It seems as if there was another function that gets called after the didBecomeActive method which unpauses the scene again, so changing the paused property in didBecomeActive has no effect at all for me. – Gary Jun 24 '16 at 12:56
  • But are you pausing another instance??? GameViewController().pause(true) is pausing a new instance... – Simone Pistecchia Jun 24 '16 at 13:08
  • I don't know any other way to pause it, Simone. Also, when calling the same function through a button tap it works perfectly fine. – Gary Jun 24 '16 at 13:10
  • With button are you start the GameViewController().pause(true) or self.pause(true) ? Anyway I use NSNotificationCenter, see http://stackoverflow.com/questions/29756777/tap-to-resume-pause-text-spritekit – Simone Pistecchia Jun 24 '16 at 13:19
  • I use self.pause(true) with the button but how would I access the function other that using GameViewController.pause(true) given that it is in another class? – Gary Jun 24 '16 at 13:22
  • NSNotificationCenter is the solution, check the link above – Simone Pistecchia Jun 24 '16 at 14:28
  • Sadly NSNotificationCenter does not work for me. As I said, it seems as if the scene unpauses after didBecomeActive calls so putting a NSNotification in the didBecomeActive function does not do anything. I would have to put it somewhere after didBecomeActive but I don't know what fires after that – Gary Jun 25 '16 at 07:32
  • @Knight0fDragon your comment here seems like the best swift answer to the 14 questions on SO about this topic, but your linked answer is a different approach. Would you consider submitting an answer with an example using this approach? I have been fighting this for a while, and your comment helped more than any answer I could find. – mogelbuster Oct 28 '16 at 13:17
  • @mogelbuster I honestly haven't found a universal way that I liked yet, what exactly is the problem, and Ill explain how to do it – Knight0fDragon Oct 28 '16 at 13:44

1 Answers1

2

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.

crashoverride777
  • 10,581
  • 2
  • 32
  • 56
  • Thank you very much, I implemented everything exactly as you said, however the problem remains: When first opening the app the worldNode gets paused (which is what I'd expect) when becoming active from background however, the worldNode unpauses automatically. – Gary Jul 01 '16 at 11:03
  • Yeah sorry forgot to mention this. I think this happens automatically, SpriteKit resuming nodes. What I usually do is have some sort of stateMachine (bools or enums or GKState) that has a gamePaused state and a gameActive state. When I am in the gamePaused state in the update method I say something like if isPaused { worldNode.paused = true } to ensure it doesn't resume automatically. I will update my answer – crashoverride777 Jul 01 '16 at 11:09
  • Hm.. I have tried that but it resumes anyway.. It seems as if the game I pauses after my update method fired but I don't know where else I should put it. But thank you anyways for trying to help! – Gary Jul 09 '16 at 13:38
  • You need to have a check in the update method right at the top like in my updated answer. The app delicate resume method does not only get called when you reopen your app, it also gets called for things such in app purchases etc. Thats why its good to have the update check to always pause the game where you are an in pause. If you have the check in the update it will work, thats what apple does and I have done in 3 games. If your game resumes than you must be doing something wrong or are calling resume game by accident somewhere. Check my answer again incase you missed something. – crashoverride777 Jul 09 '16 at 17:40
  • Yes you are right, it works when using the update method, but that was what I wrote in my initial question too. I was wondering if there was a better way to do it instead, but it looks as though there's not and I'll have to continue to check wether the game is paused or not for every frame. – Gary Jul 10 '16 at 11:28
  • Yeah it depends. The app delegate resume methods gets also called for things such as in app purchases as well. So I had the same problem, I paused the game, made a purchase and it resumed even though i don't want to. Better safe than sorry. Are you worried about performance? Why so hesitant? If you dont want to use the update method you can make an SKAction that repeats forever with an interval of like 1 sec. – crashoverride777 Jul 10 '16 at 11:39
  • Yeah I was wondering about performance and if I use a timer that repeats each second, one second may be to long.. Then again, It probably does not cost too many resources to check wether a variable is true or false once a frame so I'll probably stick with the standard update method – Gary Jul 11 '16 at 08:48
  • Defo, performance will be fine. If you feel like my answer helped would you be so kind to mark it. Happy coding – crashoverride777 Jul 11 '16 at 10:00