アマゾンバナーリンク

弾幕シューティングゲーム制作その8 自機がやられた時復活する処理

前回はコマンドパターンを使って、ファイルから敵データを読み込んだり、待機時間を指定するイベントの発生する処理を実装しました。今回はプレイヤーが被弾して、やられた後に復活する処理を追加しましょう。

Listのバッファを用意する

バッファとは、データを一時的に保存しておく場所のことです。
一度バッファに保存してから、元のリストに追加する仕様に変更します。
なぜかというと、foreach文などでMove関数を呼んでる最中に、同じリストに追加や削除の変更を加えるとInvalidOperationExceptionがスローされるためです。これを回避するためにまずバッファを用意します。

public List<CPlayer> Player = new List<CPlayer>();
public List<CEnemy> Enemy = new List<CEnemy>();
public List<CBackGround> BackGround = new List<CBackGround>();
public List<CShot> Shot = new List<CShot>();
public List<CBullet> Bullet = new List<CBullet>();

public List<CPlayer> PlayerBuf = new List<CPlayer>();
public List<CEnemy> EnemyBuf = new List<CEnemy>();
public List<CBackGround> BackGroundBuf = new List<CBackGround>();
public List<CShot> ShotBuf = new List<CShot>();
public List<CBullet> BulletBuf = new List<CBullet>();

バッファをリストに追加

バッファをリストに追加した後、Clear()を読んで空にします。ハイライトの部分を追加してください。以下MainLoop関数。

public void MainLoop() 
{
    CommandManager.Run();

    if (0 < PlayerBuf.Count) { Player.AddRange(PlayerBuf); PlayerBuf.Clear(); }
    if (0 < EnemyBuf.Count) { Enemy.AddRange(EnemyBuf); EnemyBuf.Clear(); }
    if (0 < ShotBuf.Count) { Shot.AddRange(ShotBuf); ShotBuf.Clear(); }
    if (0 < BulletBuf.Count) { Bullet.AddRange(BulletBuf); BulletBuf.Clear(); }
    if (0 < BackGroundBuf.Count) { BackGround.AddRange(BackGroundBuf); BackGroundBuf.Clear(); }
    // ここに処理を追加する
    // Lifeが0より多いなら処理する
    foreach (var player in Player.Where(x => 0 < x.Life)) player.Move();         
    foreach (var enemy in Enemy.Where(x => 0 < x.Life)) enemy.Move();
    foreach (var shot in Shot.Where(x => 0 < x.Life)) shot.Move();
    foreach (var bullet in Bullet.Where(x => 0 < x.Life)) bullet.Move();
    foreach (var back in BackGround.Where(x => 0 < x.Life)) back.Move();

    foreach (var player in Player.Where(x => 0 < x.Life)) player.Draw();          
    foreach (var enemy in Enemy.Where(x => 0 < x.Life)) enemy.Draw();
    foreach (var shot in Shot.Where(x => 0 < x.Life)) shot.Draw();
    foreach (var bullet in Bullet.Where(x => 0 < x.Life)) bullet.Draw();
    foreach (var back in BackGround.Where(x => 0 < x.Life)) back.Draw();

    // Lifeが0以下の要素を削除する
    Player.RemoveAll(s => s.Life <= 0);
    Shot.RemoveAll(s => s.Life <= 0);
    Bullet.RemoveAll(s => s.Life <= 0);
    Enemy.RemoveAll(s => s.Life <= 0);
    BackGround.RemoveAll(s => s.Life <= 0);

    CFpsControl.DrawFps(0, 465);
    CFpsControl.FpsWait();
    // 裏画面の内容を表画面に反映する
    DX.ScreenFlip();
}

さらにCtrl+Fを押して置換機能で以下の単語を全部置換しましょう。

  • Player.Add→PlayerBuf.Add
  • Enemy.Add→EnemyBuf.Add
  • Shot.Add→ShotBuf.Add
  • Bullet.Add→BulletBuf.Add
  • BackGround.Add→BackGroundBuf.Add

Ctrl+Fを押すと以下の様なUIが表示されます。

プレイヤーがやられた時の処理の追加

追加する処理

  1. プレイヤー復帰地点から無敵状態で画面内に移動する
  2. 無敵状態の時にプレイヤーを点滅ざせる
  3. 復帰して動かせるようになってもしばらくの間は無敵状態

まず1の処理を追加するためにCPlayer.csにCRevivalPlayerクラスを追加します。

public class CRevivalPlayer : CPlayer
{
    public CRevivalPlayer(double x, double y) : base(x, y, 1)
    {

    }
    public override void Move()
    {   // 自機を点滅させる
        CalcApha();

        Cnt++;
        // 一定時間経ったらプレイヤーを復帰させる
        if (Cnt > 120)
        {               
            Life = 0;
            SGP.PlayerBuf.Add(new CPlayer(X, Y, 1, true));
        }
        else // プレイヤーを上に移動させる
        {
            Y--;
        }
    }
}

プレイヤーを点滅させる

次にプレイヤーを点滅させるためのアルファ値を計算するCalcApha関数をCPlayerクラスに追加します。

protected void CalcApha()
{
    Alpha = 130.0 + 130.0 * Math.Sin(Cnt * 0.15);
    if (Alpha > 255)
    {
        Alpha = 255;
    }
    if (Alpha < 0)
    {
        Alpha = 0;
    }
}

そして、最後にプレイヤーの基底クラスのDraw関数を点滅させられるように書き換えます。

public void Draw()
{
    DX.SetDrawBlendMode(DX.DX_BLENDMODE_ALPHA, (int)Alpha);
    // 自キャラの描画
    DX.DrawRotaGraphF((float)X, (float)Y, 1.0f, 0.0f, SGP.ImagePlayer[ImageCnt], 1);
    DX.DrawBox((int)(X+L), (int)(Y+T), (int)(X+R), (int)(Y+B), DX.GetColor(255, 0, 0), 1);
    DX.SetDrawBlendMode(DX.DX_BLENDMODE_NOBLEND, 0);
    // X,Y座標の描画
    DX.DrawString(35, 15, $"X{X},Y{Y},Cnt{Cnt}", DX.GetColor(255, 255, 255));
}

実行結果

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

自キャラのやられた処理を追加できたので、次回以降はエフェクトや背景などを追加する予定です。