次は「手詰まり」の判定を行います。
ブロックを消していくと、いつかは2個以上つながっているブロックが無くなります。
その時点で「手詰まり」となり、ゲームは終了します。
上に書いた通り、2個以上つながっているブロックが無くなった時点で「手詰まり」です。
まずは判定する関数から作りましょう。
「Samegame.h」を開き、publicメンバ関数を追加します。
//ブロックを消す void DeleteBlock(); //手詰まりチェック bool IsStalemate(); //ブロックの描画 void DrawBlock(Engine *pEngine); |
「手詰まり」になったらtrueを返す関数です。
「Stalemate」とは「手詰まり」を表す英単語です。
「Samegame.cpp」を開き、「DeleteBlock関数」の下に「IsStalemate関数」の本体を追加します。
//手詰まりチェック
bool Samegame::IsStalemate()
{
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if (m_field[i][j].m_color != NO_BLOCK) {
}
}
}
}
|
少しずつ説明しながら作ります。
フィールドの左上から右下へ向けて、全てのブロックを調べるための二重ループを用意しました。
その中で、存在しているブロックかどうかを判定しています。
存在していれば、周りを調べて隣接している同じ色のブロックを探します。
隣接している同じ色のブロックの個数が2個以上であれば「手詰まり」ではありません。
//手詰まりチェック bool Samegame::IsStalemate() { for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { if (m_field[i][j].m_color != NO_BLOCK) { ClearSelectFlag(); CheckAround(j, i, m_field[i][j].m_color); int selectBlockNum = GetSelectBlockNum(); if (selectBlockNum >= 2) { } } } } } |
「手詰まり」で無い場合はfalseを返し、「手詰まり」であればtrueを返すようにします。
//手詰まりチェック bool Samegame::IsStalemate() { for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { if (m_field[i][j].m_color != NO_BLOCK) { ClearSelectFlag(); CheckAround(j, i, m_field[i][j].m_color); int selectBlockNum = GetSelectBlockNum(); if (selectBlockNum >= 2) { return false; } } } } return true; } |
では、「SceneGame.cpp」を開き、「Update関数」から呼び出してみましょう。
//============================================================================= // シーンの実行時に繰り返し呼び出される更新処理関数 //============================================================================= void SceneGame::Update() { POINT point = m_pEngine->GetMousePosition(); m_samegame.SelectBlock(point); if (m_pEngine->GetMouseButtonSync(DIK_LBUTTON)) { m_samegame.DeleteBlock(); } if (m_samegame.IsStalemate()) { m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL); } } |
一度実行して動作を確認します。
<実行結果 クライアント領域のみ>
バグが発生しているのが分かりますか?
どのブロックも選択していないのに、網掛けが行われています。
これは「IsStalemate関数」で「CheckAround関数」を使って選択フラグを立てた結果が残っている事が原因です。
「手詰まり」判定用に「試しに」チェックしていますので、終わったらフラグをクリアする必要があります。
//手詰まりチェック bool Samegame::IsStalemate() { for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { if (m_field[i][j].m_color != NO_BLOCK) { ClearSelectFlag(); CheckAround(j, i, m_field[i][j].m_color); int selectBlockNum = GetSelectBlockNum(); if (selectBlockNum >= 2) { ClearSelectFlag(); return false; } } } } return true; } |
再度実行してみます。
<実行結果 クライアント領域のみ>
今度はカーソルをブロックの上に移動させても「網掛け」されなくなりました。
このようなバグが発生した場合、プログラムの流れをよく観察しなければなりません。
「SceneGame.cpp」の「Update関数」を見直してみましょう。
//=============================================================================
// シーンの実行時に繰り返し呼び出される更新処理関数
//=============================================================================
void SceneGame::Update()
{
POINT point = m_pEngine->GetMousePosition();
m_samegame.SelectBlock(point);
if (m_pEngine->GetMouseButtonSync(DIK_LBUTTON)) {
m_samegame.DeleteBlock();
}
if (m_samegame.IsStalemate()) {
m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL);
}
}
|
最初の「SelectBlock関数」で、マウスカーソルが指しているブロックと同じブロックを選択します。
ボタンは押していないので「DeleteBlock関数」は無視しましょう。
今回作った「IsStalemate関数」が呼び出され、「手詰まり」判定をした後に選択フラグをクリアしました。
SceneGameクラスの「Draw関数」はこの後呼び出されます。
「Draw関数」が呼び出された時点では、選択フラグはクリアされていますので「網」は描画されません。
そこで、プログラムの順番を変更します。
//============================================================================= // シーンの実行時に繰り返し呼び出される更新処理関数 //============================================================================= void SceneGame::Update() { if (m_samegame.IsStalemate()) { m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL); } POINT point = m_pEngine->GetMousePosition(); m_samegame.SelectBlock(point); if (m_pEngine->GetMouseButtonSync(DIK_LBUTTON)) { m_samegame.DeleteBlock(); } } |
先に「手詰まり」判定を行えば、選択フラグがクリアされても「SelectBlock関数」で再度選択されます。
それでは、実行してみましょう。
<実行結果 クライアント領域のみ>
上手くいくようになりました。
終了するかどうか「手詰まり」になるまで試してみてください。
今の状態は「手詰まり」になると終了シーンに切り替わるため、一気に終わってしまいます。
一呼吸おいてから終了させたいので、少しプログラムを変更しましょう。
「SceneGame.h」を開き、privateにフラグを追加します。
private:
Samegame m_samegame;
bool m_bStalemate;
};
|
「SceneGame.cpp」を開き、「Start関数」で初期値を設定します。
//============================================================================= // シーンの実行時に1度だけ呼び出される開始処理関数 //============================================================================= void SceneGame::Start() { m_pEngine->AddTexture(TEXTURE_SAMEGAME); m_samegame.Initialize(4); m_bStalemate = false; } |
「Update関数」も改良します。
//============================================================================= // シーンの実行時に繰り返し呼び出される更新処理関数 //============================================================================= void SceneGame::Update() { if (m_samegame.IsStalemate()) { m_bStalemate = true; } POINT point = m_pEngine->GetMousePosition(); m_samegame.SelectBlock(point); if (m_pEngine->GetMouseButtonSync(DIK_LBUTTON)) { if (!m_bStalemate) { m_samegame.DeleteBlock(); } else { m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL); } } } |
「手詰まり」になると終了するのではなく、フラグがtrueになります。
マウスの左ボタンを押したとき、フラグがtrueであれば終了します。
一気に終了するのではなく、最後に「ボタンを押す」と言う動作が入るようになりました。
しかし「手詰まり」かどうか一瞬では分からないので、ボタンを連打するとすぐに終わりそうです。
そこで「手詰まり」を知らせるために画像を表示させましょう。
「Samegame.h」を開き、publicメンバ関数を追加します。
//手詰まりチェック bool IsStalemate(); //ブロックの描画 void DrawBlock(Engine *pEngine); //手詰まり描画 void DrawStalemate(Engine *pEngine); |
描画用の定数もprivateに追加しましょう。
const int BLOCK_SOUR_X; //ブロックの転送元左座標 const int BLOCK_SOUR_Y; //ブロックの転送元上座標 const int BLOCK_DEST_X; //ブロックの転送先左座標 const int BLOCK_DEST_Y; //ブロックの転送先上座標 const int MESH_SOUR_X; //網掛けの転送元左座標 const int MESH_SOUR_Y; //網掛けの転送元左上座標 const int BLOCK_WIDTH; //ブロックの幅 const int BLOCK_HEIGHT; //ブロックの高さ const int STALEMATE_SOUR_X; //手詰まりの転送元左座標 const int STALEMATE_SOUR_Y; //手詰まりの転送元上座標 const int STALEMATE_DEST_X; //手詰まりの転送先左座標 const int STALEMATE_DEST_Y; //手詰まりの転送先上座標 const int STALEMATE_WIDTH; //手詰まりの幅 const int STALEMATE_HEIGHT; //手詰まりの高さ |
「Samegame.cpp」のコンストラクタ関数で定数の初期値を設定します。
Samegame::Samegame()
: BLOCK_SOUR_X(0)
, BLOCK_SOUR_Y(0)
, BLOCK_DEST_X(0)
, BLOCK_DEST_Y(0)
, MESH_SOUR_X(0)
, MESH_SOUR_Y(32)
, BLOCK_WIDTH(32)
, BLOCK_HEIGHT(32)
, STALEMATE_SOUR_X(0)
, STALEMATE_SOUR_Y(96)
, STALEMATE_DEST_X(194)
, STALEMATE_DEST_Y(224)
, STALEMATE_WIDTH(252)
, STALEMATE_HEIGHT(32)
{
}
|
「DrawBlock関数」の下に「DrawStalemate関数」の本体を追加します。
//手詰まり描画
void Samegame::DrawStalemate(Engine *pEngine)
{
SetRect(&m_sour,
STALEMATE_SOUR_X,
STALEMATE_SOUR_Y,
STALEMATE_SOUR_X + STALEMATE_WIDTH,
STALEMATE_SOUR_Y + STALEMATE_HEIGHT);
SetRect(&m_dest,
STALEMATE_DEST_X,
STALEMATE_DEST_Y,
STALEMATE_DEST_X + STALEMATE_WIDTH,
STALEMATE_DEST_Y + STALEMATE_HEIGHT);
pEngine->Blt(&m_dest, TEXTURE_SAMEGAME, &m_sour);
}
|
最後に「SceneGame.cpp」を開き「Draw関数」から呼び出します。
//============================================================================= // シーンの実行時に繰り返し呼び出される描画処理関数 //============================================================================= void SceneGame::Draw() { m_samegame.DrawBlock(m_pEngine); if (m_bStalemate) { m_samegame.DrawStalemate(m_pEngine); } } |
実行して「手詰まり」にしてみましょう。
<実行結果 クライアント領域のみ>
この状態で、マウスの左ボタンを押したら終了します。