弾幕シューティングゲーム制作その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の処理を追加するために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です。
自キャラのやられた処理を追加できたので、次回以降はエフェクトや背景などを追加する予定です。