★マインスイーパー パネルを開く★


ゲームを始める前準備は出来ましたので、次はパネルを開くプログラムを作ります。


パネルを閉じる


初期状態は全てのパネルが閉じている状態ですから、まずはそこから作っていきましょう。

Field構造体を作った時に、オープンフラグも作っていたと思います。

「Mine.cpp」を開き、「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;
    }
}

最初は閉じている(false)にしておきます。


閉じている時は、その画像を描画する必要があります。

画像転送用の定数を追加するため「Mine.h」を開いてください。

class Mine {
public:

    Mine();

    ~Mine();

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

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

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 PANEL_WIDTH;  //パネルの幅
    const int PANEL_HEIGHT; //パネルの高さ

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

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

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

「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)
    , PANEL_WIDTH(40)
    , PANEL_HEIGHT(40)
    , m_pField(NULL)
{

}

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

では、実行してみましょう。

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


パネルを開く


この状態がゲームの開始状態ですから、次はクリックするとパネルが開くようにしましょう。

画面のクリックや座標の変換については説明済みですので、そこは飛ばしていきます。

「SceneGame.cpp」の「Update関数」にマウスの入力コードを追加します。

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

        POINT point = m_pEngine->GetMousePosition();
    }
}

左ボタンを押した時に、座標を取得します。

「Mine.h」を開き、パネルを開くための「Open関数」のプロトタイプ宣言を追加します。

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

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 PANEL_WIDTH;  //パネルの幅
    const int PANEL_HEIGHT; //パネルの高さ

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

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

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

引数はPOINT構造体のconst参照です。

戻り値は、trueの場合「爆弾を選択」した事にします。


「Mine.cpp」に関数本体を追加しますが、まずは枠組みだけ追加します。

「Draw関数」の下に追加しましょう。

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

この関数でまずやるべきことは、範囲チェックです。

パネルの外側をクリックしても反応しないようにしなければなりません。

この関数内にチェック用のコードを書いても動きますが、他の操作の際にもチェックが必要になりますので、privateメンバ関数を新しく作ります。


「Mine.h」を開き、プロトタイプ宣言を追加します。

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

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 PANEL_WIDTH;  //パネルの幅
    const int PANEL_HEIGHT; //パネルの高さ

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

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

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

POINT構造体で座標を受け取り、範囲外であればtrueを返します。


「Mine.cpp」を開き、「GetAroundBombNum関数」の下に「IsOutsideField関数」の本体を追加します。

//フィールド外か?
bool Mine::IsOutsideField(const POINT &refPoint) const
{
    const int LIMIT_LEFT = ADJUST_X;
    const int LIMIT_RIGHT = ADJUST_X + PANEL_WIDTH * m_col;
    const int LIMIT_TOP = ADJUST_Y;
    const int LIMIT_BOTTOM = ADJUST_Y + PANEL_HEIGHT * m_row;

    return refPoint.x < LIMIT_LEFT || refPoint.x >= LIMIT_RIGHT || refPoint.y < LIMIT_TOP || refPoint.y >= LIMIT_BOTTOM;
}

上下左右の端の座標を定数化して、範囲外であればtrueを返すよう作りました。

ここでも、式を直接書くのではなく、式に名前を付けて見た目に分かりやすくしています。


「Open関数」に戻り、「IsOutsideField関数」を呼び出します。

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

    return false;
}

範囲内であった場合は、座標を計算しパネルを開かなければなりません。

続けてコードを追加しましょう。

//パネルを開く
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_bOpen = true;

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

            return true;
        }
    }

    return false;
}

引数の座標から2次元配列の添え字(x, y)に変換し、さらに1次元配列の添え字(inex)に変換しました。

該当するパネルが「開いていなかったら」、オープンフラグをtrueにしてパネルを開きます。

もし、爆弾だった場合はtrueを返しゲームオーバーとなります。


「SceneGame.cpp」の「Update関数」から「Open関数」を呼び出して確かめましょう。

//=============================================================================
// シーンの実行時に繰り返し呼び出される更新処理関数
//=============================================================================
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);
        }
    }
}

実行してみてください。

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

ルールをよく知っている方は、↑のように爆弾だけを避けてすべてのパネルを開く事が出来るはずです。

もちろん、爆弾を選択して終了するかどうかの確認も行っておいてください。


次へ

戻る

目次へ