★スライドパズル ゲームシーン3★


今回は時間の計測や表示を行ってみます。


残り時間の設定と表示


まずは残り時間用の定数と変数を追加します。

「SceneGame.h」を開いてください。

private:

    const int PUZZLE_WIDTH;  //元画像の幅
    const int PUZZLE_HEIGHT; //元画像の高さ

    const int SHUFFLE; //シャッフル回数

    const int INFO_SOUR_X; //転送元左座標
    const int INFO_SOUR_Y; //転送元上座標
    const int INFO_DEST_X; //転送先左座標
    const int INFO_DEST_Y; //転送先上座標
    const int INFO_WIDTH;  //画像の幅
    const int INFO_HEIGHT; //画像の高さ

    const int ANSWER_DEST_X; //転送先左座標
    const int ANSWER_DEST_Y; //転送先上座標
    const int ANSWER_WIDTH;  //転送元左座標
    const int ANSWER_HEIGHT; //転送元左座標

    const int TIME_SPAN; //基準となる時間間隔

    int m_panelWidth;  //パネル1枚の幅
    int m_panelHeight; //パネル1枚の高さ

    int *m_pField; //フィールド

    unsigned int m_timeRemain; //残り時間
};

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

//=============================================================================
// コンストラクタ
// 引 数:Engine* エンジンクラスのアドレス
//=============================================================================
SceneGame::SceneGame(Engine *pEngine) : Scene(pEngine)
    , PUZZLE_WIDTH(480)
    , PUZZLE_HEIGHT(480)
    , SHUFFLE(100)
    , INFO_SOUR_X(0)
    , INFO_SOUR_Y(0)
    , INFO_DEST_X(480)
    , INFO_DEST_Y(0)
    , INFO_WIDTH(160)
    , INFO_HEIGHT(480)
    , ANSWER_DEST_X(490)
    , ANSWER_DEST_Y(100)
    , ANSWER_WIDTH(140)
    , ANSWER_HEIGHT(140)
    , TIME_SPAN(60)
    , m_pField(NULL)
{

}

「60」秒を1単位として残り時間を設定します。

「Srart関数」に計算式を追加します。

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

    m_panelWidth = PUZZLE_WIDTH / m_gameData.m_split;
    m_panelHeight = PUZZLE_HEIGHT / m_gameData.m_split;

    int size = m_gameData.m_split * m_gameData.m_split;

    m_pField = new int[size];

    for (int i = 0; i < size; i++) {
        m_pField[i] = i;
    }

    int x1, y1;
    int x2, y2;

    int work;

    int shuffle_num = m_gameData.m_split * SHUFFLE;

    x1 = y1 = x2 = y2 = 0;

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

        switch (rand() % 4) {

        case 0:
            if (y1 - 1 >= 0) {
                x2 = x1;
                y2 = y1 - 1;
            }
            break;

        case 1:
            if (x1 + 1 < m_gameData.m_split) {
                x2 = x1 + 1;
                y2 = y1;
            }
            break;

        case 2:
            if (y1 + 1 < m_gameData.m_split) {
                x2 = x1;
                y2 = y1 + 1;
            }
            break;

        case 3:
            if (x1 - 1 >= 0) {
                x2 = x1 - 1;
                y2 = y1;
            }
            break;
        }

        work = m_pField[y1 * m_gameData.m_split + x1];
        m_pField[y1 * m_gameData.m_split + x1] = m_pField[y2 * m_gameData.m_split + x2];
        m_pField[y2 * m_gameData.m_split + x2] = work;

        x1 = x2;
        y1 = y2;
    }

    m_timeRemain = (m_gameData.m_split - 2) * TIME_SPAN;
}

「難易度」によって残り時間を変えたいので上のような式にしました。

分割単位が「3」であれば「60秒」、「4」であれば「120秒」になります。


DrawNumber関数


残り時間を表示するのに、今回は「DrawNumber関数」を使おうと思います。

リンク先の説明文にも書いてありますが、フォントを使った文字や数値の表示はPCの環境によって変わる可能性があります。

特に数値は変化しますので、特別に関数を用意しました。

使う画像は「information.png」にある↓の部分です。

大きさに制限はありませんが、同じ高さ、同じ幅で「0〜9」と「空欄」が1つ分必要です。

今回は、幅「30ピクセル」高さ「40ピクセル」で作りました。

「DrawNumber関数」の引数は、

転送を開始するX座標。

転送を開始するY座標。

描画したい数値(正数)。

描画したい桁数。

前ゼロ制御(true 前ゼロあり、false なし)。

転送元のX座標(画像の'0'の左上座標)

