★宝探しゲーム ゲームシーン2★


前回の続きです。

「宝」の場所をクリックしたら「宝」が表示されるように作りましょう。


宝の表示


「SceneGame.cpp」の「Update関数」を変更します。

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

        POINT point = m_pEngine->GetMousePosition();

        if (point.x >= 0 && point.x < CHIP_WIDTH * COL && point.y >= 0 && point.y < CHIP_HEIGHT * ROW) {

            int x = point.x / CHIP_WIDTH;
            int y = point.y / CHIP_HEIGHT;

            if (m_field[y][x].m_status != CHECKED) {
                if (m_field[y][x].m_bTreasure) {
                    m_field[y][x].m_status = TREASURE;
                }
                else {
                    m_field[y][x].m_status = CHECKED;
                }
            }
        }
    }
}

クリックした場所が「選択済み(CHECKED)」で無かった場合、

「宝」であったら、状態を「宝(TREASURE)」に変更し、

「宝」で無かったら、状態を「選択済み(CHECKED)」にする。

実行してみましょう。

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


ゲームのルールを設定


5個の「宝」を見つけるまでの、クリック回数が必要ですから、色々付け加えましょう。

「SceneGame.h」を開き、privateに定数と変数を追加します。

private:

    const int CHIP_X;
    const int CHIP_Y;
    const int CHIP_WIDTH;
    const int CHIP_HEIGHT;

    const int TREASURE_MAX;

    const int REQUIRED; //クリアに必要な宝の数

    enum {
        UNCHECKED,
        CHECKED,
        TREASURE,
    };

    //フィールド構造体
    struct Field {
        int m_status;
        bool m_bTreasure;
    };

    enum { ROW = 10, COL = 10, };
    Field m_field[ROW][COL];

    int m_clickCount;
    int m_treasureCount;
};

「SceneGame.cpp」のコンストラクタ関数で定数の初期値を入れます。

//=============================================================================
// コンストラクタ
// 引 数:Engine* エンジンクラスのアドレス
//=============================================================================
SceneGame::SceneGame(Engine *pEngine) : Scene(pEngine)
    , CHIP_X(0)
    , CHIP_Y(480)
    , CHIP_WIDTH(32)
    , CHIP_HEIGHT(32)
    , TREASURE_MAX(10)
    , REQUIRED(5)
{

}

次に「Start関数」でm_clickCountとm_treasureCountの初期値を設定します。

//=============================================================================
// シーンの実行時に1度だけ呼び出される開始処理関数
//=============================================================================
void SceneGame::Start()
{
    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COL; j++) {
            m_field[i][j].m_status = UNCHECKED;
            m_field[i][j].m_bTreasure = false;
        }
    }

    int idxX;
    int idxY;

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

        do {

            idxX = rand() % COL;
            idxY = rand() % ROW;

        } while (m_field[idxY][idxX].m_bTreasure);

        m_field[idxY][idxX].m_bTreasure = true;
    }

    m_clickCount = 0;
    m_treasureCount = 0;
}

「Update関数」でクリック回数と「宝」を選択した回数をカウントアップします。

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

        POINT point = m_pEngine->GetMousePosition();

        if (point.x >= 0 && point.x < CHIP_WIDTH * COL && point.y >= 0 && point.y < CHIP_HEIGHT * ROW) {

            int x = point.x / CHIP_WIDTH;
            int y = point.y / CHIP_HEIGHT;

            if (m_field[y][x].m_status != CHECKED) {
                if (m_field[y][x].m_bTreasure) {
                    m_field[y][x].m_status = TREASURE;
                    m_treasureCount++;
                }
                else {
                    m_field[y][x].m_status = CHECKED;
                }

                m_clickCount++;
            }
        }
    }
}

「Draw関数」で見つけた宝の数を表示します。

フォントを(いつもの)「MS ゴシック」で20ポイントで追加してください。

