0

I am working on a game that is nearing completion, but I ran into one problem that I'm not able to solve myself (To be honest, there's multiple, but let's save that for other questions).

My game is organized like this: MainMenu --> GameScene --> Pause/GameOver The Pause and GameOver menus are basically just overlays made out of Sprite and Label nodes.

In the game, there is a player that needs to avoid touching enemies, that spawn every x amount of time. I achieve this by repeating an SKAction that runs a code block forever.

When the game pauses (When the pause-button is hit), it changes a boolean (isPaused) to YES, which causes every method to stop. It also removes the enemy-spawning action.

When the game restarts, it deletes everything that is currently there and recreates the entire scene.

Here's the problem: Whenever I click on the pause-button, so the menu shows up and then exit the app (just background not actually quitting) and come back and press restart, the enemies do not spawn ergo the action does not run (the node-count does not go up either I checked).

Here's some code:

I will not put in everything so if you find something referring to something that isn't there, that thing probably is there in the original code.

In GameScene.m:

-(void)didMoveToView:(SKView *)view {
if (!self.contentCreated) {
    [self createSceneContents];
    self.contentCreated = YES;
    }
   }

-(void)createSceneContents {
self.isTouchingGround = NO;
self.isPaused = NO;
self.world = [SKNode node];
NSLog(@"likewhatever");
self.playerCategory = 1;
self.enemyCategory = 2;
self.edgeCategory = 4;
self.bottomCategory = 8;
self.playerScore = 0;

[GameDataHelper sharedGameData].score = 0;

self.physicsWorld.contactDelegate = self;

SKSpriteNode *bottom = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:CGSizeMake(self.frame.size.width, 10)];
bottom.position = CGPointMake(self.frame.size.width/2, 0);
bottom.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:bottom.size];
bottom.physicsBody.dynamic = NO;
bottom.physicsBody.restitution = 0;
bottom.physicsBody.categoryBitMask = self.bottomCategory;
bottom.physicsBody.contactTestBitMask = self.playerCategory | self.enemyCategory;

SKSpriteNode *left = [[SKSpriteNode alloc]init];
left.size = CGSizeMake(1, self.frame.size.height);
left.position = CGPointMake(0, self.frame.size.height/2);
left.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:left.size];
left.physicsBody.dynamic = NO;
left.physicsBody.restitution = 0;

SKSpriteNode *right  = [[SKSpriteNode alloc]init];
right.size = CGSizeMake(1, self.frame.size.height);
right.position = CGPointMake(self.frame.size.width - 1, self.frame.size.height/2);
right.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:right.size];
right.physicsBody.dynamic = NO;
right.physicsBody.restitution = 0;


self.backgroundColor = [SKColor blackColor];
self.playerData = [[Player alloc]init];
self.customUnit = self.frame.size.width/7;
self.player = [self.playerData newPlayer:self.customUnit];
self.player.physicsBody.categoryBitMask = self.playerCategory;
self.player.physicsBody.contactTestBitMask = self.enemyCategory | self.edgeCategory | self.bottomCategory;
self.player.position = CGPointMake(CGRectGetMidX(self.frame), self.customUnit*5);
[self.playerData movementSetup];

[self createUI];

NSString *path = [NSString stringWithFormat:@"%@/gamemusic2.mp3", [[NSBundle mainBundle]resourcePath]];
NSURL *mainMusicURL = [NSURL fileURLWithPath:path];
self.mainMusicPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:mainMusicURL error:nil];
self.mainMusicPlayer.volume = 0.1;
self.mainMusicPlayer.numberOfLoops = -1;
[self.mainMusicPlayer play];

[self spawnObject];
[self addChild:self.world];
[self.world addChild:bottom];
[self.world addChild:self.player];
[self addChild:left];
[self addChild:right];
[self addChild:self.pause];
[self addChild:self.scoreLabelInGame];
[self addChild:self.actualScore];



}

-(void)createUI {

self.pause = [SKSpriteNode spriteNodeWithImageNamed:@"pausebutton.png"];
self.pause.size = CGSizeMake(self.customUnit,self.customUnit);
self.pause.name = @"pauseButton";
self.pause.position = CGPointMake(30, self.frame.size.height - 30);

self.pausedImage = [SKSpriteNode spriteNodeWithImageNamed:@"paused.png"];
self.pausedImage.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)*1.5);

self.restart = [SKLabelNode labelNodeWithFontNamed:@"Futura"];
self.restart.text = @"RESTART";
self.restart.fontSize = 25;
self.restart.position = CGPointMake(CGRectGetMidX(self.frame), self.pausedImage.position.y - self.pausedImage.position.y/5);

self.resume = [SKLabelNode labelNodeWithFontNamed:@"Futura"];
self.resume.text = @"RESUME";
self.resume.fontSize = 25;
self.resume.position = CGPointMake(self.restart.position.x, self.restart.position.y - self.customUnit);

