pausing spritekit game on app launch / exit .. iOS8
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)
}
Pausing a SpriteKit Game - UIApplicationWillResignActive vs. UIApplicationDidBecomeActive?
Pausing
This is from the docs about applicationWillResignActive method:
You should 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. An app in the inactive state should do minimal work
while it waits to transition to either the active or background state.
So this method is meant to be used when it comes to pausing the games.
Unpausing
iirc, game should be automatically unpaused when app transition from inactive to active state. This is the moment when applicationDidBecomeActive
method is called, so in some cases you may want to pause the game here as well... But that depends on your game.
I don't know why did you mention combination of applicationDidBecomeActive
& applicationDidEnterBackground
for pausing/unpausing... Because it is probably too late to pause the game in applicationDidEnterBackground
and technically it is not needed to unpause the game in applicationDidBecomeActive
because this is done automatically.
Spritekit game crashes when pausing scene from app delegate
Add a new method in appDelegate:
- (SKView *)getGameView {
NSArray *viewControllers = self.window.rootViewController.childViewControllers;
for (UIViewController *vc in viewControllers) {
if ([vc.view isKindOfClass:[SKView class]]) {
SKView *view = (SKView *)vc.view;
return view;
}
}
return nil;
}
and now ... modify your code from:
SKView *view = (SKView *)self.window.rootViewController.view;
view.paused = YES;
to:
SKView *view = [self getGameView];
if (view) {
view.paused = YES; //or NO
}
SpriteKit Autoresumes and auto pauses iOS8
I'm pretty sure it's a bug in spritekit. No matter what you do, the game will unpause itself in applicationDidBecomeActive
I asked the same question here. pausing spritekit game on app launch / exit .. iOS8 You have to subclass SKScene and override the paused
property to get it to work. It's weird you have to do that. It really shouldnt have so many problems, but thats the only way I could get my game to stay paused
EDIT: okay, I translated the code to objective-c. I hope this is useful to you because my objective-c was rustier than i expected.
AppDelegate.m
- (void)applicationWillResignActive:(UIApplication *)application {
[[NSNotificationCenter defaultCenter]postNotificationName:@"pauseGameScene" object:nil];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[NSNotificationCenter defaultCenter]postNotificationName:@"stayPausedNotification" object:nil];
}
SKView Subclass
@interface MySKView : SKView
- (void) setStayPaused;
@end
@implementation MySKView
bool _stayPaused = false;
- (void) setPaused:(BOOL)paused{
if (!_stayPaused) {
super.paused = paused;
}
_stayPaused = NO;
}
- (void) setStayPaused{
_stayPaused = YES;
}
@end
GameViewController
@interface GameViewController : UIViewController
-(void)pauseGame;
@end
@implementation GameViewController
SKScene *_scene;
MySKView *_skView;
-(void)pauseGame{
_skView.paused = YES;
_skView.scene.view.paused = YES;
}
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(pauseGame) name:@"pauseGameScene" object:nil];
// Configure the view.
_skView = [[MySKView alloc]initWithFrame:self.view.frame];
_skView.showsFPS = YES;
_skView.showsNodeCount = YES;
/* Sprite Kit applies additional optimizations to improve rendering performance */
_skView.ignoresSiblingOrder = YES;
// Create and configure the scene.
_scene = [[GameScene alloc]initWithSize:_skView.frame.size];
_scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[self.view addSubview:_skView];
[_skView presentScene:_scene];
}
- (void)viewDidAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter]addObserver:_skView selector:@selector(setStayPaused) name:@"stayPausedNotification" object:nil];
}
@end
SKAction resumes when appDidBecomeActive() even when game is paused before appDidResignActive()
Change pauseGame() and resumeGame() methods to look like this:
private func pauseGame() {
node.speed = 0.0
gameState = .paused
pauseLayer.run(SKAction.fadeAlpha(to: 0.5, duration: 1))
}
private func resumeGame() {
pauseLayer.run(.fadeOut(withDuration: 1))
node.speed = 1.0
gameState = .inProgress
}
Note that I am using speed property instead of isPaused property... That is all the difference you need to make. Can't really say why this happens because this behaviour is not documented, but I have seen it already, and been writing about it probably more in detail in some of my previous posts.
Keep SpriteKit scene paused after app becomes active
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.
Pausing iOS app game when User clicks Home Button
iOS can't magically pause or un-pause your game for you - it's something you have to handle on your own in the code.
Once you have the logic to un-pause the game you can get the 2 seconds delay you mentioned in your question by calling performSelector:withObject:afterDelay:
(see the docs). For example:
[self performSelector:@selector(continueGame) withObject:nil afterDelay:2.0]
where self
has a method called continueGame
to .. continue the game. :)
How to pause a timer in SpriteKit by using AppDelegate, Swift?
This is what I did in my game. Just define the timer variable outside the GameScene Class instead of inside it.
var timer = NSTimer() //Define my timer outside my gamescene class so I could access it in App Delegate
class GameScene: SKScene {
}
Related Topics
Saving Contact Address to Unified Contact Results in (Cnerrordomain Error 500)
My App Is Rejected with Exception Exc_Breakpoint (Sigtrap)
How to Make a Uiimage Scrollable Inside a Uiscrollview
Uiimage Aspect Fill When Using Drawinrect
How to Calculate a Random Cgpoint Which Does Not Touch a Uiview
Why Sending Message from Watchkit Extension to iOS and Getting Back a Reply Is So Slow
Swift Scenekit - Centering Scntext - the Getboundingboxmin:Max Issue
Transit Mkdirectionsrequest Produces Null Error Error Domain=Mkerrordomain Code=5 "(Null)"
Swift Add Line Above to Control
How to Get All Keys and Values into Separate String Arrays from Nsdictionary in Swift
How to Access Index of Tabbar Item Using Swift
Swift - Convert Values in Array to Doubles or Floats
Cmpedometer Querypedometerdatafromdate Returns Error 103
Change Ncwidgetdisplaymode Programmatically in iOS10 Widget
Calculate Distance Between My Location and a Mapkit Pin on Swift
How to Get Label Name from Button
How to Unwrap the Elements of an Array in Swift? (Ie. Array<Int> as Array<Int>)