★ハイアンドロー★


今回は「ハイアンドロー」や「ビッグオアスモール」と呼ばれるゲームを作ります。

使う画像はトランプ画像です。

ルールは、まず1枚トランプを表にして置いておきます。

次のカードは伏せて置き、表にしたカードよりも数字が大きいか小さいかを当てるゲームです。

当たれば次のゲームに進み、外れたらゲームオーバーという事にします。

なお、カードの数字が同じ(引き分けの)場合は「負け」になります。


画像とフォントの準備


新しくプロジェクトファイルをコピーして、トランプ画像をプロジェクトフォルダの「Resource\\Textureフォルダ」に入れてください。

毎回の事ですが、画像とフォントが使える準備を先にやります。

「GameBase.h」で画像とフォントを定数化します。

//-----------------------------------------------------------------------------
//ゲーム中で使用するテクスチャ、BGM、SE、フォントのパス付ファイル名を書きます。
//-----------------------------------------------------------------------------
namespace KeyString
{
    const std::string TEXTURE_TRUMP = "Resource\\Texture\\Trump.png";

    const std::string FONT_GOTHIC = "MS ゴシック";
}

「SceneGame.cpp」の「Start関数」に追加、「Exit関数」に解放コードを追加します。

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

//=============================================================================
// シーンの終了時に呼び出される終了処理関数
//=============================================================================
void SceneGame::Exit()
{
    m_pEngine->ReleaseFont(FONT_GOTHIC);
    m_pEngine->ReleaseTexture(TEXTURE_TRUMP);
}

ついでに背景画像も転送しておきましょう。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    //背景の転送
    SetRect(&m_sour, 0, 1568, 640, 2048);
    SetRect(&m_dest, 0, 0, 640, 480);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
}

画面構成


今回は大きい方のトランプ画像の「スペード」のみを使います。

画面の配置としては、カードが2枚分、「ハイ(High)」または「ロー(Low)」を選択する領域が必要となります。

とりあえず配置を図にすると↓のようになります。


表のカードを転送


乱数を使ってカードを選択して転送します。

基準となるカードですから「現在のカード」と呼ぶ事にします。

まずは変数を用意しましょう。

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

private:
    
    int m_nowCard;
};

「SceneGame.cpp」を開き「Start関数」で初期値を入れます。

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

    m_nowCard = rand() % 13 + 1;
}

トランプのAからKまでを表すように、1から13までの乱数をセットしました。

この数を元に転送元座標を計算する方法は前に説明しました。

転送先は、画面構成に書いてありますので「Draw関数」に転送コードを書いてみてください。









































解答です。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    //背景の転送
    SetRect(&m_sour, 0, 1568, 640, 2048);
    SetRect(&m_dest, 0, 0, 640, 480);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
    
    //現在のカードの転送
    SetRect(&m_sour, (m_nowCard - 1) * 100, 0, (m_nowCard - 1) * 100 + 100, 150);
    SetRect(&m_dest, 146, 150, 246, 300);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
}

実行して確認してみましょう。

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


裏のカードを転送


裏のカードは「次のカード」と呼ぶ事にします。

最初は裏返しておいて、ハイかローかを選んだ後で表にします。

カードの裏は、トランプ画像のクラブの下にある↓の画像です。

座標を確認して「Draw関数」に追加してください。









































解答です。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    //背景の転送
    SetRect(&m_sour, 0, 1568, 640, 2048);
    SetRect(&m_dest, 0, 0, 640, 480);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
    
    //現在のカードの転送
    SetRect(&m_sour, (m_nowCard - 1) * 100, 0, (m_nowCard - 1) * 100 + 100, 150);
    SetRect(&m_dest, 146, 150, 246, 300);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);

    //次のカードの転送
    SetRect(&m_sour, 0, 600, 100, 750);
    SetRect(&m_dest, 394, 150, 494, 300);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
}

実行してみましょう。

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


ハイ、ローの転送


ハイとローを選択するためのグラフィックは、大きなトランプの右側に用意しています。

上の方にボタンのようなものがありますが、どれも幅「100ピクセル」高さ「50ピクセル」です。

画面構成に従って、この2つのグラフィックを転送してみてください。









































解答です。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    //背景の転送
    SetRect(&m_sour, 0, 1568, 640, 2048);
    SetRect(&m_dest, 0, 0, 640, 480);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
    
    //現在のカードの転送
    SetRect(&m_sour, (m_nowCard - 1) * 100, 0, (m_nowCard - 1) * 100 + 100, 150);
    SetRect(&m_dest, 146, 150, 246, 300);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);

    //次のカードの転送
    SetRect(&m_sour, 0, 600, 100, 750);
    SetRect(&m_dest, 394, 150, 494, 300);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);

    //Lowの転送
    SetRect(&m_sour, 1300, 100, 1400, 150);
    SetRect(&m_dest, 146, 350, 246, 400);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);

    //Highの転送
    SetRect(&m_sour, 1300, 150, 1400, 200);
    SetRect(&m_dest, 394, 350, 494, 400);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
}

