GoogleAdsence

弾幕シューティングゲーム制作その14 ボムの追加

2020年1月9日

前回でゲームの骨格は完成したので、今回はボムを作っていきます。

ボムに必要な処理

  • ボムキーを押すとボムを生成
  • 画像の読み込み
  • 縦線と横線の計算と描画
  • キャラの計算と描画
  • 後ろから前に来る弾の計算と描画
  • 敵とボスにダメージを与える
  • 自キャラを一定時間無敵にする

ではさっそく画像の読み込みから追加
便利な文字列補完を使って一気に読み込みます。余談ですが、こういう細かい機能がたくさん追加されて、C#は本当に使いやすくなりました。

public int[] ImageBom = new int[4];
string[] bom_str = { "dat/img/effect/bom0.png", "dat/img/effect/bom1.png", "dat/img/char/body.png", "dat/img/bullet/bom_title0.png" };
for (int i = 0; i < ImageBom.Length; ++i)
{
    ImageBom[i] = DX.LoadGraph(bom_str[i]);
    if (ImageBom[i] == -1)
    {
        DX.LoadGraph($"{bom_str[i]}");
        MessageBox.Show($"{bom_str[i]}が開けませんでした");
    }
}

次にボールとキャラと縦線と横線をエフェクトクラスに追加します。

public class CCreateBomEffect : CEffect
{
    int Image = 0;
    public CCreateBomEffect(double x, double y) : base(x, y)
    {   
        // ボム時(キャラ)
        SGP.EffectBuf.Add(new CCreateCharaEffect());
        // ボム時(縦線)
        SGP.EffectBuf.Add(new CCreateLineEffect(300, 300, PI / 2, -PI / 2));
        // ボム時(横線)
        SGP.EffectBuf.Add(new CCreateLineEffect(100, 370, 0, 0));
        SGP.PlaySound(9);
    }
    public override void Move()
    {
        if(Cnt%10 == 0)
        {
            int n = Cnt / 10;
            if(n < 4)
            {
                double[] shot_angle = { 0, PI, PI / 2, PI * 1.5 };//4発エフェクトが飛ぶ角度
                Image = SGP.ImageBom[(Cnt / 10) / 3];//画像
                SGP.EffectBuf.Add(new CCreateBallBomEffect(X, Y, 13 + SGP.Rang(2), SGP.Rang(PI), shot_angle[n] - PI / 4, Image));
                SGP.PlaySound(10);
            }
        }
        ++Cnt;
        if (Cnt < 40)
            SGP.Bright = 255 - Cnt * 5;//画面の明るさ設定(暗く)
        if (Cnt > 90)
            SGP.Bright = 255 - 40 * 5 + (Cnt - 90) * 5;//画面の明るさ設定(明るく)
        if (Cnt > 130)
        {
            Life = 0;
            SGP.Bright = 255;
        }
    }
}

public class CCreateBallBomEffect : CEffect
{
    public double Speed;
    public double Angle, MvAngle;
    public int Image = 0;
    double Size = 0;
    double Bright = 0;
    public CCreateBallBomEffect(double x, double y, double speed, double angle, double mv_angle, int image) : base(x, y)
    {
        Speed = speed;
        Angle = angle;
        MvAngle = mv_angle;
        Image = image;
    }
    public override void Move()
    {
        X += Math.Cos(MvAngle) * Speed;
        Y += Math.Sin(MvAngle) * Speed;
        //スピード計算
        if (Cnt < 60)
            Speed -= (0.2 + Cnt * Cnt / 3000.0);
        if (Cnt == 60)
        {
            Speed = 0;
            //se_flag[15] = 1;
            //enter_dn(11, 20);//(45)
        }
        //明るさと大きさ計算
        Size += 0.015;
        if (Cnt < 51)
            Bright += 5;
        if (Cnt >= 60)
        {
            Size += 0.04;
            Bright -= 255 / 30.0;
        }
        //カウントアップと消去計算
        Cnt++;
        if (Cnt >= 90)
            Life = 0;
    }
    public override void Draw()
    {
        DX.SetDrawBlendMode(DX.DX_BLENDMODE_ALPHA, (int)Bright);
        DX.DrawRotaGraphF((float)X, (float)Y, Size, Angle, Image, 1);
        DX.SetDrawBlendMode(DX.DX_BLENDMODE_NOBLEND, 0);
    }
}