※内容は書きません。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COL; j++) {

            SetRect(&m_sour,
                CHIP_X + m_field[i][j].m_status * CHIP_WIDTH,
                CHIP_Y,
                CHIP_X + m_field[i][j].m_status * CHIP_WIDTH + CHIP_WIDTH,
                CHIP_Y + CHIP_HEIGHT);

            SetRect(&m_dest, 
                j * CHIP_WIDTH,
                i * CHIP_HEIGHT,
                j * CHIP_WIDTH + CHIP_WIDTH, 
                i * CHIP_HEIGHT + CHIP_HEIGHT);

            m_pEngine->Blt(&m_dest, TEXTURE_TREASURE, &m_sour);
        }
    }

    m_pEngine->DrawPrintf(0, 400, FONT_GOTHIC, D3DCOLOR_XRGB(255, 255, 255), "発見した宝 %d", m_treasureCount);
    m_pEngine->DrawPrintf(0, 420, FONT_GOTHIC, D3DCOLOR_XRGB(255, 255, 255), "発見した宝 %d", m_treasureCount);
}

実行してみましょう。

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


終了判定


それでは終了判定を追加します。

「SceneGame.h」を開き、privateに変数を追加します。

private:

    const int CHIP_X;
    const int CHIP_Y;
    const int CHIP_WIDTH;
    const int CHIP_HEIGHT;

    const int TREASURE_MAX;

    const int REQUIRED;

    enum {
        UNCHECKED,
        CHECKED,
        TREASURE,
    };

    //フィールド構造体
    struct Field {
        int m_status;
        bool m_bTreasure;
    };

    enum { ROW = 10, COL = 10, };
    Field m_field[ROW][COL];

    int m_clickCount;
    int m_treasureCount;

    bool m_bFinish;
};

「SceneGame.cpp」の「Start関数」で初期化します。

//=============================================================================
// シーンの実行時に1度だけ呼び出される開始処理関数
//=============================================================================
void SceneGame::Start()
{
    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COL; j++) {
            m_field[i][j].m_status = UNCHECKED;
            m_field[i][j].m_bTreasure = false;
        }
    }

    int idxX;
    int idxY;

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

        do {

            idxX = rand() % COL;
            idxY = rand() % ROW;

        } while (m_field[idxY][idxX].m_bTreasure);

        m_field[idxY][idxX].m_bTreasure = true;
    }

    m_clickCount = 0;
    m_treasureCount = 0;

    m_bFinish = false;
}

「Update関数」に判定を追加します。

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

        POINT point = m_pEngine->GetMousePosition();

        if (point.x >= 0 && point.x < CHIP_WIDTH * COL && point.y >= 0 && point.y < CHIP_HEIGHT * ROW) {

            int x = point.x / CHIP_WIDTH;
            int y = point.y / CHIP_HEIGHT;

            if (m_field[y][x].m_status != CHECKED) {
                if (m_field[y][x].m_bTreasure) {
                    m_field[y][x].m_status = TREASURE;
                    m_treasureCount++;
                }
                else {
                    m_field[y][x].m_status = CHECKED;
                }

                m_clickCount++;
            }
        }
    }

    if (m_treasureCount >= REQUIRED) {
        m_bFinish = true;
    }
}

本来であれば、ここでシーンを変えて結果を表示するべきですが、まだ他の事もやりたいのでシーンは追加しません。

今回はゲームが終わったら動かないようにします。

皆さんはシーンを追加していただいても構いません!

「SceneGame.cpp」の「Update関数」を変更します。

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

        if (m_pEngine->GetMouseButtonSync(DIK_LBUTTON)) {

            POINT point = m_pEngine->GetMousePosition();

            if (point.x >= 0 && point.x < CHIP_WIDTH * COL && point.y >= 0 && point.y < CHIP_HEIGHT * ROW) {

                int x = point.x / CHIP_WIDTH;
                int y = point.y / CHIP_HEIGHT;

                if (m_field[y][x].m_status != CHECKED) {
                    if (m_field[y][x].m_bTreasure) {
                        m_field[y][x].m_status = TREASURE;
                        m_treasureCount++;
                    }
                    else {
                        m_field[y][x].m_status = CHECKED;
                    }

                    m_clickCount++;
                }
            }
        }

        if (m_treasureCount >= REQUIRED) {
            m_bFinish = true;
        }
    }
}

これで5個の宝を見つけたらゲームが止まります。

やってみてください。

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

これで完成です!


さらに


元画像を見ると左下に背景画像があります。

これを使って「見た目」を変えてみます。

今回は定数設定を考えていますので、「SceneGame.h」を開き、定数を追加します。