実行してみましょう。

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


領域判定


では、領域判定をするためにマウスの準備をしましょう。

「SceneGame.h」を開き、privateにPOINT構造体を追加します。

private:
    
    int m_nowCard;

    POINT m_point;
};

「SceneGame.cpp」を開き、「Update関数」に判定用のコードを書きます。

判定する領域は「ハイ(High)」と「ロー(Low)」の転送先座標を見れば分かりますね。

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

        m_point = m_pEngine->GetMousePosition();

        //Lowの領域判定
        if (m_point.x >= 146 && m_point.x < 246 && m_point.y >= 350 && m_point.y < 400) {
            
        }

        //Highの領域判定
        if (m_point.x >= 394 && m_point.x < 494 && m_point.y >= 350 && m_point.y < 400) {
            
        }
    }
}

それぞれの領域を選択した場合に何をしなければならないかと言うと、

ハイとローのどちらを選んだのか、フラグに値をセットする。

選択が完了した事をフラグにセットする。

「次のカード」が何か決まっていないので、ここで決める。

この3つです。

フラグや変数の追加をしますので、「SceneGame.h」を開きましょう。

private:
    
    int m_nowCard;

    int m_nextCard;

    bool m_bLow;

    bool m_bSelected;

    POINT m_point;
};

「次のカード」と「ロー選択フラグ」「選択完了フラグ」を追加しました。

「SceneGame.cpp」を開き、「Start関数」で初期値を入れます。

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

    m_nowCard = rand() % 13 + 1;

    m_bSelected = false;
}

「選択完了フラグ」だけfalseにしておきます。

では、「Update関数」から追加していきましょう。

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

        m_point = m_pEngine->GetMousePosition();

        //Lowの領域判定
        if (m_point.x >= 146 && m_point.x < 246 && m_point.y >= 350 && m_point.y < 400) {
            m_bLow = true;
            m_bSelected = true;
            m_nextCard = rand() % 13 + 1;
        }

        //Highの領域判定
        if (m_point.x >= 394 && m_point.x < 494 && m_point.y >= 350 && m_point.y < 400) {
            m_bLow = false;
            m_bSelected = true;
            m_nextCard = rand() % 13 + 1;
        }
    }
}

これで「ハイかローどちらを選んだか」「選択が完了したか」「次のカードは何か」が決まりました。


結果の判定と表示


選択が完了したので、「次のカード」を表にする事から手を付けましょう。

「Draw関数」のコードに処理を追加します。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    //背景の転送
    SetRect(&m_sour, 0, 1568, 640, 2048);
    SetRect(&m_dest, 0, 0, 640, 480);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
    
    //現在のカードの転送
    SetRect(&m_sour, (m_nowCard - 1) * 100, 0, (m_nowCard - 1) * 100 + 100, 150);
    SetRect(&m_dest, 146, 150, 246, 300);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);

    //次のカードの転送
    if (!m_bSelected) {
        SetRect(&m_sour, 0, 600, 100, 750);
        SetRect(&m_dest, 394, 150, 494, 300);
        m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
    }
    else {
        SetRect(&m_sour, (m_nextCard - 1) * 100, 0, (m_nextCard - 1) * 100 + 100, 150);
        SetRect(&m_dest, 394, 150, 494, 300);
        m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
    }

    //Lowの転送
    SetRect(&m_sour, 1300, 100, 1400, 150);
    SetRect(&m_dest, 146, 350, 246, 400);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);

    //Highの転送
    SetRect(&m_sour, 1300, 150, 1400, 200);
    SetRect(&m_dest, 394, 350, 494, 400);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
}

実行して「ハイ」や「ロー」を押してみましょう。

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


次に結果の判定を行います。

結果については、

「ロー」を選択した場合、次のカードが現在のカードより小さければ勝ち

「ハイ」を選択した場合、次のカードが現在のカードより大きければ勝ち

それ以外はゲームオーバー

