6

I know SpriteKit already handles pausing the game when the app enters the inactive state but what I'm trying to do is add a SKLabelNode "tap to resume" when the app re-enters the active state. Right now it's calling my functions correctly and pausing the game, but the text is not showing.

AppDelegate.swift

func applicationWillResignActive(application: UIApplication) {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    println("applicationWillResignActive")
    NSNotificationCenter.defaultCenter().postNotificationName("PauseGameScene", object: self)
    NSNotificationCenter.defaultCenter().postNotificationName("ShowPauseText", object: self)
    ...
}

GameScene.swift

class GameScene: SKScene, SKPhysicsContactDelegate {
    ...
    let tapToResume = SKLabelNode(fontNamed: "Noteworthy")
    ...
    override func didMoveToView(view: SKView) {
        ...
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("pauseGameScene"), name: "PauseGameScene", object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("showPauseText"), name: "ShowPauseText", object: nil)

        tapToResume.text = "tap to resume"
        tapToResume.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
        tapToResume.fontSize = 55
        tapToResume.hidden = true
        self.addChild(tapToResume)
        ...
    }

    func pauseGameScene() {
        println("pause game")
        self.view?.paused = true
    }

    func showPauseText() {
        if self.view?.paused == true {
            tapToResume.hidden = false
            println("show text")
        }
    }

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        ...
        if self.paused {
            self.view?.paused = false
            if tapToResume.hidden == false {
                tapToResume.hidden = true
            }
        }
    }
    ...
}

EDIT:

Below is a screenshot of my terminal output with my latest edits to my above code: enter image description here

Braiam
  • 1
  • 11
  • 47
  • 78
Mike
  • 1,307
  • 3
  • 17
  • 29
  • Should you not be setting the `SKLabelNode`'s `text` property? Or did you just not include that bit in your code above? – ABakerSmith Apr 20 '15 at 19:46
  • Apologies, the code is there just forgot to add it – Mike Apr 20 '15 at 19:56
  • are you calling showPauseText before pausing the view? It looks like you are pausing first then trying to unhide the text. – Steve Apr 20 '15 at 20:27
  • I just tried switching them, calling the show text first, and the text is still not showing up. – Mike Apr 20 '15 at 21:13
  • maybe your `fontNamed` parameter (when you declare the label) has an incorrect font name passed to it, or maybe the label is hidden behind another sprite because its z-index is lower? – Abdul Ahmad Apr 20 '15 at 21:24
  • Do you have anything handling the tap? – LinusGeffarth Apr 20 '15 at 21:24
  • The functions are being called correctly, I previously had print statements in `pauseGameScene()` and `showPauseText()` that were firing correctly, and the label appears correctly if it's added in `DidMoveToView()` as any other label. – Mike Apr 20 '15 at 21:27
  • What I mean is that if I don't hide the label, it will appear fine on the screen in `didMoveToView()` – Mike Apr 20 '15 at 21:28
  • @LinusG. Yes I have my `touchesBegan()` which resumes the game. My problem is not with pausing/resuming the game, this works fine. My problem is with my 'tap to resume' label not showing up. – Mike Apr 20 '15 at 21:56
  • I see. Does the method `showPauseText()` get called actually? I mean have you checked with some logs? – LinusGeffarth Apr 20 '15 at 22:08
  • @LinusG. yes, as I stated earlier, I had print statements in each clause and both are being called. I removed them for clarity. – Mike Apr 21 '15 at 05:33

3 Answers3

4

So I "hacked" my solution here. Thanks to ABakerSmith with the suggestion of setting self.speed = 0.0, the actions were paused and the my label will appear but the physicsWorld was still active. So my solution was to set self.speed = 0.0 AND self.physicsWorld.speed = 0.0. When the app returns from the inactive state, I just reset self.speed = 1.0 and self.physicsWorld.speed = 1.0. I'm sure there are other solutions to this dilemma but since SpriteKit already handles interruptions, all I really needed to do was pause the actions and the physics.

