为什么我的游戏从后台返回后无法正常运行?

Why does my game not function properly after returning from the background?

我正在开发一款即将完成的游戏,但我 运行 遇到了一个我自己无法解决的问题(老实说,有多个问题,但让我们把它留到其他问题).

我的游戏是这样组织的:MainMenu --> GameScene --> Pause/GameOver Pause 和 GameOver 菜单基本上只是由 Sprite 和 Label 节点组成的叠加层。

在游戏中,有一个玩家需要避免接触每隔 x 时间生成一次的敌人。我通过重复 运行 永远是一个代码块的 SKAction 来实现这一点。

当游戏暂停时(按下暂停按钮时),它会将布尔值 (isPaused) 更改为 YES,这会导致所有方法停止。它还删除了生成敌人的动作。

游戏重新启动时,它会删除当前存在的所有内容并重新创建整个场景。

问题是:每当我点击暂停按钮时,菜单就会出现,然后然后退出应用程序(只是背景实际上并没有退出)然后回来并按重新启动,敌人不会产生因此动作不会 运行 (我检查过节点数也没有增加)。

这是一些代码:

我不会把所有东西都放进去,所以如果你发现某些东西引用了不存在的东西,那东西可能在原始代码中就存在。

在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];
}

在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";



}

我遇到了与您非常相似的问题(并注意到您的其他问题性质相似)。我很幸运能够解决这个问题,希望我能为您提供一些见解。

我的情况: 我正在创建一个基于物理的游戏,它使用粒子发射器和 SKActions 来处理游戏的某些方面。但是,我注意到这些 "broke" 从非活动状态返回时;那就是粒子发射器没有显示任何东西,尽管所有属性在进入背景之前和之后都是相同的,并且 SKActions 不工作,即使它们在从非活动状态返回后被初始化。和你一样,我使用自定义“isPaused”变量来维护游戏状态。

我的解决方案: 将“isPaused”标志的名称更改为类似“gameIsPaused”的名称。

为什么? 我的研究使我相信它与场景如何保持其子级以及 SpriteKit 进入背景时的行为的某种组合有关。虽然 Apple 没有公布引擎盖下发生的事情,但各种论坛似乎表明 SpriteKit 会在应用程序不活动时自动暂停场景。

但是,通过拥有自己的 "isPaused" 变量,我认为您正在踩踏一些导致错误的幕后流程。

其他利用 NSNotification 中心 (What is causing my SKAction timer to behave strangely?) 的解决方案通过重命名“isPaused”标志按预期工作。