はかせのラボ

私の頭の中を書いていく雑記ブログです

DirectX 敵のウェーブ処理

あいさつ

鍋がおいしい時期になりましたね。
ということで鍋を食べたはかせです。

敵の種類を作る

弾幕シューティングといえば色んな敵が表れて、
プレイヤーにたくさんの弾を打ってくるゲームです。

今までの実装でプレイヤーにたくさん弾を打つことはしてきましたが、
敵は一種類でした。これでは上に書いた定義が成立しません。
ということで増やしました。
ついでに増やしやすいようにもしました。

まずはコードです。

//敵ベースクラス
class EnemyBase :public Component,public IHP
{
public:
	EnemyBase() {};
	virtual ~EnemyBase() {};
	virtual void Initialize(DXGameObject* gameObject) override;
	virtual void Initialize() override {};
	virtual void Update() override {};
	virtual void OnCollisionEnter() override;
	//セッター
	void SetBulletPool(BulletPool* pool) { mBulletPool = pool; }
	void SetPlayer(DXGameObject* player) { mPlayer = player; }
	void SetBarrageManager(BarrageManager* manager) { mBarrageManager = manager; }
	//体力のインターフェース
	virtual void SetHP(double hp) override { mDefaultHitPoint = hp; }
	virtual double GetHP() override { return mHitPoint; }
protected:
	//弾のオブジェクトプール
	BulletPool* mBulletPool;
	//何フレーム経過したか
	int mWaitCount;
	//弾を発射するフレーム間隔
	int mCoolCount = 60;
	//体力
	double mHitPoint = 1;
	//初期体力
	double mDefaultHitPoint;
	//プレイヤー
	DXGameObject* mPlayer;
	//今セットされている弾幕
	BarrageBase* mBarrage;
	//弾幕管理クラス
	BarrageManager* mBarrageManager;
	//弾幕識別に使用
	BarrageName mBarrageName;
	//発射数
	int mShotCount;
	//弾幕切り替え
	virtual void ChangeBarrageName() {};
	//弾幕終了判定
	virtual bool IsBarrageEnd();
};

今まで実装していた敵のクラスをバラシてベースクラスを作りました。
あとは今まで通り継承先でごにょれば色んな敵が出来上がります。
特に変わったことはしてないので詳細は割愛します。

敵をウェーブで管理する

そもそもウェーブって何よって話ですよね。

大体敵ってのは
雑魚→中ボス→雑魚→ラスボス
みたいに一定の区切りで出てくると思います。
それがウェーブです。

まぁ文字より動画のが分かりやすいと思いますので動画です。
youtu.be

白い三角形が雑魚敵で、
画像ついてるやつがボスです。

雑魚を全滅させたらボスが出てきます。
本当はエフェクトとかつけるといいんですが、
まずは実装ということで。
では実装コードです。

/*変数*/
//ウェーブを管理する配列
std::vector<std::vector<DXGameObject*>> mEnemyWaveList;
//最終ウェーブか
bool mIsLastWave;
//今のウェーブ数
int mWaveCount;
//第一陣
std::vector<DXGameObject*> firstWave;
//第二陣
std::vector<DXGameObject*> secondWave;

/*ウェーブ初期処理(敵の生成と格納は省略)*/
//第一陣を初期リストに追加
for(auto game:firstWave)
{
	mAwakeObject.push_back(game);
}
//それぞれのウェーブ配列を管理配列に追加
mEnemyWaveList.push_back(firstWave);
mEnemyWaveList.push_back(secondWave);

/*ウェーブ更新処理*/
//次のウェーブへ行くか
bool isNext = true;
//現在ウェーブの敵が一体でも生存していたら次ウェーブへ行かない
for(auto game:mEnemyWaveList[mWaveCount])
{
	if (game->GetEnable()) isNext = false;
}

if(isNext)
{
	mWaveCount++;
	//最終ウェーブか
	if (mWaveCount == mEnemyWaveList.size() - 1) mIsLastWave = true;
	//もしもウェーブ数が用意している数を超えたら処理しない
	if (mWaveCount >= mEnemyWaveList.size()) return;
	//次ウェーブの敵をアクティブ化
	for (auto game : mEnemyWaveList[mWaveCount])
	{
		game->SetEnable(true);
	}
}

すごい力押しですがこんな感じです。

今回の小ネタ

//ウェーブを管理する配列
std::vector<std::vector<DXGameObject*>> mEnemyWaveList;

あんま見ない書き方だと思います(私も初めて書きましたw)
これは配列の配列とか、ジャグ配列とか言われてるやつです。
↓イメージ図
f:id:hakase0274:20181124223336p:plain

Aの[0]にはa配列があって、
その中にはa[0]と[1]があって
Aの[1]にはb配列があって、
その中にはb[0]と[1]と[2]があって・・・
といった感じで複数の配列を扱うのに便利です。

似たやつに二次元配列がありますが
あれは縦横の要素数が決まってるときには便利ですが
(パズルゲームの盤面とか)
今回のウェーブ処理のような要素数不定の時はちょっと不便です。

あとがき

ちゃくちゃくとただのプログラムから
ゲームプログラムになってってる気がします。

それにしても弾幕シューティングって
作るのもムズイですけどやるのもムズイですよね・・・
作ってて動きもタイミングも全部わかってるのに当たってしまいますw
私にはセンスがないってことですかねー・・・

まぁ私がやって楽しむために作ってるわけではないのでいいんですけどね。
それでは今回はこの辺でノシ

今回作ったものはgithubに上げました
github.com