です。

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

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

        m_point = m_pEngine->GetMousePosition();

        //Lowの領域判定
        if (m_point.x >= 146 && m_point.x < 246 && m_point.y >= 350 && m_point.y < 400) {
            m_bLow = true;
            m_bSelected = true;
            m_nextCard = rand() % 13 + 1;
        }

        //Highの領域判定
        if (m_point.x >= 394 && m_point.x < 494 && m_point.y >= 350 && m_point.y < 400) {
            m_bLow = false;
            m_bSelected = true;
            m_nextCard = rand() % 13 + 1;
        }
    }

    if (m_bSelected) {
        if (m_bLow) {
            if (m_nowCard > m_nextCard) {
                m_nowCard = m_nextCard;
                m_bSelected = false;
            }
            else {
                m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL);
            }
        }
        else {
            if (m_nowCard < m_nextCard) {
                m_nowCard = m_nextCard;
                m_bSelected = false;
            }
            else {
                m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL);
            }
        }
    }
}

選択が完了したら判定を行っていますが、少し長いので部分的に見てみましょう。

if (m_bLow) {
    if (m_nowCard > m_nextCard) {
        m_nowCard = m_nextCard;
        m_bSelected = false;
    }
    else {
        m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL);
    }
}

「ロー」が選択されていた場合、「現在のカード」>「次のカード」なら勝ちです。

勝った場合は、「次のカード」を「現在のカード」に代入し基準を変えます。

その後「選択完了フラグ」をfalseにして、再度選択可能にしています。

「ハイ」の場合は勝ちの条件が逆なだけですね。

実行して正しく動作するかどうか試してください。


ダブルアップ


「ハイアンドロー」はダブルアップと組み合わせて作られる事が多いです。

ダブルアップとは「勝った場合掛け金が2倍になる」と言うルールです。

今回は掛け金(スコア)を「1」としてどこまでダブルアップ出来るかと言うゲームにしてみます。

まずはスコアを格納する変数を作るため「SceneGame.h」を開いてください。

private:
    
    int m_nowCard;

    int m_nextCard;

    bool m_bLow;

    bool m_bSelected;

    POINT m_point;

    int m_score;
};

「SceneGame.cpp」を開き「Start関数」で初期値「1」を入れておきましょう。

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

    m_nowCard = rand() % 13 + 1;

    m_bSelected = false;

    m_score = 1;
}

勝った場合にスコアを2倍するコードを「Update関数」に挿入します。

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

        m_point = m_pEngine->GetMousePosition();

        //Lowの領域判定
        if (m_point.x >= 146 && m_point.x < 246 && m_point.y >= 350 && m_point.y < 400) {
            m_bLow = true;
            m_bSelected = true;
            m_nextCard = rand() % 13 + 1;
        }

        //Highの領域判定
        if (m_point.x >= 394 && m_point.x < 494 && m_point.y >= 350 && m_point.y < 400) {
            m_bLow = false;
            m_bSelected = true;
            m_nextCard = rand() % 13 + 1;
        }
    }

    if (m_bSelected) {
        if (m_bLow) {
            if (m_nowCard > m_nextCard) {
                m_nowCard = m_nextCard;
                m_bSelected = false;
                m_score *= 2;
            }
            else {
                m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL);
            }
        }
        else {
            if (m_nowCard < m_nextCard) {
                m_nowCard = m_nextCard;
                m_bSelected = false;
                m_score *= 2;
            }
            else {
                m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL);
            }
        }
    }
}

最後に「Draw関数」でスコアを転送します。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    //背景の転送
    SetRect(&m_sour, 0, 1568, 640, 2048);
    SetRect(&m_dest, 0, 0, 640, 480);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
    
    //現在のカードの転送
    SetRect(&m_sour, (m_nowCard - 1) * 100, 0, (m_nowCard - 1) * 100 + 100, 150);
    SetRect(&m_dest, 146, 150, 246, 300);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);

    //次のカードの転送
    if (!m_bSelected) {
        SetRect(&m_sour, 0, 600, 100, 750);
        SetRect(&m_dest, 394, 150, 494, 300);
        m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
    }
    else {
        SetRect(&m_sour, (m_nextCard - 1) * 100, 0, (m_nextCard - 1) * 100 + 100, 150);
        SetRect(&m_dest, 394, 150, 494, 300);
        m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
    }

    //Lowの転送
    SetRect(&m_sour, 1300, 100, 1400, 150);
    SetRect(&m_dest, 146, 350, 246, 400);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);

    //Highの転送
    SetRect(&m_sour, 1300, 150, 1400, 200);
    SetRect(&m_dest, 394, 350, 494, 400);
    m_pEngine->Blt(&m_dest, TEXTURE_TRUMP, &m_sour);
    
    //スコアの転送
    m_pEngine->DrawPrintf(220, 420, FONT_GOTHIC, D3DCOLOR_XRGB(255, 255, 255), "SCORE = %d", m_score);
}

実行してみましょう。

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


1シーンしか無いため少々味気ないですが、シーンが増えれば色々な表現が出来るようになります。

次は配列を使ってみましょう。


次へ

戻る

目次へ