简体   繁体   中英

pausing spritekit game on app launch / exit .. iOS8

I've read everything I could find on this topic and still cant figure out my issue. I have tried pausing my game in every area of appdelegate

func applicationWillResignActive(application: UIApplication!) {
    NSNotificationCenter.defaultCenter().postNotificationName("pauseGameScene", object: self)
}

func applicationDidEnterBackground(application: UIApplication!) {
    NSNotificationCenter.defaultCenter().postNotificationName("pauseGameScene", object: self)
}

func applicationWillEnterForeground(application: UIApplication!) {
    NSNotificationCenter.defaultCenter().postNotificationName("pauseGameScene", object: self)
}

func applicationDidBecomeActive(application: UIApplication!) {
    NSNotificationCenter.defaultCenter().postNotificationName("pauseGameScene", object: self)
}

In my controller:

override func viewDidLoad() {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "pauseGame:", name: "pauseGameScene", object: nil)
}

func pauseGame(){
    self.skView.paused = true
    self.skView.scene!.paused = true
}

i know pauseGame works because if i toggle it with a button in my scene, it will stop the game. Even if I pause my skview and scene directly after they are loaded in the controller.. the game will not be paused on launch. It's easy to pause the game when I'm in-game. but for some reason whenever i exit and resume the app, the game will un-pause itself.

i notice if i get hacky and use some kind of delay.. i can get it to work. but obviously this is very stupid.. i just need to know where the game is unpausing itself!

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

func pauseGame(sender: UIButton!){

    delay(2) {
        println("blah")
        self.skView.paused = true
        self.skView.scene!.paused = true
    }
}

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

Here is an Obj-C example completely based on answer given by 0x141E, without subclassing a view. In my case, because I have internal game states, like finished, paused and started, I had to make an additional check in overridden setPaused: method. But this worked flawlessly for me and definitely it is the one possible way to go.

AppDelegate.m

- (void)applicationDidBecomeActive:(UIApplication *)application {

    [[NSNotificationCenter defaultCenter] postNotificationName:@"stayPausedNotification" object:nil];
}

GameScene.m

@interface GameScene()

@property (nonatomic, assign) BOOL stayPaused;

@end

@implementation GameScene

//Use initWithCoder: if you load a scene from .sks file, because initWithSize is not called in that case.

-(instancetype)initWithSize:(CGSize)size{ 

    if(self = [super initWithSize:size]){

        _stayPaused = NO;


         //register to listen for event
        [[NSNotificationCenter defaultCenter]
         addObserver:self
         selector:@selector(setStayPaused)
         name:@"stayPausedNotification"
         object:nil ];
    }
    return self;
}

-(void)setStayPaused{

    self.stayPaused = YES;
}

-(void)setPaused:(BOOL)paused{

    if (!self.stayPaused) {
        [super setPaused:paused];
    }
    self.stayPaused = NO;
}

-(void)willMoveFromView:(SKView *)view{

    [[NSNotificationCenter defaultCenter] removeObserver:self  name:@"stayPausedNotification" object:nil];
}

@end

I recently encountered the same issue. The stayPaused solution works fine for me. Thanks guys:)

However, I think the mechanism you use to set stayPaused is NOT robust. Just by calling setStayPaused once on App becoming active is not enough, engineers in apple may change the code of Sprite Kit and call setPaused(false) MORE THAN ONCE on activating (In fact, I think they just did!). The second setPaused(false) will resume the game as you set stayPaused back to false on the first call.

My suggestion is to directly set the stayPaused property yourself in your pauseGame/resumeGame functions, and remove the "self.stayPaused = NO;" from setPaused function. In this way, the default behavior of SpriteKit can do whatever it likes, but the game will stay paused as long as we want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM