★マインスイーパー 旗を立てる★


実際のマインスイーパーでは、爆弾だと思われる個所に「旗」を立てる事が出来ます。

もちろん、そこが爆弾かどうかは分かりませんが、旗が立っている個所はクリックしてもパネルが開かなくなります。

旗は右クリックで設置する事が出来るようにします。

また、旗が立った状態で右クリックする事で撤去出来るように作ります。

※実際には、「旗」→「?」→撤去、なのですが、今回「?」は作りません。


旗データ、関数の追加


「Mine.h」のField構造体に旗データを追加し、旗を設置する関数を宣言しましょう。

class Mine {
public:

    Mine();

    ~Mine();

    //フィールドの初期化
    void Initialize(const int row, const int col, const int bombMax);

    //フィールドの描画
    void Draw(Engine *pEngine);

    //パネルを開く
    bool Open(const POINT &refPoint);

    //旗を切り替える
    void SwitchFlag(const POINT &refPoint);

private:

    const int ADJUST_X; //調整用左座標
    const int ADJUST_Y; //調整用上座標

    const int PANEL_SOUR_X; //パネルの転送元左座標
    const int PANEL_SOUR_Y; //パネルの転送元上座標

    const int HIDE_SOUR_X; //閉じた状態の転送元左座標
    const int HIDE_SOUR_Y; //閉じた状態の転送元上座標

    const int FLAG_SOUR_X; //旗の転送元左座標
    const int FLAG_SOUR_Y; //旗の転送元上座標

    const int PANEL_WIDTH;  //パネルの幅
    const int PANEL_HEIGHT; //パネルの高さ

    enum {
        NONE,     //爆弾なし
        BOMB = 9, //爆弾
    };

    struct Field {
        int m_number; //周りにある爆弾の数(0〜8)、爆弾は9
        bool m_bOpen; //オープンフラグ
        bool m_bFlag; //旗フラグ
    };

    Field *m_pField; //フィールド

    int m_row; //縦方向のパネル数
    int m_col; //横方向のパネル数

    int m_bombMax; //爆弾の最大数

    RECT m_sour;
    RECT m_dest;

    //フィールドのクリア
    void ClearField();

    //爆弾のセット
    void SetBomb();

    //爆弾の個数のセット
    void SetNumber();

    //周りにある爆弾の数を数える
    int GetAroundBombNum(const int x, const int y);

    //フィールド外か?
    bool IsOutsideField(const POINT &refPoint) const;

    //隣接したパネルを開く(再帰)
    void OpenAdjacentPanel(const int x, const int y);
};

ついでに、画像の転送元座標の定数も宣言しておきました。

「旗」は英語で「FLAG」です。

今まで色々と使ってきたフラグとは「旗」の事です。


「Mine.cpp」のコンストラクタ関数で、定数の初期値を設定します。

Mine::Mine()
    : ADJUST_X(0)
    , ADJUST_Y(0)
    , PANEL_SOUR_X(0)
    , PANEL_SOUR_Y(0)
    , HIDE_SOUR_X(0)
    , HIDE_SOUR_Y(40)
    , FLAG_SOUR_X(40)
    , FLAG_SOUR_Y(40)
    , PANEL_WIDTH(40)
    , PANEL_HEIGHT(40)
    , m_pField(NULL)
{

}

次に「ClearField関数」で初期値を設定します。

//フィールドのクリア
void Mine::ClearField()
{
    const int FIELD_MAX = m_row * m_col;

    for (int i = 0; i < FIELD_MAX; i++) {
        m_pField[i].m_number = NONE;
        m_pField[i].m_bOpen = false;
        m_pField[i].m_bFlag = false;
    }
}

「Draw関数」で旗を描画するコードを追加します。