private:

    const int CHIP_X;
    const int CHIP_Y;
    const int CHIP_WIDTH;
    const int CHIP_HEIGHT;

    const int BACK_SOUR_X; //背景の転送元左座標
    const int BACK_SOUR_Y; //背景の転送元上座標
    const int BACK_DEST_X; //背景の転送先左座標
    const int BACK_DEST_Y; //背景の転送先上座標
    const int BACK_WIDTH;  //背景の幅
    const int BACK_HEIGHT; //背景の高さ

    const int TREASURE_MAX;

    const int REQUIRED; //クリアに必要な宝の数

    enum {
        UNCHECKED,
        CHECKED,
        TREASURE,
    };

    //フィールド構造体
    struct Field {
        int m_status;
        bool m_bTreasure;
    };

    enum { ROW = 10, COL = 10, };
    Field m_field[ROW][COL];

    int m_clickCount;
    int m_treasureCount;
};

「SceneGame.cpp」のコンストラクタ関数で初期値を入れます。

//=============================================================================
// コンストラクタ
// 引 数:Engine* エンジンクラスのアドレス
//=============================================================================
SceneGame::SceneGame(Engine *pEngine) : Scene(pEngine)
    , CHIP_X(0)
    , CHIP_Y(480)
    , CHIP_WIDTH(32)
    , CHIP_HEIGHT(32)
    , TREASURE_MAX(10)
    , REQUIRED(5)
    , BACK_SOUR_X(0)
    , BACK_SOUR_Y(544)
    , BACK_DEST_X(0)
    , BACK_DEST_Y(0)
    , BACK_WIDTH(640)
    , BACK_HEIGHT(480)
{

}

配置が変わる事も考えて定数化しました。

「Draw関数」で背景を転送しましょう。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    SetRect(&m_sour, BACK_SOUR_X, 
        BACK_SOUR_Y, 
        BACK_SOUR_X + BACK_WIDTH,
        BACK_SOUR_Y + BACK_HEIGHT);

    SetRect(&m_dest,
        BACK_DEST_X,
        BACK_DEST_Y,
        BACK_DEST_X + BACK_WIDTH,
        BACK_DEST_Y + BACK_HEIGHT);

    m_pEngine->Blt(&m_dest, TEXTURE_TREASURE, &m_sour);

    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COL; j++) {

            SetRect(&m_sour,
                CHIP_X + m_field[i][j].m_status * CHIP_WIDTH,
                CHIP_Y,
                CHIP_X + m_field[i][j].m_status * CHIP_WIDTH + CHIP_WIDTH,
                CHIP_Y + CHIP_HEIGHT);

            SetRect(&m_dest, 
                j * CHIP_WIDTH,
                i * CHIP_HEIGHT,
                j * CHIP_WIDTH + CHIP_WIDTH, 
                i * CHIP_HEIGHT + CHIP_HEIGHT);

            m_pEngine->Blt(&m_dest, TEXTURE_TREASURE, &m_sour);
        }
    }

    m_pEngine->DrawPrintf(0, 400, FONT_GOTHIC, D3DCOLOR_XRGB(255, 255, 255), "発見した宝 %d", m_treasureCount);
    m_pEngine->DrawPrintf(0, 420, FONT_GOTHIC, D3DCOLOR_XRGB(255, 255, 255), "発見した宝 %d", m_treasureCount);
}

実行してみましょう。

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

ずれてますよね。


転送位置の調整


背景は真ん中に白い領域があります。

サイズはフィールドと同じ「320×320ピクセル」です。

と言う事は転送先の開始座標は、左座標が

 (640 - 320) / 2 = 160

で、上座標が、

 (480 - 320) / 2 = 80

です。

この数値は定数化します。

「SceneGame.h」を開き、privateに定数を追加しましょう。

private:

    const int CHIP_X;
    const int CHIP_Y;
    const int CHIP_WIDTH;
    const int CHIP_HEIGHT;

    const int BACK_SOUR_X; //背景の転送元左座標
    const int BACK_SOUR_Y; //背景の転送元上座標
    const int BACK_DEST_X; //背景の転送先左座標
    const int BACK_DEST_Y; //背景の転送先上座標
    const int BACK_WIDTH;  //背景の幅
    const int BACK_HEIGHT; //背景の高さ

    const int FIELD_X; //フィールドの転送開始X座標
    const int FIELD_Y; //フィールドの転送開始Y座標

    const int TREASURE_MAX;

    const int REQUIRED; //クリアに必要な宝の数

    enum {
        UNCHECKED,
        CHECKED,
        TREASURE,
    };

    //フィールド構造体
    struct Field {
        int m_status;
        bool m_bTreasure;
    };

    enum { ROW = 10, COL = 10, };
    Field m_field[ROW][COL];

    int m_clickCount;
    int m_treasureCount;
};

