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

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

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

【偽スペースハリアー】サイン・コサインでドラゴンの動きを完コピ?

cocos2d スペースハリアー TravelShooting JP

雑魚キャラのモーション作成の目処もついたので、いよいよボスキャラをどう実装するか検討してみたいと思います。

スペースハリアーの多くのボスキャラはやられない限り永遠に画面に居続けるため、雑魚キャラのようにあらかじめ動きの軌跡をデータとして持っておく方法はあまり適していないように思えます。
それではどうやって実装しようかと、1面のボスのスケイラの動きを目を皿のようにして10分くらい見ていると何となく螺旋状の動きが見えてきました。
x方向、y方向の動きはsine, cosine関数で実装できそうな気がします。z方向の動きを見てみると単純に決められた範囲を等速で行ったり来たりしているようです。

これを実装すると螺旋を描きながら行ったり来たりする動きを実装できます。
本物はもっと複雑な動きをしているのでよく見てみると、どうやら地面の左右スクロールの動きがミックスされているように見えます。
単純にミックスするとスクロールと共に遥か彼方に行ってしまうので画面中心から一定以上離れないようにリミッターを掛ける事でほぼ完コピとも言えるドラゴンの動きが実現できました。

それでは、動画をご覧下さい。

クリソツと言わざるを得ない出来になってしまいました。
(追記:この時点でクリソツは言い過ぎでした。)

ただ、ドラゴンの影が重なった部分が濃くなってしまうのが気に入らないので直す方法を考えます。

ドラゴンの動きのプログラムはこうなっています。

- (void)update:(ccTime)delta
{
    if (_coming) {
        _z -= Z_SPEED;
        if (_z <= DISTANCE_TO_SCREEN) {
            _coming = NO;
            CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"squila_head_back.png"];
            [self setDisplayFrame:frame];
        }
    } else {
        _z += Z_SPEED;
        if (_z >= FAR_LIMIT) {
            _coming = YES;
            CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"squila_head.png"];
            [self setDisplayFrame:frame];
        }
    }
    _time += TIME_INCREMENT;
    Position3D pos3D;
    _xOffset += [Scene3D sharedScene3D].xSpeed * X_SPEED_FACTOR;
    _xOffset = MIN(X_MAX, MAX(X_MIN, _xOffset));
    pos3D.position.x = sin(_time) * X_AMPLITUDE + _xOffset;
    pos3D.position.y = cos(_time) * Y_AMPLITUDE + Y_CENTER;
    pos3D.z = _z;
    self.pos3D = pos3D;
    [self convert3DTo2D];
    [_positionHistory insertObject:[NSValue valueWithBytes:&pos3D objCType:@encode(Position3D)] atIndex:0]; //先頭に追加
    [_positionHistory removeLastObject]; // 配列が伸びないように最後を削除
    pos3D.position.y = 0.0;
    self.shadow.pos3D = pos3D;
    [self.shadow convert3DTo2D];
    self.shadow.vertexZ -= 0.1;

    for (int i = 0; i < _bodyParts.count; i++) {
        [[_positionHistory objectAtIndex:(i + 1) * BODY_INTERVAL] getValue:&pos3D]; //頭と間隔をあけるためi + 1とする(頭 = 0)
        Sprite3D* body = [_bodyParts objectAtIndex:i];
        body.pos3D = pos3D;
        [body convert3DTo2D];
        pos3D.position.y = 0.0;
        body.shadow.pos3D = pos3D;
        [body.shadow convert3DTo2D];
        self.shadow.vertexZ -= 0.1;
    }
}

位置の計算はドラゴンの頭に関してのみ行っています。
_positionHistoryという配列に過去の頭の位置をある程度蓄積しておき、体の各パーツはそこから座標を読み出して使っています。

追記:
もう一度オリジナルと比較してみると、オリジナルは単純な螺旋運動ではなくもっと複雑でダイナミックな動きをしていることに気がつきました。
どうやら、x方向、y方向の振動数が違っていて、x方向の振幅ももっと大きくした方が良さそうです。
という訳で、パラメーターを若干調整し、y方向の振動数を半分、x方向の振幅を適当に増やしたバージョンがこちらです。今度こそ本当にクリソツです。