転送元のY座標(画像の'0'の左上座標)

数字画像1つ分の幅

数字画像1つ分の高さ

転送元となる画像ファイルのパス付ファイル名。
GameBase.hにて定数として宣言する。

です。

「SceneGame.h」を開き、定数化出来るものは定数にします。

private:

    const int PUZZLE_WIDTH;  //元画像の幅
    const int PUZZLE_HEIGHT; //元画像の高さ

    const int SHUFFLE; //シャッフル回数

    const int INFO_SOUR_X; //転送元左座標
    const int INFO_SOUR_Y; //転送元上座標
    const int INFO_DEST_X; //転送先左座標
    const int INFO_DEST_Y; //転送先上座標
    const int INFO_WIDTH;  //画像の幅
    const int INFO_HEIGHT; //画像の高さ

    const int ANSWER_DEST_X; //転送先左座標
    const int ANSWER_DEST_Y; //転送先上座標
    const int ANSWER_WIDTH;  //転送元左座標
    const int ANSWER_HEIGHT; //転送元左座標

    const int NUMBER_DEST_X; //転送先左座標
    const int NUMBER_DEST_Y; //転送先上座標
    const int NUM_OF_DIGIT;  //桁数
    const int NUMBER_SOUR_X; //転送元左座標
    const int NUMBER_SOUR_Y; //転送元上座標
    const int NUMBER_WIDTH;  //数字1つ分の幅
    const int NUMBER_HEIGHT; //数字1つ分の高さ

    const int TIME_SPAN; //基準となる時間間隔

    int m_panelWidth;  //パネル1枚の幅
    int m_panelHeight; //パネル1枚の高さ

    int *m_pField; //フィールド

    unsigned int m_timeRemain; //残り時間
};

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

//=============================================================================
// コンストラクタ
// 引 数:Engine* エンジンクラスのアドレス
//=============================================================================
SceneGame::SceneGame(Engine *pEngine) : Scene(pEngine)
    , PUZZLE_WIDTH(480)
    , PUZZLE_HEIGHT(480)
    , SHUFFLE(100)
    , INFO_SOUR_X(0)
    , INFO_SOUR_Y(0)
    , INFO_DEST_X(480)
    , INFO_DEST_Y(0)
    , INFO_WIDTH(160)
    , INFO_HEIGHT(480)
    , ANSWER_DEST_X(490)
    , ANSWER_DEST_Y(100)
    , ANSWER_WIDTH(140)
    , ANSWER_HEIGHT(140)
    , NUMBER_DEST_X(490)
    , NUMBER_DEST_Y(360)
    , NUM_OF_DIGIT(3)
    , NUMBER_SOUR_X(160)
    , NUMBER_SOUR_Y(0)
    , NUMBER_WIDTH(30)
    , NUMBER_HEIGHT(40)
    , TIME_SPAN(60)
    , m_pField(NULL)
{

}

「Draw関数」で「DrawNumber関数」を呼び出します。

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

            if (m_pField[i * m_gameData.m_split + j] != 0) {

                SetRect(&m_sour,
                    (m_pField[i * m_gameData.m_split + j] % m_gameData.m_split) * m_panelWidth,
                    (m_pField[i * m_gameData.m_split + j] / m_gameData.m_split) * m_panelHeight,
                    (m_pField[i * m_gameData.m_split + j] % m_gameData.m_split) * m_panelWidth + m_panelWidth,
                    (m_pField[i * m_gameData.m_split + j] / m_gameData.m_split) * m_panelHeight + m_panelHeight);

                SetRect(&m_dest,
                    j * m_panelWidth,
                    i * m_panelHeight,
                    j * m_panelWidth + m_panelWidth,
                    i * m_panelHeight + m_panelHeight);

                m_pEngine->Blt(&m_dest, m_gameData.m_strGraphicName, &m_sour);
            }
        }
    }

    SetRect(&m_sour,
        INFO_SOUR_X,
        INFO_SOUR_Y,
        INFO_SOUR_X + INFO_WIDTH,
        INFO_SOUR_Y + INFO_HEIGHT);


    SetRect(&m_dest,
        INFO_DEST_X,
        INFO_DEST_Y,
        INFO_DEST_X + INFO_WIDTH,
        INFO_DEST_Y + INFO_HEIGHT);

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

    SetRect(&m_sour,
        0,
        0,
        PUZZLE_WIDTH,
        PUZZLE_HEIGHT);

    SetRect(&m_dest,
        ANSWER_DEST_X,
        ANSWER_DEST_Y,
        ANSWER_DEST_X + ANSWER_WIDTH,
        ANSWER_DEST_Y + ANSWER_HEIGHT);

    m_pEngine->Blt(&m_dest, m_gameData.m_strGraphicName, &m_sour);

    m_pEngine->DrawNumber(
        NUMBER_DEST_X,
        NUMBER_DEST_Y,
        m_timeRemain, 
        NUM_OF_DIGIT,
        false,
        NUMBER_SOUR_X,
        NUMBER_SOUR_Y,
        NUMBER_WIDTH,
        NUMBER_HEIGHT,
        TEXTURE_INFO);
}

