「弾」と「敵」が衝突した際に何をするか考えます。
一般的には「弾」が命中したのですから「敵」が倒れるとかダメージを与えるようにすれば良いと思います。
今回は「敵」が倒れるように作ってみたいと思います。
「敵」が倒れるとは言え、倒れるアニメーションは用意されていませんので画面から消す事で倒したように見せます。
オブジェクトを画面から消す、と言うのは「表示を止める」のはもちろんですが動作や判定も止めなければなりません。
「弾クラス」の「発射中フラグ(m_bShooting)」が良い例です。
発射すると「発射中フラグ」が true になり、移動や描画、画面外に出る判定などを行っています。
「弾」が画面外に出たら「発射中フラグ」は false になります。
この「発射中フラグ」を「存在フラグ」と言い換えてみます。
「存在フラグ」が true の間「弾」が存在し、 false になったら存在しなくなる。
この仕組みを「敵」に当てはめても同じでしょう。
もちろん「プレイヤー」も同じです。
と言う事で、「存在フラグ」を「Baseクラス」に追加したいと思います。
「Baseクラス」に追加すれば、「プレイヤー」「弾」「敵」それぞれに「存在フラグ」が設定されます。
「Base.h」を開き「存在フラグ」を追加しましょう。
protected:
enum {
UP,
RIGHT,
DOWN,
LEFT,
};
const int WIDTH;
const int HEIGHT;
int m_x;
int m_y;
int m_direction;
RECT m_sour;
RECT m_dest;
bool m_bExist;
};
|
「Exist」とは「存在する」と言う意味です。
コンストラクタ関数での初期化は行いません。
オブジェクトによってフラグを切り替えるタイミングが異なるからです。
「プレイヤー」や「敵」は最初から存在していますが、「弾」は発射するまで存在していません。
それぞれのクラスの「Initialize関数」で初期値を設定しましょう。
まずは「弾」クラスから変更してみましょう。
「弾クラス」には「発射中フラグ」と言う「存在フラグ」がすでに用意されています。
二重に用意する必要は無いので「発射中フラグ」を「存在フラグ」に置き換えましょう。
「Shot.h」を開き、「発射中フラグ(m_bShooting)」を削除します。
private:
const int SPEED;
//移動
void Move();
};
|
「Shot.cpp」を開き、「m_bShooting」と書いてある場所を「m_bExist」に変更します。
void Shot::Initialize()
{
m_bExist = false;
}
|
void Shot::Shoot(const int x, const int y, const int direction) { if (!m_bExist) { m_x = x; m_y = y; m_bExist = true; m_direction = direction; } } |
void Shot::Draw(Engine *pEngine)
{
if (m_bExist) {
SetRect(&m_sour, 0, 0, WIDTH, HEIGHT);
SetRect(&m_dest, m_x, m_y, m_x + WIDTH, m_y + HEIGHT);
pEngine->Blt(&m_dest, TEXTURE_SHOT, &m_sour);
}
}
|
void Shot::Move() { if (m_bExist) { switch (m_direction) { case Base::UP: m_y -= SPEED; break; case Base::RIGHT: m_x += SPEED; break; case Base::DOWN: m_y += SPEED; break; case Base::LEFT: m_x -= SPEED; break; } if (m_x >= WindowSetting::WINDOW_WIDTH || m_x <= -WIDTH || m_y >= WindowSetting::WINDOW_HEIGHT || m_y <= -HEIGHT) { m_bExist = false; } } } |
これで「発射中フラグ」が「存在フラグ」に置き換わりました。
実行してみましょう。
特に動作は変わっていないはずです。
次に「敵クラス」に「存在フラグ」を設定してみましょう。
「Enemy.cpp」を開き、「Initialize関数」で初期値を設定します。
void Enemy::Initialize()
{
m_x = 576;
m_y = 256;
m_direction = LEFT;
m_imAnimation.SetInterval(500);
m_animation = 0;
m_kind = rand() % KIND_MAX;
m_bExist = true;
}
|
続いて「Draw関数」にif文を追加しましょう。
void Enemy::Draw(Engine *pEngine) { if (m_bExist) { SetRect(&m_sour, m_kind * WIDTH * 2 + m_animation * WIDTH, m_direction * HEIGHT, m_kind * WIDTH * 2 + m_animation * WIDTH + WIDTH, m_direction * HEIGHT + HEIGHT); SetRect(&m_dest, m_x, m_y, m_x + WIDTH, m_y + HEIGHT); pEngine->Blt(&m_dest, TEXTURE_ENEMY, &m_sour); } } |
これで「存在フラグ」が false の時は「敵」が描画されなくなりました。
「ChangeAnimation関数」も変更します。
void Enemy::ChangeAnimation() { if (m_bExist) { if (m_imAnimation.GetTiming()) { if (m_animation == 0) { m_animation = 1; } else { m_animation = 0; } } } } |
存在していない場合はアニメーションもしないようにします。
もう一か所「Base.cpp」の「CollideCircle関数」を開いてください。
「弾」か「敵」が存在していなければ、衝突判定を行う必要はありませんよね。
そこで次のコードを追加しておきましょう。
bool Base::CollideCircle(const Base &refTarget)
{
if (!m_bExist) {
return false;
}
if (!refTarget.m_bExist) {
return false;
}
int myRadius = WIDTH / 2;
int targetRadius = refTarget.WIDTH / 2;
int myCenterX = m_x + myRadius;
int myCenterY = m_y + myRadius;
int targetCenterX = refTarget.m_x + targetRadius;
int targetCenterY = refTarget.m_y + targetRadius;
int x = myCenterX - targetCenterX;
int y = myCenterY - targetCenterY;
double distance = sqrt(x * x + y * y);
int totalRadius = myRadius + targetRadius;
if (distance <= totalRadius) {
return true;
}
return false;
}
|
これを追加しておかなければ、「敵」が表示されていないのに「弾」が衝突してしまいます。
これで準備が出来ました。
「弾」が「敵」に当たったら、「敵」の「存在フラグ」を false にすれば「敵」が消えるはずです。
もう一度、衝突判定を行うプログラムを確認するため「SceneGame.cpp」の「Update関数」を開きましょう。
void SceneGame::Update() { m_player.Update(m_pEngine, m_shot); m_shot.Update(); m_enemy.Update(); if (m_shot.CollideCircle(m_enemy)) { } } |
このif文が成立した場合、「敵(m_enemy)」の「存在フラグ」を false にすれば良い訳です。
しかし「敵」の「存在フラグ」はprotectedメンバですから、ここでは変更出来ません。
このような場合は、フラグを操作する関数を追加します。
「存在フラグ」を false にする関数、「Delete関数」を作りましょう。
「敵」だけでなく、「弾」や「プレイヤー」も消す可能性がありますから「Baseクラス」に作るのが望ましいです。
「Base.h」を開き、「Delete関数」のプロトタイプ宣言を追加します。
class Base {
public:
Base(const int width, const int height);
bool CollideCircle(const Base &refTarget);
void Delete();
protected:
|
「Base.cpp」を開き、「CollideCircle関数」の下に「Delete関数」本体を追加しましょう。
void Base::Delete() { m_bExist = false; } |
中身は単純です。
それでは「SceneGame.cpp」の「Update関数」で「Delete関数」を呼び出してみましょう。
void SceneGame::Update()
{
m_player.Update(m_pEngine, m_shot);
m_shot.Update();
m_enemy.Update();
if (m_shot.CollideCircle(m_enemy)) {
m_enemy.Delete();
}
}
|
実行して、「敵」に「弾」を当ててください。
「敵」が消えれば成功です。
「敵」だけでなく「弾」も一緒に消す事も出来ます。
void SceneGame::Update()
{
m_player.Update(m_pEngine, m_shot);
m_shot.Update();
m_enemy.Update();
if (m_shot.CollideCircle(m_enemy)) {
m_enemy.Delete();
m_shot.Delete();
}
}
|
実行して確かめてみてください。
何とか出来上がったように見えますが、じっくり見ると「弾」が当たる前に「敵」が消えているようにも見えます。
次回は微妙な部分の確認などを行ってみましょう。