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

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

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

【偽スペースハリアー】地上物を追加。だが違う。何かが違う!

cocos2d スペースハリアー TravelShooting JP

スペースハリアープロジェクトにようやく敵キャラクター的なオブジェクトを追加します。
今回は地上のスクロールとシンクロして動く茂みを表示してみました。

こんな感じです。

しかし、本家のスペハリと比較するとかなりの違和感を感じます。
本物はもっとダイナミックに茂みが近づいてくる感じなのですが、私が作った方は何かノロノロしています。

どうやら遠くにあるときの動きが違うようです。

本物の場合は遠くのオブジェクトが結構な速さで近づいて来るように見えます。
私の場合は単純に遠くの物の大きさやスピードは距離に反比例して小さくなるという計算でプログラムしているので遠くのオブジェクトはなかなか近づいて来ません。かといってスクロールの速度を上げると近くにあるオブジェクトが超音速で過ぎ去ってしまいます。
スペハリの実装が3Dの計算として正しいのかどうか知りませんが、遊んでいて楽しいのはどう考えてもダイナミックにグイグイ迫ってくる方なので、何とかそれっぽい計算式を見つけ出したいと思います。

さて、cocos2dのプログラムですが、今回初めてvertexZを使ってみました。
通常、cocos2dのスプライトの重ね合わせの順序は親にaddChildするときのzパラメーターで制御しますが、3D的なプログラムで毎フレーム親ノードがreorderChildするのはパフォーマンス的にあまり良さそうではありません。(試していませんが)

そこで使えそうなのがvertexZプロパティです。
標準状態でvertexZに値を入れても何も変わりません。vertexZを有効にするにはAppDelegate.mを1行変更します。

	CCGLView *glView = [CCGLView viewWithFrame:[window_ bounds]
		pixelFormat:kEAGLColorFormatRGB565	//kEAGLColorFormatRGBA8
		depthFormat:GL_DEPTH_COMPONENT24_OES // VertexZを使う
		preserveBackbuffer:NO
		sharegroup:nil
		multiSampling:NO
		numberOfSamples:0];

「VertexZを使う」とコメントを入れてある行の値はデフォールトでは0になっていますが、GL_DEPTH_COMPONENT24_OESに変更します。

あと、スプライト同士が重なったときに手前のスプライトの四角い透明な枠が後ろのスプライトに掛かるのを防止するおまじないを入れます。このサイトを参考にしました。

        // スプライトが重なったときおかしくなるの防止
        [self setShaderProgram:[[CCShaderCache sharedShaderCache]
                                programForKey:kCCShader_PositionTextureColorAlphaTest]];

このおまじないをどこに入れるのか上記のサイトには詳しく書いていなかったのでとりあえずCCSpriteのサブクラスのinitに入れてみましたが、今のところ問題なく動いているようです。

追記:やっぱりinitではダメでした。条件により枠が見えてしまいます。
下記のようにdrawメソッドを上書きすると上手く行きました。

- (void)draw
{
    // スプライトが重なったときおかしくなるの防止
    [self setShaderProgram:[[CCShaderCache sharedShaderCache]
                            programForKey:kCCShader_PositionTextureColorAlphaTest]];
    [super draw];
}

これでvertexZを使えるようになりました。
vertexZは値が小さいほど先に描かれるので、vertexZの値が大きいスプライトに覆い隠されます。

私の場合画面の奥に行くほどzの値が大きくなるようにプログラムしているため、この値をそのままvertexZに入れると重ね合わせが逆になってしまいます。
なので、

self.vertexZ = -_pos3D.z*0.1;

のように値をマイナスにひっくり返して入れています。
0.1を掛けているのはvertexZで使える数値に制限があるためです。
試した結果-1024より小さい(-1025など)値を入れるとスプライトが表示されなくなりました。
今回は-1024より小さいz座標の値を使う可能性があるので0.1を掛けて小数点以下も有効に使うようにしています。
なお、全く同じvetexZの値を持つスプライトが重なると激しくチラつきますので、なるべく同じ値にならないようにします。

次回は地上オブジェクトがダイナミックにグイグイ近づいて来るように修正したいと思います。