当应用程序恢复活动状态时,如何保持SpriteKit场景暂停?

7

有没有办法防止SpriteKit在进入前台/变为活动状态时自动取消暂停场景?

我设置了paused = true,希望即使应用程序被发送到后台后再次变为活动状态,它仍保持不变。

我应该补充说,我是用swift编写的,尽管我不认为在这方面行为会有所不同。


我不熟悉这些框架,但是paused = false不会取消暂停场景吗? - BergQuester
我是指 paused = true - 已更正。 - Pinxaton
如果您在进入后台模式之前使用 self.scene.view.paused = YES 暂停 SKView,则当应用程序变为活动状态时,其将保持暂停状态。您确定您正确地暂停了视图吗? - 0x141E
我相当确定我在正确地进行着。在AppDelegate的applicationWillResignActive()函数中,我只是做了 'scene.view.paused = true' (swift)。如果我在应用程序恢复活动时什么都不做,我会看到任何被暂停的SKActions都会恢复执行,并且update()函数仍然被调用并执行其操作。我本来期望在没有明确将scene.view.paused设置为 'false' 的情况下,将应用程序带回前台会使场景保持暂停状态,但很明显这并不是发生的事情。 - Pinxaton
5个回答

5

不确定Objective C是否相同,但是在Swift中,我必须“重写”SKView在幕后调用的回调函数。

func CBApplicationDidBecomeActive()
{

}

这个函数会导致暂停被重置。

(注意,不应用override关键字)

在某些情况下,如果您只想保留pause状态,请创建一个新变量,并覆盖isPaused方法。

class GameScene:SKScene
{
  var realPaused = false
  {
     didSet
     {
         isPaused = realPaused
     }
  }
  override var isPaused : Bool
  {
    get
    {
       return realPaused
    }
    set
    {
      //we do not want to use newValue because it is being set without our knowledge
      paused = realPaused
    }
  }
}

你是怎么得到这个秘密函数的?而且你是怎么实现它的? - mogelbuster
1
我通过查看堆栈跟踪找到了它,只需将该函数完全放置在您的GameScene类中,这将覆盖从SKScene调用的函数。您不使用override的原因是因为我们无法通过头文件访问此函数,因此我们不知道它的存在。 - Knight0fDragon
1
它在GameScene中没有被调用,但在SKView扩展和SKView子类中被调用。它像你说的那样工作,但这似乎很危险。我的意思是,除了设置isPaused之外,这个函数还做了什么?我想调用super的实现,然后重置isPaused,但我无法弄清楚如何使用super,因为我们并没有技术上覆盖它。 - mogelbuster
不知道它还能做什么,但从技术上讲,它不应该做任何事情。是的,你说得对,SKView哈哈,我甚至在我的答案中说了那个,不知道为什么我的大脑会说GameScene。另一个选项是覆盖GameScene上的isPaused变量,并保留另一个变量称为isRealPaused,以在需要跟踪时跟踪它。 - Knight0fDragon
抱歉,@mogelbuster忘记标记你了,不确定作为最后一个发送者是否会收到通知。 - Knight0fDragon

5

我对Knight0fDragon的解决方案做了一些调整以使其能够适用于我。这样可以确保isPaused始终等于realPaused。为了暂停游戏,“realPaused”变量应该被改变,这也会自动改变isPaused变量。

var realPaused: Bool = false {
    didSet {
        self.isPaused = realPaused
    }
}
override var isPaused: Bool {
    didSet {
        if (self.isPaused != self.realPaused) {
            self.isPaused = self.realPaused
        }
    }
}

很不幸,这将导致应用在后台运行时场景无法暂停。为了防止这种情况发生,我将条件更改为:“self.isPaused != self.realPaused && self.isPaused == false”,这样场景将在应用程序被置于后台时仍然自动暂停,但只有当realPaused也为true时才会重新激活:

var realPaused: Bool = false {
    didSet {
        self.isPaused = realPaused
    }
}
override var isPaused: Bool {
    didSet {
        if (self.isPaused == false && self.realPaused == true) {
            self.isPaused = true
        }
    }
}

我将这个应用到了我的SKNode子类中,因为它们包含了一些SKAction,我不希望在isPaused==true时触发它们,而场景仍然保持未暂停状态。 - Nick Greg
你和@knight0fdragon的回答都很好。这似乎也可以避免覆盖CBApplicationDidBecomeActive。如果它们有帮助,@pinxaton你应该接受其中一个答案。 - muZero

3
这个问题可能已经很老了,但我今天遇到了同样的问题,我认为我已经找到了一个相当好的解决方案:
在AppDelegate中,我执行以下操作:
- (void)applicationDidBecomeActive:(UIApplication *)application 
{
    SKView *view = (SKView *)self.window.rootViewController.view;

    if ([view.scene isKindOfClass:[GameScene class]]) 
    {
        XGGameScene *scene = (GameScene *)view.scene;
        [scene resumeGame];
    }
}

然后在GameScene类中,我更新了一个BOOL值以反映应用程序刚从后台恢复:

- (void)resumeGame
{
    //  Do whatever is necessary

    self.justResumedFromBackground = YES;
}

最后,在更新循环中,我运行以下代码:
- (void)update:(NSTimeInterval)currentTime
{
    // Other codes go here...

    // Check if just resumed from background.
    if (self.justResumedFromBackground) 
    {
        self.world.paused = YES;
        self.justResumedFromBackground = NO;
    }

    // Other codes go here...
}

希望这能帮到你!

2

Pinxaton,你是正确的,但是你可以通过添加短暂的延迟来暂停应用程序。

 (void)theAppIsActive:(NSNotification *)note 
{
    self.view.paused = YES;
       SKAction *pauseTimer= [SKAction sequence:@[
                                                [SKAction waitForDuration:0.1],
                                                [SKAction performSelector:@selector(pauseTimerfun)
                                                                 onTarget:self]

                                                ]];
    [self runAction:pauseTimer withKey:@"pauseTimer"];
}

-(void) pauseTimerfun
{
     self.view.paused = YES;


}

1
你应该利用你的应用程序委托,特别是applicationDidBecomeActive方法。在那个方法中发送一个通知,供你的SpriteKit视图监听。
因此,在applicationDidBecomeActive方法中,你的代码应该像这样:
// Post a notification when the app becomes active
[[NSNotificationCenter defaultCenter] postNotificationName:@"appIsActive" object:nil];    

现在,在你的SKScene文件的didMoveToView方法中添加以下内容:
// Add a listener that will respond to the notification sent from the above method
 [[NSNotificationCenter defaultCenter] addObserver:self 
                                          selector:@selector(theAppIsActive:) 
                                              name:@"appIsActive" object:nil];

然后只需将此方法添加到您的SKScene文件中:

//The method called when the notification arrives
(void)theAppIsActive:(NSNotification *)note 
{
    self.view.paused = YES;
}

谢谢您的建议。我尝试了这个方法,但好像没有达到预期的效果。当应用程序变成活动状态时,场景会暂停一小段时间,然后再次恢复。我想SpriteKit在接收和处理通知之后必须重新启动,可能是在不同的线程中? - Pinxaton
尝试将self.view.paused更改为self.scene.view.paused。您也可以在上述方法中简单地将bool设置为true。然后在每帧触发一次的update方法中,只需检查该bool以查看是否应暂停即可。 - Scooter
顺便提一下,已经有UIApplicationDidBecomeActiveNotification了,所以不需要发布你自己的。 - bio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接