第13回:ボスを作る(2)

 続いてボスの砲台部分を装着させることにします。こちらも以前に紹介した当たり判定領域を分割する手法をとり、砲台の向きが回転しても、適度の正確さを持った当たり判定を行えるようにします。


 砲台の座標は青色部分を中心とし、実際の範囲はボスの位置と角度に応じてリアルタイムに計算するようにしています。
class CBoss : public CEnemyBase
{
    /* 省略 */
private:
    CSprite battery;    // 砲台のグラフィック
    float bangle[2];    // 砲台が向いている角度×2
    float bypos[5];        // 砲台の当たり判定の基準位置(y軸のみ)
}
// ボスの中心座標に対する砲台の配置場所
#define BUTTERY_X 196.0f
#define BUTTERY_Y (-2.0f)

CBoss::CBoss(void)
{
    /* 省略 */
    
    bangle[0] = bangle[1] = 0.0f;
    bypos[0] = -72.0f;
    bypos[1] = -32.0f;
    bypos[2] = 0.0f;
    bypos[3] = 32.0f;
    bypos[4] = 72.0f;
}

void CBoss::Init()
{
    CEnemyBase::Init();

    sprite.SetTexture((CTexture*)FindItemBox("bosstex"));
    sprite.SetSpriteSize(200, 136);

    // 砲台のスプライト設定
    battery.SetTexture((CTexture*)FindItemBox("bosstex"));
    battery.SetSpriteRect(200, 0, 240, 184);
}

void CBoss::Exec()
{
    /* 省略 */

    battery.Draw(x - BUTTERY_X, y - BUTTERY_Y, bangle[0]);
    battery.Draw(x + BUTTERY_X, y - BUTTERY_Y, bangle[1]);

    if(this->HitTest(player) && hardness > 0){
        player->Destroy();
    }
}

bool CBoss::HitTest(CCharactor* target)
{
    /* 省略 */

    for(int i = 0; i < 2; i++){
        /* 省略 */

        // 砲台部分との当たり判定
        for(int j = 0; j < 5; j++){
            // 回転後の座標を計算
            float xx = -sinf(bangle[i]) * bypos[j];
            float yy = cosf(bangle[i]) * bypos[j];

            // 回転後の座標に水平移動量を合成
            if(i == 0){
                xx += x - BUTTERY_X;
            }else{
                xx += x + BUTTERY_X;
            }
            yy += y + BUTTERY_Y;
            
            // 領域に適用
            brect.SetRect(-20.0f, -20.0f, 20.0f, 20.0f);
            brect.Shift(xx, yy);

            if(brect.IsCross(&trect)) return true;
        }
    }

    return false;
}
 プログラム実行例です。ぐるぐる回転する砲台の当たり判定がきちんと行われていることがおわかりいただけるでしょう。

 続いて、砲台の発射口より弾を発射する処理を追加します。
class CBoss : public CEnemyBase
{
    /* 省略 */

private:
    void ShootFromNozzle(float speed);
};
// 砲台に対する発射口の相対位置
#define MUZZLE_X 20.0f
#define MUZZLE_Y 92.0f

void CBoss::ShootFromNozzle(float speed)
{
    // 砲弾から弾の発射
    for(int i = 0; i < 2; i++){
        // 砲台口の中心位置を計算
        float xx = -sinf(bangle[i]) * MUZZLE_Y;
        float yy = cosf(bangle[i]) * MUZZLE_Y;
        if(i == 0){
            xx += x - BUTTERY_X;
        }else{
            xx += x + BUTTERY_X;
        }
        yy += y + BUTTERY_Y;

        // 弾を発射
        AppendObject(
            new CEnemyBullet(xx, yy, bangle[i] + d2r(90.0f), speed),
            ENEMYBULLET_PRIORITY, true);
    }
}
 以下のサンプルムービーでは、30フレームごとに1発、砲台から発射する、つまり、ShootFromNozzle()を実行するようにプログラムしています。