GameScene.swift

class GameScene: SKScene, SKPhysicsContactDelegate {
    let tapToResume = SKLabelNode(fontNamed: "Noteworthy")
    ...

    override func didMoveToView(view: SKView) {
        ...
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("pauseGameScene"), name: "PauseGameScene", object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("showPauseText"), name: "ShowPauseText", object: nil)
    }

    func pauseGameScene() {
        self.physicsWorld.speed = 0.0
        self.speed = 0.0
    }

    func showPauseText() {
        if self.physicsWorld.speed == 0.0 {
        tapToResume.hidden = false
        }
    }

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        ...
        if self.physicsWorld.speed == 0.0 {
            self.physicsWorld.speed = 1.0
            self.speed = 1.0
            if tapToResume.hidden == false {
                tapToResume.hidden = true
            }
        }
    }

    ...
}

AppDelegate.swift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    func applicationWillResignActive(application: UIApplication) {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    NSNotificationCenter.defaultCenter().postNotificationName("PauseGameScene", object: self)
    NSNotificationCenter.defaultCenter().postNotificationName("ShowPauseText", object: self)
    }
    ...
}
Mike
  • 1,307
  • 3
  • 17
  • 29
3

I believe your problem was setting self.view?.paused = true, as was pointed out by @Steve in a comment and @Linus G. in his answer. Therefore, when you tried to unhide the label, nothing happened because the view was paused.

I tried using self.paused to pause the SKScene instead. This solved the problems of showing the label, however it didn't actually pause the scene. This could be due to: since iOS8, SpriteKit automatically pauses your game when it enters the background and un-pauses the game when it enters the foreground. Therefore, trying to set self.paused = true, using applicationWillResignActive, had no effect because it was un-paused when entering the foreground.

To solve this you can observe UIApplicationWillResignActiveNotification. Then when the application is going to resign being active, you set self.speed = 0, which has the same effect as pausing the SKScene. This displays the label and pauses the scene as required.

For example:

class GameScene: SKScene {
    let tapToResume = SKLabelNode(fontNamed: "Noteworthy")

    override func didMoveToView(view: SKView) {
        NSNotificationCenter.defaultCenter().addObserver(self, 
                             selector: Selector("pauseScene"), 
                             name: UIApplicationWillResignActiveNotification, 
                             object: nil)

        tapToResume.text = "tap to resume"
        tapToResume.position = CGPoint(x: frame.midX, y: frame.midY)
        tapToResume.fontSize = 55
        tapToResume.hidden = true
        self.addChild(tapToResume)
}

func pauseScene() {
    self.speed = 0.0
    tapToResume.hidden = false
}

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    // Check the label was pressed here.
    if labelWasPressed {
        self.speed = 1.0
        tapToResume.hidden = true
    }
}
ABakerSmith
  • 22,759
  • 9
  • 68
  • 78
  • Thanks for the suggestion. Though this does allow the 'tap to resume' label to unhide when the app enters the inactive state and allows me to hide the label when I return and click on it, my "flappy bird" (which is actually Kirby, a childhood favorite) is still effected by the physicsWorld and falls to the ground. tl;dr setting the speed to 0.0 stop the world but does not stop my Kirby sprite. – Mike Apr 21 '15 at 15:20
2

I think I got the problem. You should call pauseGameScene() first. Then view?.paused is true. Then you can call showPauseText().

Hope that helps :)

LinusGeffarth
  • 27,197
  • 29
  • 120
  • 174
  • Hi Linus, I moved my `showPauseText()` function to the AppDelegate function `applicationWillEnterForeground()` such that when the app returns from the inactive state, it displays the text. But still no luck. I have updated my code in my original post to reflect the changes and I have also added my terminal output. – Mike Apr 21 '15 at 05:43
  • Disregard the above. Hi Linus, I changed around my code a bit and put back my print statements as you suggested. The label is still not being shown but you can see that it is getting called. – Mike Apr 21 '15 at 05:48