self.scoreLabelInGame = [SKLabelNode labelNodeWithFontNamed:@"Futura"];
self.scoreLabelInGame.text = @"";
self.scoreLabelInGame.fontSize = 25;
self.scoreLabelInGame.position = CGPointMake(self.frame.size.width - 100, self.frame.size.height - 40);

self.actualScore = [SKLabelNode labelNodeWithFontNamed:@"Futura"];
self.actualScore.text = @"SCORE: 0";
self.actualScore.fontSize = 25;
self.actualScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeRight;
self.actualScore.position = CGPointMake(self.frame.size.width - 20, self.frame.size.height - 40);


self.deathImage = [SKSpriteNode spriteNodeWithImageNamed:@"youdied.png"];
self.deathImage.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)*1.5);
}

-(void)spawnObject {
NSLog(@"called");
if (self.isPaused == NO){
    NSLog(@"notpaused");
    self.spawningSpeed = 1.5;
    self.enemyData = [[Enemy alloc]init];
    SKAction *wait = [SKAction waitForDuration:self.spawningSpeed];
    SKAction *run = [SKAction runBlock:^{
        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];



    }];
    SKAction *action = [SKAction repeatActionForever:[SKAction sequence:@[wait,run]]];
    [self runAction:action withKey:@"spawn"];




}
}

-(void)didBeginContact:(SKPhysicsContact *)contact {

if (self.isPaused == NO) {

    SKPhysicsBody *firstBody, *secondBody;

    if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
        firstBody = contact.bodyA;
        secondBody = contact.bodyB;
    }else {
        firstBody = contact.bodyB;
        secondBody = contact.bodyA;
    }
    if (firstBody.categoryBitMask == self.playerCategory && secondBody.categoryBitMask == self.bottomCategory) {
        self.isTouchingGround = YES;
    }
    if (firstBody.categoryBitMask == self.playerCategory && secondBody.categoryBitMask == self.enemyCategory) {
        self.isTouchingGround = YES;
        if ([secondBody.node.name isEqualToString:@"fallingEnemy"] && self.player.position.y < secondBody.node.position.y) {
           [self gameOver];
        }
    }

    if (firstBody.categoryBitMask == self.enemyCategory && secondBody.categoryBitMask == self.enemyCategory) {

        [self.enemyData changeBlock:firstBody.node];
        [self.enemyData changeBlock:secondBody.node];
        [self impactSound];

        [self updateScore];

        NSLog(@"Change1");
    }
    if (firstBody.categoryBitMask == self.enemyCategory && secondBody.categoryBitMask == self.bottomCategory) {
        NSLog(@"Change2");
        [self.enemyData changeBlock:firstBody.node];
        [self impactSound];

        [self updateScore];

    }





}
}

-(void)pauseGame {
NSLog(@"Pausing...");
[self removeActionForKey:@"spawn"];
[self addChild:self.pausedImage];
[self addChild:self.restart];
[self addChild:self.resume];

NSString *path = [NSString stringWithFormat:@"%@/menu_music.mp3", [[NSBundle mainBundle]resourcePath]];
NSURL *pauseMusicURL = [NSURL fileURLWithPath:path];
self.pauseMusicPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:pauseMusicURL error:nil];
self.pauseMusicPlayer.numberOfLoops = -1;
[self.pauseMusicPlayer play];

[self.pause removeFromParent];
self.scoreLabelInGame.position = CGPointMake(self.restart.position.x, self.resume.position.y - self.customUnit);
self.actualScore.position = CGPointMake(self.restart.position.x, self.scoreLabelInGame.position.y - self.customUnit);
self.isPaused = YES;
[self.mainMusicPlayer pause];
}

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

-(void)resumeGame {
self.isPaused = NO;
[self.pauseMusicPlayer stop];
[self spawnObject];
self.scoreLabelInGame.position = CGPointMake(self.frame.size.width - 100, self.frame.size.height - 40);
self.actualScore.position = CGPointMake(self.frame.size.width - 20, self.frame.size.height - 40);
[self.mainMusicPlayer play];
[self.restart removeFromParent];
[self.resume removeFromParent];
[self.pausedImage removeFromParent];
[self addChild:self.pause];

}

-(void)gameOver {
NSLog(@"Game Over");
GameDataHelper *gameData = [[GameDataHelper alloc]init];

[self removeActionForKey:@"spawn"];
[self addChild:self.restart];
[self addChild:self.deathImage];
SKAction *gameOverSound = [SKAction playSoundFileNamed:@"gameover_tune.mp3" waitForCompletion:NO];
[self runAction:gameOverSound];

NSString *path = [NSString stringWithFormat:@"%@/menu_music.mp3", [[NSBundle mainBundle]resourcePath]];
NSURL *pauseMusicURL = [NSURL fileURLWithPath:path];
self.pauseMusicPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:pauseMusicURL error:nil];
self.pauseMusicPlayer.numberOfLoops = -1;
[self.pauseMusicPlayer play];
[self.pause removeFromParent];