//フィールドの描画
void Mine::Draw(Engine *pEngine)
{
    for (int i = 0; i < m_row; i++) {
        for (int j = 0; j < m_col; j++) {

            int index = i * m_col + j;

            if (m_pField[index].m_bOpen) {

                SetRect(&m_sour,
                    PANEL_SOUR_X + m_pField[index].m_number * PANEL_WIDTH,
                    PANEL_SOUR_Y,
                    PANEL_SOUR_X + m_pField[index].m_number * PANEL_WIDTH + PANEL_WIDTH,
                    PANEL_SOUR_Y + PANEL_HEIGHT);
            }
            else {

                SetRect(&m_sour,
                    PANEL_SOUR_X + HIDE_SOUR_X,
                    PANEL_SOUR_Y + HIDE_SOUR_Y,
                    PANEL_SOUR_X + HIDE_SOUR_X + PANEL_WIDTH,
                    PANEL_SOUR_Y + HIDE_SOUR_Y + PANEL_HEIGHT);
            }

            SetRect(&m_dest,
                ADJUST_X + j * PANEL_WIDTH,
                ADJUST_Y + i * PANEL_HEIGHT,
                ADJUST_X + j * PANEL_WIDTH + PANEL_WIDTH,
                ADJUST_Y + i * PANEL_HEIGHT + PANEL_HEIGHT);

            pEngine->Blt(&m_dest, TEXTURE_MINE, &m_sour);

            if (m_pField[index].m_bFlag) {

                SetRect(&m_sour,
                    PANEL_SOUR_X + FLAG_SOUR_X,
                    PANEL_SOUR_Y + FLAG_SOUR_Y,
                    PANEL_SOUR_X + FLAG_SOUR_X + PANEL_WIDTH,
                    PANEL_SOUR_Y + FLAG_SOUR_Y + PANEL_HEIGHT);

                pEngine->Blt(&m_dest, TEXTURE_MINE, &m_sour);
            }
        }
    }
}

旗の絵は重ねて描画出来るように、背景を紫にしています。

デフォルトで紫色は描画されませんので、パネルの上に「旗」が描画されるようになります。


次に「Open関数」を変更します。

//パネルを開く
bool Mine::Open(const POINT &refPoint)
{
    if (IsOutsideField(refPoint)) {
        return false;
    }

    int x = (refPoint.x - ADJUST_X) / PANEL_WIDTH;
    int y = (refPoint.y - ADJUST_Y) / PANEL_HEIGHT;

    int index = y * m_col + x;

    if (!m_pField[index].m_bOpen && !m_pField[index].m_bFlag) {

        m_pField[index].m_bOpen = true;

        if (m_pField[index].m_number == BOMB) {

            return true;
        }
        else {

            if (m_pField[index].m_number == NONE) {

                OpenAdjacentPanel(x, y);
            }
        }
    }

    return false;
}

旗が立っているパネルは開けないようにします。


さらに「OpenAdjacentPanel関数」も変更します。

//隣接したパネルを開く(再帰)
void Mine::OpenAdjacentPanel(const int x, const int y)
{
    int index = y * m_col + x;

    m_pField[index].m_bOpen = true;

    if (m_pField[index].m_number != NONE) {
        return;
    }

    for (int i = -1; i < 2; i++) {
        for (int j = -1; j < 2; j++) {

            int wx = x + j;
            int wy = y + i;

            if (wx >= 0 && wx < m_col && wy >= 0 && wy < m_row) {

                index = wy * m_col + wx;

                if (!m_pField[index].m_bOpen && !m_pField[index].m_bFlag) {

                    OpenAdjacentPanel(wx, wy);
                }
            }
        }
    }
}

ここも同じで、旗が立っているパネルを自動的に開くのを防ぎます。


「Open関数」下に「SwitchFlag関数」の本体を追加します。

//旗を切り替える
void Mine::SwitchFlag(const POINT &refPoint)
{
    if (IsOutsideField(refPoint)) {
        return;
    }

    int x = (refPoint.x - ADJUST_X) / PANEL_WIDTH;
    int y = (refPoint.y - ADJUST_Y) / PANEL_HEIGHT;

    int index = y * m_col + x;

    if (m_pField[index].m_bFlag) {

        m_pField[index].m_bFlag = false;
    }
    else {

        if (!m_pField[index].m_bOpen) {

            m_pField[index].m_bFlag = true;
        }
    }
}

旗が立っている場合は撤去し、旗が立っていない場合はパネルが開かれていない事を確認して旗を立てます。


最後に「SceneGame.cpp」の「Update関数」にコードを追加しましょう。

//=============================================================================
// シーンの実行時に繰り返し呼び出される更新処理関数
//=============================================================================
void SceneGame::Update()
{
    if (m_pEngine->GetMouseButtonSync(DIK_LBUTTON)) {

        POINT point = m_pEngine->GetMousePosition();

        if (m_mine.Open(point)) {

            m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL);
        }
    }

    if (m_pEngine->GetMouseButtonSync(DIK_RBUTTON)) {

        POINT point = m_pEngine->GetMousePosition();

        m_mine.SwitchFlag(point);
    }
}

実行して動作を確認しましょう。

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

右クリックで旗の設置と撤去が出来る事を確認してください。

また、旗が立っているパネルは開けない事も確認しましょう。


次へ

戻る

目次へ