★マインスイーパ 爆弾の配置とフィールドの描画★


続いて「爆弾」の配置を考えます。

「爆弾」の個数も難易度に影響しますので、定数には出来ません。

変数を用意し、「Initialize関数」でフィールドの縦横サイズと同じく受け取れるよう改良しましょう。


「爆弾」の最大数


「Mine.h」を開き、クラスに変数と引数を追加します。

class Mine {
public:

    Mine();

    ~Mine();

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

private:

    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; //爆弾の最大数

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

「Mine.cpp」を開き、「Initialize関数」を変更します。

//フィールドの初期化
void Mine::Initialize(const int row, const int col, const int bombMax)
{
    m_row = row;
    m_col = col;

    m_pField = new Field[m_row * m_col];

    m_bombMax = bombMax;

    ClearField();
}

「爆弾」の配置


次に「爆弾」を配置する関数「SetBomb関数」を作りましょう。

これも「ClearField関数」と同じく、privateメンバ関数として作り「Initialize関数」から呼び出します。

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

class Mine {
public:

    Mine();

    ~Mine();

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

private:

    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; //爆弾の最大数

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

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

「Mine.cpp」を開き、「ClearField関数」の下に「SetBomb関数」の本体を追加しましょう。

//爆弾のセット
void Mine::SetBomb()
{
    const int FIELD_MAX = m_row * m_col;

    for (int i = 0; i < m_bombMax; i++) {

        int index;

        do {

            index = rand() % FIELD_MAX;

        } while (m_pField[index].m_number == BOMB);

        m_pField[index].m_number = BOMB;
    }
}

C言語編の「すごろく」のところでも書きましたが、同じ場所に重複して値をセットする事を避けています。

「爆弾」の最大数は、ゲームのクリアにも関連する重要な数です。

同じパネルに「爆弾」が複数配置されると、フィールド上の「爆弾」の個数と最大値が一致しません。

それを避けるために、配置済みの場所には配置出来ないようなコードにしています。


まだ実行は出来ませんが、「Initialize関数」から「SetBomb関数」を呼び出してみましょう。

//フィールドの初期化
void Mine::Initialize(const int row, const int col, const int bombMax)
{
    m_row = row;
    m_col = col;

    m_pField = new Field[m_row * m_col];

    m_bombMax = bombMax;

    ClearField();

    SetBomb();
}

ビルドしてエラーの有無だけはチェックしておいてください。


フィールドの描画


ちゃんと爆弾などがセットされているかどうかのチェックも含めて、フィールドの描画を作りましょう。

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

コンストラクタ関数の初期化リストで定数の初期値を設定しましょう。

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

}

「Initialize関数」の下に「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;

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

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

ここでも、先に変数「index」を用意し、添え字の計算をしています。

SetRect関数の中で、計算式を書くよりも読みやすくなっていると思います。


ゲームシーンから呼び出す


それでは実際に動かせるよう、ゲームシーンを変更します。

「SceneGame.h」を開き、Mineクラスの実体を作りましょう。

private:

    Mine m_mine;
};

「SceneGame.cpp」の「Start関数」で、Mineクラスの「Initialize関数」を呼び出します。

//=============================================================================
// シーンの実行時に1度だけ呼び出される開始処理関数
//=============================================================================
void SceneGame::Start()
{
    m_pEngine->AddTexture(TEXTURE_MINE);

    m_mine.Initialize(10, 10, 10);
}

10×10のフィールドに10個の爆弾が配置されると言う設定です。


最後に「Draw関数」から、Mineクラスの「Draw関数」を呼び出します。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    m_mine.Draw(m_pEngine);
}

実行してみましょう。

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

実行する度に爆弾の位置も変わる事を確認してください。


次へ

戻る

目次へ