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

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

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

【偽スペースハリアー】ロックオンしない通常弾はダミーターゲットを追尾

cocos2d スペースハリアー TravelShooting JP

今回は敵をロックオンしない状態で発射される通常弾を実装しました。
ロックオン弾より簡単に思えますが、実はこっちの方が難しいので後回しにしていました。
「まっすぐ飛ばせばいいんじゃないの?」と思うかもしれませんが、3D座標上でまっすぐ前方に飛ばすと画面上では地平線の中央に向かって飛んで行くように見えます。
これでは画面端にいる敵に弾を当てる事は無理です。
本物のスペースハリアーではどうなっているかというと、弾はバナナのように外側に曲がって飛んで行くようになっています。この自然な曲がり具合を再現すべく試行錯誤した結果を動画でご覧下さい。

今回は弾の影や発射音もついてよりそれらしくなっていると思います。
弾の曲がり具合は影を見ていただくとわかりやすいと思います。

画面左右外側に向かって移動する非表示のターゲットを追尾させる事で弾をこのように曲げています。
初めは弾の速度は一定でターゲットを画面中央から画面端まで走らせていたのですが、その場合は弾の軌跡が途中からくの字に鋭角に曲がってしまい本物とはほど遠い状況でした。

これを改良し、弾の初速を落とし、画面奥に飛んで行くに従い加速させるようにしました。
この改良により、滑らかにまがるようになりました。また、弾が一定の速度で飛んで行くように見えるようになりました。
あとはターゲットの初期位置と移動速度で曲がり方を微調整して本物と同じように見えるようになったら完成です。
弾と敵との当り判定は球ではなくz方向に伸びる円柱が重なったかどうかで見ています。
当り判定のコードはこんな感じです。

// 最初にヒットを検出したオブジェクトを返す。複数とのヒットはチェックしない
- (Sprite3D *)checkCollision
{
    for (Sprite3D* object in self.parent.children) {
        // objectのカテゴリーがコリジョンマスクに含まれる(自分自身はこの時点で除外される)
        if (self.collisionMask & object.category) {
            // objectのz座標がself以上self+collisionDepth以下の範囲にあるなら
            if ((object.pos3D.z >= self.pos3D.z) && (object.pos3D.z <= self.pos3D.z + self.collisionDepth)) {
                // selfとobjectとの距離が半径の和よりも小さい
                if (ccpDistance(self.pos3D.position, object.pos3D.position) <= (self.radius + object.radius)) {
                    // ヒット!
                    return object;
                }
            }
        }
    }
    return nil;
}

Sprite3DはCCSpriteを継承したクラスで、3D座標のためのPosition3D形構造体のpos3Dプロパティを持っています。ccpAddなどの関数を使いやすいよう、x,y座標は分けずににCGPoint構造体を使っています。

typedef struct {
    CGPoint position;
    CGFloat z;
} Position3D;