「SceneGame.cpp」のコンストラクタ関数で初期値を入れます。

//=============================================================================
// コンストラクタ
// 引 数:Engine* エンジンクラスのアドレス
//=============================================================================
SceneGame::SceneGame(Engine *pEngine) : Scene(pEngine)
    , CHIP_X(0)
    , CHIP_Y(480)
    , CHIP_WIDTH(32)
    , CHIP_HEIGHT(32)
    , TREASURE_MAX(10)
    , REQUIRED(5)
    , BACK_SOUR_X(0)
    , BACK_SOUR_Y(544)
    , BACK_DEST_X(0)
    , BACK_DEST_Y(0)
    , BACK_WIDTH(640)
    , BACK_HEIGHT(480)
    , FIELD_X(160)
    , FIELD_Y(80)
{

}

「Update関数」の範囲をチェックする箇所を変更します。

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

        if (m_pEngine->GetMouseButtonSync(DIK_LBUTTON)) {

            POINT point = m_pEngine->GetMousePosition();

            if (point.x >= FIELD_X && point.x < FIELD_X + CHIP_WIDTH * COL && point.y >= FIELD_Y && point.y < FIELD_Y + CHIP_HEIGHT * ROW) {

                int x = (point.x - FIELD_X) / CHIP_WIDTH;
                int y = (point.y - FIELD_Y) / CHIP_HEIGHT;

                if (m_field[y][x].m_status != CHECKED) {
                    if (m_field[y][x].m_bTreasure) {
                        m_field[y][x].m_status = TREASURE;
                        m_treasureCount++;
                    }
                    else {
                        m_field[y][x].m_status = CHECKED;
                    }

                    m_clickCount++;
                }
            }
        }

        if (m_treasureCount >= REQUIRED) {
            m_bFinish = true;
        }
    }
}

「Draw関数」も変えます。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    SetRect(&m_sour, BACK_SOUR_X, 
        BACK_SOUR_Y, 
        BACK_SOUR_X + BACK_WIDTH,
        BACK_SOUR_Y + BACK_HEIGHT);

    SetRect(&m_dest,
        BACK_DEST_X,
        BACK_DEST_Y,
        BACK_DEST_X + BACK_WIDTH,
        BACK_DEST_Y + BACK_HEIGHT);

    m_pEngine->Blt(&m_dest, TEXTURE_TREASURE, &m_sour);

    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COL; j++) {

            SetRect(&m_sour,
                CHIP_X + m_field[i][j].m_status * CHIP_WIDTH,
                CHIP_Y,
                CHIP_X + m_field[i][j].m_status * CHIP_WIDTH + CHIP_WIDTH,
                CHIP_Y + CHIP_HEIGHT);

            SetRect(&m_dest, 
                FIELD_X + j * CHIP_WIDTH,
                FIELD_Y + i * CHIP_HEIGHT,
                FIELD_X + j * CHIP_WIDTH + CHIP_WIDTH, 
                FIELD_Y + i * CHIP_HEIGHT + CHIP_HEIGHT);

            m_pEngine->Blt(&m_dest, TEXTURE_TREASURE, &m_sour);
        }
    }

    m_pEngine->DrawPrintf(0, 400, FONT_GOTHIC, D3DCOLOR_XRGB(255, 255, 255), "発見した宝 %d", m_treasureCount);
    m_pEngine->DrawPrintf(0, 420, FONT_GOTHIC, D3DCOLOR_XRGB(255, 255, 255), "発見した宝 %d", m_treasureCount);
}

実行してみましょう。

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

出来ました!!


タイトルシーンを作って、超ミニゲームを作りました。

最初はこれくらいからで良いんです、ちょっとずつ大きなものを作れるよう頑張りましょう。


次へ

戻る

目次へ