実行してみましょう。

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

前ゼロなし、最大3桁で残り時間が表示されました。

「4×4の16分割」の方も見てみます。

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

上手くいってますね。


残り時間を減らす


次は実際に残り時間を減らしていきます。

実際に1秒に1ずつ減らす必要がありますので、時間の計測を行います。

時間の計測には「IntervalManageクラス」を用意しました。

扱う時間の単位は「ミリ秒(1000分の1秒)」です。

1秒計測したければ「1000」と設定し、1分計測したければ「60000」と設定します。

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

private:

    const int PUZZLE_WIDTH;  //元画像の幅
    const int PUZZLE_HEIGHT; //元画像の高さ

    const int SHUFFLE; //シャッフル回数

    const int INFO_SOUR_X; //転送元左座標
    const int INFO_SOUR_Y; //転送元上座標
    const int INFO_DEST_X; //転送先左座標
    const int INFO_DEST_Y; //転送先上座標
    const int INFO_WIDTH;  //画像の幅
    const int INFO_HEIGHT; //画像の高さ

    const int ANSWER_DEST_X; //転送先左座標
    const int ANSWER_DEST_Y; //転送先上座標
    const int ANSWER_WIDTH;  //転送元左座標
    const int ANSWER_HEIGHT; //転送元左座標

    const int NUMBER_DEST_X; //転送先左座標
    const int NUMBER_DEST_Y; //転送先上座標
    const int NUM_OF_DIGIT;  //桁数
    const int NUMBER_SOUR_X; //転送元左座標
    const int NUMBER_SOUR_Y; //転送元上座標
    const int NUMBER_WIDTH;  //数字1つ分の幅
    const int NUMBER_HEIGHT; //数字1つ分の高さ

    const int TIME_SPAN; //基準となる時間間隔

    int m_panelWidth;  //パネル1枚の幅
    int m_panelHeight; //パネル1枚の高さ

    int *m_pField; //フィールド

    unsigned int m_timeRemain; //残り時間

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

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

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

    m_panelWidth = PUZZLE_WIDTH / m_gameData.m_split;
    m_panelHeight = PUZZLE_HEIGHT / m_gameData.m_split;

    int size = m_gameData.m_split * m_gameData.m_split;

    m_pField = new int[size];

    for (int i = 0; i < size; i++) {
        m_pField[i] = i;
    }

    int x1, y1;
    int x2, y2;

    int work;

    int shuffle_num = m_gameData.m_split * SHUFFLE;

    x1 = y1 = x2 = y2 = 0;

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

        switch (rand() % 4) {

        case 0:
            if (y1 - 1 >= 0) {
                x2 = x1;
                y2 = y1 - 1;
            }
            break;

        case 1:
            if (x1 + 1 < m_gameData.m_split) {
                x2 = x1 + 1;
                y2 = y1;
            }
            break;

        case 2:
            if (y1 + 1 < m_gameData.m_split) {
                x2 = x1;
                y2 = y1 + 1;
            }
            break;

        case 3:
            if (x1 - 1 >= 0) {
                x2 = x1 - 1;
                y2 = y1;
            }
            break;
        }

        work = m_pField[y1 * m_gameData.m_split + x1];
        m_pField[y1 * m_gameData.m_split + x1] = m_pField[y2 * m_gameData.m_split + x2];
        m_pField[y2 * m_gameData.m_split + x2] = work;

        x1 = x2;
        y1 = y2;
    }

    m_timeRemain = (m_gameData.m_split - 2) * TIME_SPAN;

    m_imSpan.SetInterval(1000);
}

計測間隔の設定には「SetInterval関数」を使います。

今回は、1秒間隔で時間計測を行いますので「1000」をセットしました。

次に「Update関数」で1秒経過したかどうかを判断し、経過したら残り時間を減らします。

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

設定した時間が経過したかどうかは「GetTiming関数」で調べます。

残り時間が0より大きい間、1ずつ減少させていきます。

実行してみましょう。

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


次回はゲームのクリアとタイムオーバーシーンを追加します。


次へ

戻る

目次へ