2
votes

Okay, I have a scene in which I have this method, createSceneContents, that gets called when didMoveToView gets called. In this method I have a couple of things that create the scenes, including a timer that spawns nodes like this:

self.spawningSpeed = 1.5;
self.enemyData = [[Enemy alloc]init];
SKAction *wait = [SKAction waitForDuration:1.5];
SKAction *run = [SKAction performSelector:@selector(spawningEnemy) onTarget:self];
self.spawnAction = [SKAction repeatActionForever:[SKAction sequence:@[wait,run]]];
[self.world runAction:self.spawnAction withKey:@"spawn"];

enemyData is an object of my enemy class which basically just adds a SKSpriteNode to the scene. The world node is simply a node that I add all the game elements to.

This is what happens in the spawningEnemy method:

-(void)spawningEnemy {

NSLog(@"spawned");
SKSpriteNode *aNewEnemy = [self.enemyData createEnemyWithSize:self.customUnit andWidth:self.frame.size.width andHeight:self.frame.size.height + self.player.position.y];
aNewEnemy.physicsBody.allowsRotation = NO;
aNewEnemy.physicsBody.categoryBitMask = self.enemyCategory;
aNewEnemy.physicsBody.collisionBitMask = self.enemyCategory | self.playerCategory | self.edgeCategory | self.bottomCategory;
aNewEnemy.physicsBody.contactTestBitMask = self.enemyCategory | self.playerCategory | self.edgeCategory | self.bottomCategory;
[self.world addChild:aNewEnemy];

}

This just gets an SKSpriteNode from the enemy class and sets some properties. It also adds it to the world.

I also have 4 methods that pause, resume, restart and game-over the game:

-(void)pauseGame {
[self createPauseMenu];
NSLog(@"Pausing...");
self.world.paused = YES;
self.isPaused = YES;

}
-(void)restartGame {
[self removeAllChildren];
[self removeAllActions];
self.enemyData = nil;
self.isPaused = NO;
[self createSceneContents];
}

-(void)resumeGame {
self.isPaused = NO;
self.world.paused = NO;
}

-(void)gameOver {
NSLog(@"Game Over");
self.world.paused = YES;

self.isPaused = YES;

}

There's more in these methods, but this is all that really matters.

Now here's the problem: This all works fine until I exit the app. When returning to the app, the game automatically calls the pause method, but then I hit restart or resume, the spawningEnemy method is not being called. (I checked with an NSLog message as you can see)

What might have caused this?

2
When returning to your app just re-run the SKActions you need.sangony
I am doing it: when returning to the app the pause method gets called, and when you then resume or restart the action automatically gets called.Squid
From the code you posted the only thing you are doing is setting self.isPaused = NO; and self.world.paused = NO; So where/when are you re-running your SKActions?sangony
yes and when the world is paused the action stops, and when it resumes the action resumes because it is a child of the world nodeSquid
Obviously it's not working so create a method which specifically re-runs any SKActions not working after the app comes back from the pause. Additionally you can remove all SKActions before your app goes into background to be sure you do not double up upon return.sangony

2 Answers

1
votes

I have tried the below code and it works. The spawning stops when the app resigns active and starts itself again once the app becomes active again.

You do not need to manually include code to pause as SpriteKit will pause itself when resigning active but I included it to show how to communicate between the AppDelegate and SKScene.

AppDelegate.m

- (void)applicationWillResignActive:(UIApplication *)application {
[[NSNotificationCenter defaultCenter]postNotificationName:@"applicationWillResignActive" object:self];
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
[[NSNotificationCenter defaultCenter]postNotificationName:@"applicationDidBecomeActive" object:self];
}

GameScene.m

-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
    SKAction *wait = [SKAction waitForDuration:1.5];
    SKAction *run = [SKAction performSelector:@selector(spawningEnemy) onTarget:self];
    SKAction *spawnAction = [SKAction repeatActionForever:[SKAction sequence:@[wait,run]]];
    [self runAction:spawnAction withKey:@"spawn"];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(pauseGame)
                                                 name:@"applicationWillResignActive"
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(resumeGame)
                                                 name:@"applicationDidBecomeActive"
                                               object:nil];
    }
return self;
}

-(void)spawningEnemy {
    NSLog(@"spawningEnemy");
}

-(void)pauseGame {
    NSLog(@"applicationWillResignActive...");
    self.paused = YES;
}

-(void)resumeGame {
    NSLog(@"applicationDidBecomeActive...");
    self.paused = NO;
}

-(void)willMoveFromView:(SKView *)view {
// good housekeeping
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
0
votes

If it is the whole scene that you want to have paused, then you should only pause the SKView containing the scene. This will pause all animations, run loops and interaction of the scene and all nodes within the scene.

Also experience learned me that it is good practice to pause the scene when the application goes to the background and resume when it returnes to the foreground. It prevents issues such as: Sprite Kit & playing sound leads to app termination