★スライドパズル 結果シーン★


最後に結果シーンを追加します。

新しいシーンとして「SceneResult」を追加してください。(そろそろ慣れてきましたか?)

使う画像は「タイトル画像」ですから、すでに追加されています。

画像の左下に、結果を表示する背景がありますので転送してみましょう。

「SceneResult.h」を開き、定数を追加します。

private:

    const int RESULT_SOUR_X; //転送元左座標
    const int RESULT_SOUR_Y; //転送元上座標

    const int RESULT_DEST_X; //転送先左座標
    const int RESULT_DEST_Y; //転送先上座標

    const int RESULT_WIDTH;  //画像の幅
    const int RESULT_HEIGHT; //画像の高さ
};

「SceneResult.cpp」を開き、コンストラクタ関数で初期値を代入します。

//=============================================================================
// コンストラクタ
// 引 数:Engine* エンジンクラスのアドレス
//=============================================================================
SceneResult::SceneResult(Engine *pEngine) : Scene(pEngine)
    , RESULT_SOUR_X(0)
    , RESULT_SOUR_Y(544)
    , RESULT_DEST_X(0)
    , RESULT_DEST_Y(0)
    , RESULT_WIDTH(640)
    , RESULT_HEIGHT(480)
{

}

「Draw関数」で画像を転送します。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneResult::Draw()
{
    SetRect(&m_sour,
        RESULT_SOUR_X,
        RESULT_SOUR_Y,
        RESULT_SOUR_X + RESULT_WIDTH,
        RESULT_SOUR_Y + RESULT_HEIGHT);

    SetRect(&m_dest, 
        RESULT_DEST_X, 
        RESULT_DEST_Y, 
        RESULT_DEST_X + RESULT_WIDTH, 
        RESULT_DEST_Y + RESULT_HEIGHT);

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

このまま実行してもゲームシーンから結果シーンに遷移するコードを書いていないので何も起こりません。

一度ゲームシーンに戻って、ゲームクリアやタイムオーバーの判定を加えましょう。


タイムオーバー判定


タイムオーバーの判定は簡単なので先に作りましょう。

残り時間が「0」になったらタイムオーバーですから「SceneGame.cpp」の「Update関数」を開きます。

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

        POINT point = m_pEngine->GetMousePosition();

        if (point.x >= 0 && point.x < PUZZLE_WIDTH && point.y >= 0 && point.y < PUZZLE_HEIGHT) {

            int x = point.x / m_panelWidth;
            int y = point.y / m_panelHeight;

            int work;

            if (y - 1 >= 0) {
                if (m_pField[(y - 1) * m_gameData.m_split + x] == 0) {
                    work = m_pField[(y - 1) * m_gameData.m_split + x];
                    m_pField[(y - 1) * m_gameData.m_split + x] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }

            if (x + 1 < m_gameData.m_split) {
                if (m_pField[y * m_gameData.m_split + (x + 1)] == 0) {
                    work = m_pField[y * m_gameData.m_split + (x + 1)];
                    m_pField[y * m_gameData.m_split + (x + 1)] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }

            if (y + 1 < m_gameData.m_split) {
                if (m_pField[(y + 1) * m_gameData.m_split + x] == 0) {
                    work = m_pField[(y + 1) * m_gameData.m_split + x];
                    m_pField[(y + 1) * m_gameData.m_split + x] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }

            if (x - 1 >= 0) {
                if (m_pField[y * m_gameData.m_split + (x - 1)] == 0) {
                    work = m_pField[y * m_gameData.m_split + (x - 1)];
                    m_pField[y * m_gameData.m_split + (x - 1)] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }
        }
    }

    if (m_timeRemain > 0) {
        if (m_imSpan.GetTiming()) {
            m_timeRemain--;
        }
    }
    else {
        m_nowSceneData.Set(Common::SCENE_RESULT, false, NULL);
    }
}

実行して時間切れを待ち、結果画面が表示されるかどうか調べます。

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


クリア判定


ゲームクリアは正しい順番でパネルが配置された状態かどうかを調べる事で分かります。

「3×3の9分割」の場合、正しい順番とは↓のような状態を指します。

イメージは2次元配列ですが、実際には1次元配列に番号が入っています。

この状態を表にすると、

フィールド番号
m_pField[0]0
m_pField[1]1
m_pField[2]2
m_pField[3]3
m_pField[4]4
m_pField[5]5
m_pField[6]6
m_pField[7]7
m_pField[8]8

となり、添え字と中身が同じ状態かどうかを調べれば良い事が分かります。

「SceneGame.cpp」の「Update関数」を開いてください。

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

        POINT point = m_pEngine->GetMousePosition();

        if (point.x >= 0 && point.x < PUZZLE_WIDTH && point.y >= 0 && point.y < PUZZLE_HEIGHT) {

            int x = point.x / m_panelWidth;
            int y = point.y / m_panelHeight;

            int work;

            if (y - 1 >= 0) {
                if (m_pField[(y - 1) * m_gameData.m_split + x] == 0) {
                    work = m_pField[(y - 1) * m_gameData.m_split + x];
                    m_pField[(y - 1) * m_gameData.m_split + x] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }

            if (x + 1 < m_gameData.m_split) {
                if (m_pField[y * m_gameData.m_split + (x + 1)] == 0) {
                    work = m_pField[y * m_gameData.m_split + (x + 1)];
                    m_pField[y * m_gameData.m_split + (x + 1)] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }

            if (y + 1 < m_gameData.m_split) {
                if (m_pField[(y + 1) * m_gameData.m_split + x] == 0) {
                    work = m_pField[(y + 1) * m_gameData.m_split + x];
                    m_pField[(y + 1) * m_gameData.m_split + x] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }

            if (x - 1 >= 0) {
                if (m_pField[y * m_gameData.m_split + (x - 1)] == 0) {
                    work = m_pField[y * m_gameData.m_split + (x - 1)];
                    m_pField[y * m_gameData.m_split + (x - 1)] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }
        }
    }

    if (m_timeRemain > 0) {
        if (m_imSpan.GetTiming()) {
            m_timeRemain--;
        }
    }
    else {
        m_nowSceneData.Set(Common::SCENE_RESULT, false, NULL);
    }

    bool bFinish = true;

    for (int i = 0; i < m_gameData.m_split * m_gameData.m_split; i++) {
        if (m_pField[i] != i) {
            bFinish = false;
        }
    }

    if (bFinish) {
        m_nowSceneData.Set(Common::SCENE_RESULT, false, NULL);
    }
}

添え字と中身が1つでも違ったら完成フラグをfalseにします。

実行して、完成させてみてください。

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


結果の表示を変える


結果画面は中央に「穴」があいており、「Clear!」か「Timeover」の画像をはめ込めるよう担っています。

クリアしたのかタイムオーバーかは、ゲームシーンでしか分かりませんので共用データを追加しましょう。

「GameData.h」を開き、フラグを1つ追加しましょう。

//-----------------------------------------------------------------------------
// 複数のシーンで共用するデータを構造体にまとめます。
//-----------------------------------------------------------------------------
struct CommonData {

    int m_split; //縦横の分割数

    std::string m_strGraphicName; //画像ファイル名

    bool m_bClear; //クリアフラグ
};

「SceneGame.cpp」の「Update関数」でシーンが切り替わる前にフラグの値をセットします。

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

        POINT point = m_pEngine->GetMousePosition();

        if (point.x >= 0 && point.x < PUZZLE_WIDTH && point.y >= 0 && point.y < PUZZLE_HEIGHT) {

            int x = point.x / m_panelWidth;
            int y = point.y / m_panelHeight;

            int work;

            if (y - 1 >= 0) {
                if (m_pField[(y - 1) * m_gameData.m_split + x] == 0) {
                    work = m_pField[(y - 1) * m_gameData.m_split + x];
                    m_pField[(y - 1) * m_gameData.m_split + x] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }

            if (x + 1 < m_gameData.m_split) {
                if (m_pField[y * m_gameData.m_split + (x + 1)] == 0) {
                    work = m_pField[y * m_gameData.m_split + (x + 1)];
                    m_pField[y * m_gameData.m_split + (x + 1)] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }

            if (y + 1 < m_gameData.m_split) {
                if (m_pField[(y + 1) * m_gameData.m_split + x] == 0) {
                    work = m_pField[(y + 1) * m_gameData.m_split + x];
                    m_pField[(y + 1) * m_gameData.m_split + x] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }

            if (x - 1 >= 0) {
                if (m_pField[y * m_gameData.m_split + (x - 1)] == 0) {
                    work = m_pField[y * m_gameData.m_split + (x - 1)];
                    m_pField[y * m_gameData.m_split + (x - 1)] = m_pField[y * m_gameData.m_split + x];
                    m_pField[y * m_gameData.m_split + x] = work;
                }
            }
        }
    }

    if (m_timeRemain > 0) {
        if (m_imSpan.GetTiming()) {
            m_timeRemain--;
        }
    }
    else {
        m_gameData.m_bClear = false;
        m_nowSceneData.Set(Common::SCENE_RESULT, false, NULL);
    }

    bool bFinish = true;

    for (int i = 0; i < m_gameData.m_split * m_gameData.m_split; i++) {
        if (m_pField[i] != i) {
            bFinish = false;
        }
    }

    if (bFinish) {
        m_gameData.m_bClear = true;
        m_nowSceneData.Set(Common::SCENE_RESULT, false, NULL);
    }
}

「SceneResult.h」を開き、座標用の定数を追加します。

private:

    const int RESULT_SOUR_X; //転送元左座標
    const int RESULT_SOUR_Y; //転送元上座標

    const int RESULT_DEST_X; //転送先左座標
    const int RESULT_DEST_Y; //転送先上座標

    const int RESULT_WIDTH;  //画像の幅
    const int RESULT_HEIGHT; //画像の高さ

    const int CLEAR_X; //クリア画像の左座標
    const int CLEAR_Y; //クリア画像の上座標

    const int TIMEOVER_X; //タイムオーバー画像の左座標
    const int TIMEOVER_Y; //タイムオーバー画像の上座標

    const int MESSAGEAREA_X; //メッセージ表示領域の左座標
    const int MESSAGEAREA_Y; //メッセージ表示領域の上座標

    const int MESSAGE_WIDTH;  //画像の幅
    const int MESSAGE_HEIGHT; //画像の高さ
};

「SceneResult.cpp」を開き、コンストラクタ関数で初期値を代入します。

//=============================================================================
// コンストラクタ
// 引 数:Engine* エンジンクラスのアドレス
//=============================================================================
SceneResult::SceneResult(Engine *pEngine) : Scene(pEngine)
    , RESULT_SOUR_X(0)
    , RESULT_SOUR_Y(544)
    , RESULT_DEST_X(0)
    , RESULT_DEST_Y(0)
    , RESULT_WIDTH(640)
    , RESULT_HEIGHT(480)
    , CLEAR_X(640)
    , CLEAR_Y(544)
    , TIMEOVER_X(640)
    , TIMEOVER_Y(744)
    , MESSAGEAREA_X(128)
    , MESSAGEAREA_Y(140)
    , MESSAGE_WIDTH(384)
    , MESSAGE_HEIGHT(200)
{

}

「Draw関数」で画像を転送します。

クリアフラグの値によって、転送元座標を切り替えます。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneResult::Draw()
{
    SetRect(&m_sour,
        RESULT_SOUR_X,
        RESULT_SOUR_Y,
        RESULT_SOUR_X + RESULT_WIDTH,
        RESULT_SOUR_Y + RESULT_HEIGHT);

    SetRect(&m_dest, 
        RESULT_DEST_X, 
        RESULT_DEST_Y, 
        RESULT_DEST_X + RESULT_WIDTH, 
        RESULT_DEST_Y + RESULT_HEIGHT);

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

    if (m_gameData.m_bClear) {
        SetRect(&m_sour,
            CLEAR_X, 
            CLEAR_Y, 
            CLEAR_X + MESSAGE_WIDTH,
            CLEAR_Y + MESSAGE_HEIGHT);
    }
    else {
        SetRect(&m_sour, 
            TIMEOVER_X, 
            TIMEOVER_Y, 
            TIMEOVER_X + MESSAGE_WIDTH, 
            TIMEOVER_Y + MESSAGE_HEIGHT);
    }

    SetRect(&m_dest, 
        MESSAGEAREA_X,
        MESSAGEAREA_Y, 
        MESSAGEAREA_X + MESSAGE_WIDTH,
        MESSAGEAREA_Y + MESSAGE_HEIGHT);

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

実行してみましょう。

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

クリア

タイムオーバー


タイトルシーンへ戻す


最後はタイトルシーンへ戻しましょう。

ボタンを押すのではなく、時間計測をやりましたので時間の経過でタイトルに戻るように作ります。

「SceneResult.h」を開き、「IntervalManageクラス」を追加します。

private:

    const int RESULT_SOUR_X; //転送元左座標
    const int RESULT_SOUR_Y; //転送元上座標

    const int RESULT_DEST_X; //転送先左座標
    const int RESULT_DEST_Y; //転送先上座標

    const int RESULT_WIDTH;  //画像の幅
    const int RESULT_HEIGHT; //画像の高さ

    const int CLEAR_X; //クリア画像の左座標
    const int CLEAR_Y; //クリア画像の上座標

    const int TIMEOVER_X; //タイムオーバー画像の左座標
    const int TIMEOVER_Y; //タイムオーバー画像の上座標

    const int MESSAGEAREA_X; //メッセージ表示領域の左座標
    const int MESSAGEAREA_Y; //メッセージ表示領域の上座標

    const int MESSAGE_WIDTH;  //画像の幅
    const int MESSAGE_HEIGHT; //画像の高さ

    IntervalManage m_imWait; //時間計測用
};

「SceneResult.cpp」の「Start関数」で計測時間を設定します。

3秒でタイトル画面に戻したいので「3000」を設定しましょう。

//=============================================================================
// シーンの実行時に1度だけ呼び出される開始処理関数
//=============================================================================
void SceneResult::Start()
{
    m_imWait.SetInterval(3000);
}

「Update関数」でシーン切り替えのタイミングを待ちます。

//=============================================================================
// シーンの実行時に繰り返し呼び出される更新処理関数
//=============================================================================
void SceneResult::Update()
{
    if (m_imWait.GetTiming()) {
        m_nowSceneData.Set(Common::SCENE_TITLE, false, NULL);
    }
}

これで完成ですので、実行して確かめてみてください。


次へ

戻る

目次へ