【问题标题】:pausing spritekit game on app launch / exit .. iOS8在应用程序启动/退出时暂停 spritekit 游戏 .. iOS8
【发布时间】:2026-02-08 09:10:01
【问题描述】:

我已经阅读了我能找到的关于这个主题的所有内容,但仍然无法弄清楚我的问题。我尝试在 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)
}

在我的控制器中:

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

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

我知道 pauseGame 有效,因为如果我在场景中使用按钮切换它,它将停止游戏。即使我在将它们加载到控制器后直接暂停我的 skview 和场景..游戏在启动时也不会暂停。当我在游戏中时,很容易暂停游戏。但出于某种原因,每当我退出并恢复应用程序时,游戏都会自行取消暂停。

我注意到如果我变得 hacky 并使用某种延迟.. 我可以让它工作。但显然这很愚蠢..我只需要知道游戏在哪里取消暂停!

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

【问题讨论】:

  • 看起来精灵套件在应用程序进入后台模式之前自动暂停视图和场景。它还会在 willEnterForeground/didBecomeActive 被调用后取消暂停视图/场景。
  • 是的,这正是我注意到的。有什么办法可以防止这种行为?
  • 我怀疑这是精灵套件中的一个错误,因为在调用 didBecomeActive 后游戏会恢复。

标签: ios swift ios8 sprite-kit nsnotificationcenter


【解决方案1】:

这是一种在从后台模式返回后保持视图暂停的方法。

Xcode 7(Xcode 8 说明见下文)

在故事板中,

1) 将视图的类更改为 MyView

在视图控制器中,

2) 使用名为 stayPaused 的布尔值定义一个 SKView 子类

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) 将视图定义为 MyView

4) 添加通知器来设置stayPaused 标志

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)

在应用代理中,

5) 在应用激活时发布通知以设置保持暂停标志

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

Xcode 8

在故事板中,

1) 将视图的类从SKView 更改为MyView

在视图控制器中,

2) 使用名为 stayPaused 的布尔值定义 SKView 子类

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) 将视图定义为 MyView

4) 添加通知器来设置stayPaused 标志

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)

在应用代理中,

5) 在应用激活时发布通知以设置保持暂停标志

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

【讨论】:

  • 我们必须继承一个 skview 才能使本来应该可以工作的东西工作起来,这有点糟糕。但是,嘿,至少它有效
  • 非常感谢!此外,如果您不想再创建一个类 - 覆盖 SKScene 的 pause 属性而不是 SKView
  • @0x141 是的,这行得通。我添加了对我有用的 Obj-C 示例(没有子类化视图)。
  • 如何在我的 GameScene 中使用这个 Pause 系统和我的 Pause 功能?并通过我已经在那里设置的按钮使其可编辑?
  • @Abdou023 当用户点击暂停按钮时,您需要暂停/恢复SKView。当用户按下 Home 按钮并通过点击其图标重新启动应用程序时,代码将保持视图暂停。
【解决方案2】:

这是一个完全基于 0x141E 给出的答案的 Obj-C 示例,没有子类化视图。就我而言,因为我有内部游戏状态,比如完成、暂停和开始,我必须在覆盖的 setPaused: 方法中进行额外检查。但这对我来说完美无缺,绝对是一种可能的方式。

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

【讨论】:

  • Whirlwind 你能告诉我如何在不继承视图的情况下快速执行相同的实现
【解决方案3】:

我最近遇到了同样的问题。 stayPaused 解决方案对我来说效果很好。谢谢大家:)

但是,我认为您用来设置 stayPaused 的机制并不可靠。仅在 App 激活时调用 setStayPaused 一次是不够的,Apple 的工程师可能会更改 Sprite Kit 的代码并在激活时调用 setPaused(false) 多次(实际上,我认为他们只是这样做了!)。第二个 setPaused(false) 将恢复游戏,因为您在第一次调用时将 stayPaused 设置回 false。

我的建议是直接在你的pauseGame/resumeGame函数中自己设置stayPaused属性,去掉“self.stayPaused = NO;”来自 setPaused 函数。这样一来,SpriteKit 的默认行为就可以随心所欲,但游戏会一直保持暂停状态。

【讨论】: