読者です 読者をやめる 読者になる 読者になる

夏までにiPhone アプリつくってみっか!

趣味でiPhone/Androidアプリを開発し、日々勉強した事を書いています。オープンワールド系レースゲームをUnityで開発中です。

【cocos2d】replaceSceneでシーンを切り替えてみた

cocos2d

今開発しているアプリは縦スクロールシューティングゲームなので、ステージをクリアすると次のステージに進むように作らなければなりません。
そこで、cocos2dのシーンを次々に進めて行く機能を実装してみました。
あるステージから次のステージに直接シーンを切り替えると短時間ですが2つのシーンが同時にメモリ上に存在する瞬間ができてしまいます。このプログラムでは、シーンとシーンの間に次のステージ名を表示する単純なシーンを経由させる事でこれを防ぎます。

この中間シーン用のLoadingSceneクラスをCCSceneを継承してつくりました。
実装はこのようになっています。

#import "LoadingScene.h"
#import "GameScene.h"


@implementation LoadingScene
{
}

static NSInteger level = 0;

//トランジション開始時に呼ばれる
- (void)onEnter
{
    [super onEnter]; //忘れずに呼ぶ
    level++; //面は1から始まる
    CCLabelTTF* label = [CCLabelTTF labelWithString:[NSString stringWithFormat:@"Stage %d", level] fontName:@"arial" fontSize:30];
    CGSize winSize = [CCDirector sharedDirector].winSize;
    label.position = ccp(winSize.width/2, winSize.height/2);
    [self addChild:label];
}


// トランジション終了時に呼ばれる
- (void)onEnterTransitionDidFinish
{
    [super onEnterTransitionDidFinish]; //忘れずに呼ぶ
    [self scheduleOnce:@selector(loadScene:) delay:1.0]; //ここでは1秒待っているが最低でも1フレームは待つ必要あり
}

- (void)loadScene:(ccTime)delta
{
    [[CCDirector sharedDirector] replaceScene:[CCTransitionFlipX transitionWithDuration:1.0 scene:[GameScene sceneWithLevel:level]]];
}

@end

cocos2dのシーン切り替えで重要なのが核処理を実行するタイミングです。初期化処理はinitではなくonEnterに記述するのが普通なようです。今回のようにreplaceSceneでシーンを切り替える場合はシーンのインスタンスは使い捨てとなり毎回新しいオブジェクトが生成されますがpushScene, popSceneなどを使う場合は遷移元となるインスタンスは解放されずに残りますので、initやdeallocなどは呼ばれないからです。onEnter, onExitメソッドはシーン切り替え時に必ず呼ばれます。
今回はシーン切り替えにトランジションを使うので、処理をonEnterとonEnterTransitionDidFinishに分けています。
onEnterでステージ名を表示し、onEnterTransitionDidFinishでは時間待ちをしています。
どちらのメソッドでも必ずスーパークラスの同じメソッドを呼ぶ必要があります。
onEnterTransitionDidFinishではたとえ時間待ちをしない場合でもスケジュールを掛けて少なくとも1フレームは自分のシーンが表示されるようにプログラムしないとシーンがうまく切り替わりません。
例えば、上記のonEnterTransitionDidFinishから直接replaceSceneを呼ぶとシーンが切り替わったところでアプリがフリーズします。私はここでハマりました。

static変数 levelは何面を表示するかという情報をゲームプログラムに伝えるのに使用しています。static変数にしているのはインスタンスが使い捨てされても面の情報を保持するためです。インスタンス変数にすると毎回1面になってしまいます。スタティック変数の初期化はプログラム開始時に1回だけされ、それを毎回別のインスタンスがlevel++;でインクリメントして行きます。

それでは今回の実行動画です。面のデータを複数用意できませんでしたが、各面で違うラベルを表示していますのでシーンが正しく切り替わっている事が確認できます。