public class CCreateCharaEffect : CEffect
{
    public double Speed;
    public double Angle, MvAngle;
    public int Image;
    double Size;
    double Bright;
    public CCreateCharaEffect() : base(190.0, 270.0)
    {
        Image = SGP.ImageBom[2];
        Speed = 0.7;
        Angle = 0;
        MvAngle = -PI / 2;
        Size = 1.0;
        Bright = 0;
    }
    public override void Move()
    {
        X += Math.Cos(MvAngle) * Speed;
        Y += Math.Sin(MvAngle) * Speed;
        //明るさ計算
        if (Cnt < 51)
            Bright += 4;
        if (Cnt > 130 - 51)
            Bright -= 4;
        //カウントアップと消去計算
        Cnt++;
        if (Cnt >= 130)
            Life = 0;
    }
    public override void Draw()
    {
        DX.SetDrawBlendMode(DX.DX_BLENDMODE_ALPHA, (int)Bright);
        DX.DrawRotaGraphF((float)X, (float)Y, Size, Angle, Image, 1);
        DX.SetDrawBlendMode(DX.DX_BLENDMODE_NOBLEND, 0);
    }
}
public class CCreateLineEffect : CEffect
{
    public double Speed;
    public double Angle, MvAngle;
    public int Image;
    double Size;
    double Bright;
    public CCreateLineEffect(double x, double y, double angle, double mv_angle) : base(x,y)
    {
        Angle = angle;
        MvAngle = mv_angle;
        Bright = 0;
        Speed = 1.0;
        Size = 1.0;
        Image = SGP.ImageBom[3];
    }
    public override void Move()
    {
        X += Math.Cos(MvAngle) * Speed;
        Y += Math.Sin(MvAngle) * Speed;
        //明るさ計算
        if (Cnt < 51)
            Bright += 2;
        if (Cnt > 130 - 51)
            Bright -= 2;
        //カウントアップと消去計算
        Cnt++;
        if (Cnt >= 130)
            Life = 0;
    }
    public override void Draw()
    {
        DX.SetDrawBlendMode(DX.DX_BLENDMODE_ALPHA, (int)Bright);
        DX.DrawRotaGraphF((float)X, (float)Y, Size, Angle, Image, 1);
        DX.SetDrawBlendMode(DX.DX_BLENDMODE_NOBLEND, 0);
    }
}

CCreateBomEffecttがすべてのエフェクトを生成する中心となるクラスで初期化処理でキャラと縦線と横線の3つのエフェクトを追加して、Move関数では、10フレームに1回のペースで4回CCreateBallBomEffectを生成してます。すべてのクラスがCEffectクラスから派生して、Move関数とDraw関数をオーバーライドしてます。

描画とアルファブレンド

描画はいつも通りDrawRotaGraphを使用してますが、それと新たにSetDrawBlendModeを使ってます。
そして今回は、DX_BLENDMODE_ALPHAで透明度を指定してますが、他にも減算や加算や乗算ブレンドなどもできます。

ボムの生成と敵へのダメージと無敵処理

ここでは、ボムキーを押した瞬間の1フレームで無敵状態でないときにボムのエフェクトを発生させ、敵とボスとの当たり判定とダメージの計算をしてます。

if(GetPadKey(EINPUT_TYPE.Bom) == 1 && !MutekiFlag)
{
    SGP.EffectBuf.Add(new CCreateBomEffect(X, Y));
    // 敵との当たり判定
    foreach (var enemy in SGP.Enemy)
    {
        enemy.Life = 0;
        SGP.Score += enemy.EnemyStatus.Score;
        SGP.EffectBuf.Add(new CEnemyCrashEffectFactory(enemy.X, enemy.Y));
        SGP.ItemBuf.Add(new CItem(enemy.X, enemy.Y));
    }
    foreach (var boss in SGP.Boss)
    {
        boss.Life -= 20;
    }
    MutekiFlag = true;
    Cnt = 0;
}

実行結果

今回のプロジェクトをダウンロードする。 プロジェクトに画像は同梱してませんのでこちらからダウンロードしてください。画像を置く階層はデバッグモードで実行するなら、bin/Debugでリリースモードで実行するならbin/Releaseです。

これでようやく、会話イベント用のスクリプトとステージの分岐だけになりました。
その後はひたすら弾幕を作る講座とする予定です!