SKLabelNode *highScore = [SKLabelNode labelNodeWithFontNamed:@"Futura"];
NSString *highScoreText = [NSString stringWithFormat:@"HIGHSCORE: %ld",[GameDataHelper sharedGameData].highScore];
highScore.text = highScoreText;
highScore.fontSize = 25;
highScore.position = CGPointMake(self.frame.size.width/2, self.restart.position.y - (2*self.customUnit));
[self addChild:highScore];

self.scoreLabelInGame.position = CGPointMake(self.restart.position.x, self.resume.position.y - self.customUnit);
self.actualScore.position = CGPointMake(self.restart.position.x, self.scoreLabelInGame.position.y - self.customUnit);
self.isPaused = YES;
[self.mainMusicPlayer pause];
   [gameData save];
}

In Enemy.m:

-(SKSpriteNode *)createEnemyWithSize:(float)size andWidth:(float)width andHeight:(float)height {
self.enemy = [SKSpriteNode spriteNodeWithImageNamed:@"block.png"];
self.enemy.size = CGSizeMake(size - 5, size - 5);
self.enemy.name = @"fallingEnemy";
self.enemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(size - 3, size - 3)];
self.enemy.physicsBody.restitution = 0;
self.enemy.physicsBody.allowsRotation = NO;
int randomSection = arc4random_uniform(7);
switch (randomSection) {
    case 0:

        self.enemy.position = CGPointMake(2.5 + self.enemy.size.width/2, height-5);

        break;
    case 1:


        self.enemy.position = CGPointMake(width/7 + self.enemy.size.width/2, height-5);

        break;
    case 2:

        self.enemy.position = CGPointMake((width/7*2)  + self.enemy.size.width/2, height-5);

        break;
    case 3:

        self.enemy.position = CGPointMake((width/7*3)  + self.enemy.size.width/2, height-5);

        break;
    case 4:

        self.enemy.position = CGPointMake((width/7*4)  + self.enemy.size.width/2, height-5);

        break;
    case 5:

        self.enemy.position = CGPointMake((width/7*5)  + self.enemy.size.width/2, height-5);

        break;
    case 6:

        self.enemy.position = CGPointMake((width/7*6)  + self.enemy.size.width/2, height-5);


        break;
    default:

        break;
}


return self.enemy;
}

-(void)changeBlock:(SKNode *)block{
    block.physicsBody.dynamic = NO;
    block.name = @"staticEnemy";



}
Squid
  • 315
  • 1
  • 11
  • are your log statements being called? the ones in spawn object that say "not paused"? – Steven Ritchie Jan 20 '15 at 16:37
  • Yes the object is working perfectly, but the action isn't :( – Squid Jan 20 '15 at 16:45
  • Note: in your enemy file you could replace cases 1-6 with a default: `self.enemy.position = CGPointMake((width/7*randomSection) + self.enemy.size.width/2, height-5);` –  Jan 20 '15 at 16:48
  • Try settings self.enemyData to nil when restarting the level – Steven Ritchie Jan 20 '15 at 16:57
  • Okapi Thanks that's very clever. Steven Ritchie That doesn't work :( – Squid Jan 20 '15 at 17:55
  • In my game I had to include `if(self.paused==true)return;` as the first line in the update method. For some reason, self.paused stopped my enemies from visibly moving but upon unpausing, the sprites all jumped to where they would be if the actions continued. I would assume your issue is similar. – meisenman Jan 20 '15 at 19:54
  • Replace the switch statement with `self.enemy.position = CGPointMake((randomSection?(width/7*randomSection):2.5) + self.enemy.size.width/2, height-5);` – 0x141E Jan 21 '15 at 09:02

1 Answers1

0

I was having a very similar issue to you (and noticed that your other questions were of similar nature). I was fortunately able to solve the problem and hope I can offer some insight to you.

My Situation: I am creating a physics based game that uses particle emitters and and SKActions to handle some of the aspects of the game. However, I noticed that these "broke" when returning from insactive; that is the particle emitter wasn't showing anything despite all the properties being the same before and after going into background, and SKActions not working even if they were initialized after returning from inactive. Like you, I used a custom "isPaused" variable to maintain game state.

My Solution: Change the name of the "isPaused" flag to something like "gameIsPaused".

Why? My research led me to believe that it had to do with some combination of how the scene held on to its children and what SpriteKit does when it goes into the background. While Apple doesn't publicize what's happening under the hood, various forums seemed to indicate that SpriteKit automatically paused the scene when the app went inactive.

BUT, by having your own "isPaused" variable, I think you're stomping on some under-the-hood processes which led to the bugs.

Other solutions that utilize the NSNotification center (What is causing my SKAction timer to behave strangely?) work as expected with renaming the "isPaused" flag.

Community
  • 1
  • 1
Alex
  • 16
  • 1