SKSceneのpausedを勝手に変更されないようにする

一番ハードルが高かったのはリリースですね、本当に。。。
前回は、現象を再現したので、次は対策を打っていきたいと思います。

とりあえず、SKViewpausedが変更されるのは良しとします。
ただし、その下にぶら下がってるSKSceneはダメです。
なので、例のコードをSKSceneを継承したクラスに追加します。

//
// FOOHogeScene.h
//
@property (nonatomic, assign) BOOL stayPaused; // <-- 追加

//
// FOOHogeScene.m
//

// 初期化時に追加
self.stayPaused = NO;

// 適当なところに追加
-(void)setPaused:(BOOL)newValue
{
    if ( _stayPaused && !newValue ) {
        // ユーザーの操作ではないと判断して、Pauseを解除しない
        //NSLog( @"LCWGameScene setPaused rejected! newValue: %d", newValue );
    }
    else {
        //NSLog( @"LCWGameScene setPaused newValue: %d", newValue );
        super.paused = newValue;
    }
}

// 外からポーズにする関数の例
-(void)pause
{
    if ( self.paused ) {
        return;
    }

    self.paused = YES;
    self.stayPaused = YES;
    [self addCoverSprite];
    [self addChild:_resumuHintLabel];
}

// 外からポーズを解除する関数の例
-(void)resume
{
    self.stayPaused = NO;
    _resumeRequest = YES;
    _resumeDelayTime = .5f;
    _pauseButton.alpha = .3f; // 押せないので無効っぽい色に設定
    [_resumuHintLabel removeFromParent];
    [self removeCoverSprite];
}

もう少し補足しますね。
基本的に、SKViewpausedが勝手に変わっても、
大抵の場合は問題ないかと思います。

ですが、今回のケースでは、
プレイ中にホームボタンが押された場合は、
SKSceneで管理していないBGMの停止を行ったり、
再度、アプリが選択された場合は、
PAUSEボタンをタップするように促した状態を維持する必要があります。
要は、この手(シューティング)のゲームで勝手に再開されたら困る訳です。
なので、SKViewからSKSceneに対して、
pause/resumeを呼んで、停止と再開を実装するようにしました。(*1)

最後に、pause/resumeについて。

1. pause

PAUSEボタンがすでに押されて、ポーズに入っているケースを想定して、
すでにポーズに入っていないかチェックしています。
現在表示されているスプライトを少し薄く表示するために、
SKSceneを覆うようなスプライトを用意して追加しています。

-(void)addCoverSprite
{
    UIColor *color = [self.backgroundColor copy];
    SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithColor:color size:self.size];
    sprite.alpha = .3f;
    sprite.anchorPoint = CGPointZero;
    sprite.position = CGPointZero;
    sprite.name = kCoverSpriteName;
    sprite.zPosition = 2.0f; // 敵機のミサイルがカバーの上にくるのを防ぐ
    [self addChild:sprite];
}

あとで取り除くのに、nameを設定しているのもポイントです。
PAUSEを解除するボタンは、カバーの下になって薄くならないように、
zPositionの値をカバーより少し大きな値を設定しています。
_resumuHintLabelzPositionも同様です。
ちなみに、_resumuHintLabelは、
「再開するにはPAUSEボタンをタップしてください」といった感じの内容になっています。

2. resume

再開時は、すぐにpausedを変更しないで、若干の間を置いてから実行しています。
そのためのフラグであったり、どの程度時間を置くかだけ設定しています。
それと、先ほど追加したカバーを削除したり、ラベルの削除も行ってします。

-(void)removeCoverSprite
{
    SKNode *node = [self childNodeWithName:kCoverSpriteName];
    if ( node ) {
        [node removeFromParent];
    }
}

まとめ

変更した行数は少なかったですが、
SKViewが変更されると、そこにぶら下がってるSKSceneも変更されるのは、
今思うと当然といえば当然ですが、このおかげで変更点は少なくて済みました。
iOSのバージョンを見て切り替えるような方法もあるかもですが、
その手のコードを入れるのは後々面倒を招くので避けた次第です。

あとは、審査が通ることを祈るだけですね!

おしまい。

(*1) この方法が最善かは別(なんせ処女作ですから)

Leave a Comment