★さめがめ 手詰まり判定★


次は「手詰まり」の判定を行います。

ブロックを消していくと、いつかは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であれば終了します。

一気に終了するのではなく、最後に「ボタンを押す」と言う動作が入るようになりました。

しかし「手詰まり」かどうか一瞬では分からないので、ボタンを連打するとすぐに終わりそうです。

そこで「手詰まり」を知らせるために画像を表示させましょう。


DrawStalemate関数


「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);
    }
}

実行して「手詰まり」にしてみましょう。

<実行結果 クライアント領域のみ>

この状態で、マウスの左ボタンを押したら終了します。


次へ

戻る

目次へ