★丁半ゲーム★


次は「丁半」のゲームを作ります。

※プロジェクトフォルダは新しく用意する事をお勧めします。

最近は時代劇を放送していないので「鉄火場(賭博場)」のシーンなど見た事が無い人が多いと思います。

「丁半」とは「ツボ振り」と言われる博打で、ツボと呼ばれるものにサイコロ2つを入れて振ってツボを伏せます。

2つのサイコロの合計が偶数であれば「丁」、奇数であれば「半」であり、これを当てるゲームです。

今回の画像は↓を使います。

実際の画像サイズは「1024×1024ピクセル」ですが、大きいので縮小表示しています。

※プロジェクトフォルダの「Resource\\Textureフォルダ」に「Dice.png」と言うファイル名で保存してください。

「丁半」の文字は枠をいれて「200×200ピクセル」、サイコロは「100×100ピクセル」で作りました。

背景画像はありませんので、背景は「黒」になります。

何のゲームを作るにしても、何もない状態からいきなり作る事はありません。

ある程度、データや素材、画面構成を考えてから作り始めるものです。

素材はありますので、最初に画面構成から考えてみましょう。


画面構成と画像転送


今回の画面構成は↓のように考えました。

これである程度転送が出来るようになりました。

まずは「丁」と「半」を転送しましょう。

画像の定数化(TEXTURE_DICE にしましょう)や追加なども含めて、皆さんでやってみてください。

フォントも20ポイントの「MS ゴシック」を用意してください。









































解答です。

まずは「GameBase.h」の定数は↓のようになっています。

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

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

次に「SceneGame.cpp」を開き「Start関数」で画像とフォントを追加します。

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

続いて「Exit関数」に解放コードを書いておきます。

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

最後に「Draw関数」で「丁」「半」を転送します。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    SetRect(&m_sour, 0, 0, 200, 200);
    SetRect(&m_dest, 80, 50, 280, 250);
    m_pEngine->Blt(&m_dest, TEXTURE_DICE, &m_sour);

    SetRect(&m_sour, 200, 0, 400, 200);
    SetRect(&m_dest, 360, 50, 560, 250);
    m_pEngine->Blt(&m_dest, TEXTURE_DICE, &m_sour);
}

一度実行してみましょう。

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


サイコロの転送


サイコロも転送先は決めています。

しかし「転送元」は、どの「目」にするか決めていません。

それぞれの「目」の左座標を表にしてみます。

(上座標は「200」で固定ですからね。)

左座標
0
100
200
300
400
500

トランプと同じく「目」から座標が計算出来そうです。

「目」の変数をdiceとすると、

 (dice - 1) * 100

これで左座標が計算出来ますね。

サイコロは2つありますから「SceneGame.h」を開き、privateに2つの変数を追加しましょう。

private:
    
    int dice1;
    int dice2;
};

「SceneGame.cpp」を開き「Start関数」で初期値を格納します。

初期値は乱数で「1から6」を設定します。

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

    dice1 = rand() % 6 + 1;
    dice2 = rand() % 6 + 1;
}

この「目」を元に「Draw関数」にサイコロを転送するコードを追加します。

上の画面構成図と計算式を元に考えてみてください。

dice1が左、dice2が右のサイコロです。









































解答です。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    SetRect(&m_sour, 0, 0, 200, 200);
    SetRect(&m_dest, 80, 50, 280, 250);
    m_pEngine->Blt(&m_dest, TEXTURE_DICE, &m_sour);

    SetRect(&m_sour, 200, 0, 400, 200);
    SetRect(&m_dest, 360, 50, 560, 250);
    m_pEngine->Blt(&m_dest, TEXTURE_DICE, &m_sour);

    SetRect(&m_sour, (dice1 - 1) * 100, 200, (dice1 - 1) * 100 + 100, 300);
    SetRect(&m_dest, 200, 315, 300, 415);
    m_pEngine->Blt(&m_dest, TEXTURE_DICE, &m_sour);

    SetRect(&m_sour, (dice2 - 1) * 100, 200, (dice2 - 1) * 100 + 100, 300);
    SetRect(&m_dest, 340, 315, 440, 415);
    m_pEngine->Blt(&m_dest, TEXTURE_DICE, &m_sour);
}

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

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


サイコロの「目」の設定


本来の手順で行けば、サイコロをツボに入れ振った後にツボを伏せておき、「丁」「半」を当てるのですが、ツボの絵はありませんのでツボ振りは出来ません。

そこで、先に「丁」か「半」かを選択してからサイコロの「目」を決定します。

ゲームの手順としては、

「丁」「半」を選択するまで、サイコロの「目」はずっと変化させます。

「丁」「半」を選択すると、サイコロの「目」が確定し判定に入ります。

「丁」を選択して、サイコロの合計が偶数であれば「当たり!」と表示します。
「丁」を選択して、サイコロの合計が奇数であればゲームオーバーです。


「半」を選択して、サイコロの合計が奇数であれば「当たり!」と表示します。
「半」を選択して、サイコロの合計が偶数であればゲームオーバーです。

これに必要な変数とフラグを追加します。

領域を選択するためのマウス座標を取得しなければなりませんので、POINT構造体が必要です。

「丁」「半」を選択したかどうかの「選択完了フラグ」も必要になります。

「丁」と「半」どちらを選んだかもフラグでチェックします。

後は当たったかどうかも「結果フラグ」を用意します。

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

private:
    
    int dice1;
    int dice2;

    POINT m_point;

    bool m_bSelected;

    bool m_bEven;

    bool m_bResult;
};

「Even」とは「偶数」の意味です。

「m_bEven」がtrueの時は「丁」を選択した事にします。

とりあえず「丁フラグ」と呼びます。


「SceneGame.cpp」を開き、「Start関数」で初期値を代入しましょう。

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

    dice1 = rand() % 6 + 1;
    dice2 = rand() % 6 + 1;

    m_bSelected = false;
}

初期値は「選択完了フラグ」だけで構いません。

選択を完了した時点で他のフラグは初期化されます。

選択が完了していない間は、サイコロの「目」は変化させますので「Update関数」に次のコードを追加します。

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

        dice1 = rand() % 6 + 1;
        dice2 = rand() % 6 + 1;
    }
}

繰り返し実行される中で、常に新しい乱数をセットしています。

実行してみてください。

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

サイコロの「目」が変わり続けていれば成功です。


次にマウス関係の処理を追加します。

前と同じように「左ボタンを押したら座標を取得する」ようにしましょう。

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

        m_point = m_pEngine->GetMousePosition();
    }

    if (!m_bSelected) {

        dice1 = rand() % 6 + 1;
        dice2 = rand() % 6 + 1;
    }
}

取得した座標が「丁」の範囲内か「半」の範囲内か調べます。

「丁」と「半」の転送先座標を見れば、それぞれの領域が分かりますよね。

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

        m_point = m_pEngine->GetMousePosition();

        //丁を選択
        if (m_point.x >= 80 && m_point.x < 280 && m_point.y >= 50 && m_point.y < 250) {

        }

        //半を選択
        if (m_point.x >= 360 && m_point.x < 560 && m_point.y >= 50 && m_point.y < 250) {

        }
    }

    if (!m_bSelected) {

        dice1 = rand() % 6 + 1;
        dice2 = rand() % 6 + 1;
    }
}

「丁」を選択した場合「丁フラグ」がtrueになり、「半」を選択した場合「丁フラグ」はfalseにします。

また、選択が終わったので「選択完了フラグ」もtrueに変える必要があります。

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

        m_point = m_pEngine->GetMousePosition();

        //丁を選択
        if (m_point.x >= 80 && m_point.x < 280 && m_point.y >= 50 && m_point.y < 250) {
            m_bEven = true;
            m_bSelected = true;
        }

        //半を選択
        if (m_point.x >= 360 && m_point.x < 560 && m_point.y >= 50 && m_point.y < 250) {
            m_bEven = false;
            m_bSelected = true;
        }
    }

    if (!m_bSelected) {

        dice1 = rand() % 6 + 1;
        dice2 = rand() % 6 + 1;
    }
}

ここで1度実行してみましょう。

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

「丁」か「半」を選ぶとサイコロが止まります。


判定


これで選択が完了したので、判定に入ります。

サイコロの「目」を合計して「偶数」か「奇数」を判定します。

「丁」を選択していて「偶数」であれば「m_bResult」をtrueにします。
「丁」を選択していて「奇数」であればゲームオーバーです。

「半」を選択していて「奇数」であれば「m_bResult」をtrueにします。
「半」を選択していて「偶数」であればゲームオーバーです。

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

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

        m_point = m_pEngine->GetMousePosition();

        //丁を選択
        if (m_point.x >= 80 && m_point.x < 280 && m_point.y >= 50 && m_point.y < 250) {
            m_bEven = true;
            m_bSelected = true;
        }

        //半を選択
        if (m_point.x >= 360 && m_point.x < 560 && m_point.y >= 50 && m_point.y < 250) {
            m_bEven = false;
            m_bSelected = true;
        }
    }

    if (!m_bSelected) {

        dice1 = rand() % 6 + 1;
        dice2 = rand() % 6 + 1;
    }
    else {

        int total = dice1 + dice2;

        if (m_bEven) {
            if (total % 2 == 0) {
                m_bResult = true;
            }
            else {
                m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL);
            }
        }
        else {
            if (total % 2 == 1) {
                m_bResult = true;
            }
            else {
                m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL);
            }
        }
    }
}

ゲームオーバーについては前回説明しました。

最後に、当たった場合に「当たり!」と表示するコードを「Draw関数」に追加します。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    SetRect(&m_sour, 0, 0, 200, 200);
    SetRect(&m_dest, 80, 50, 280, 250);
    m_pEngine->Blt(&m_dest, TEXTURE_DICE, &m_sour);

    SetRect(&m_sour, 200, 0, 400, 200);
    SetRect(&m_dest, 360, 50, 560, 250);
    m_pEngine->Blt(&m_dest, TEXTURE_DICE, &m_sour);

    SetRect(&m_sour, (dice1 - 1) * 100, 200, (dice1 - 1) * 100 + 100, 300);
    SetRect(&m_dest, 200, 315, 300, 415);
    m_pEngine->Blt(&m_dest, TEXTURE_DICE, &m_sour);

    SetRect(&m_sour, (dice2 - 1) * 100, 200, (dice2 - 1) * 100 + 100, 300);
    SetRect(&m_dest, 340, 315, 440, 415);
    m_pEngine->Blt(&m_dest, TEXTURE_DICE, &m_sour);

    if (m_bSelected) {
        if (m_bResult) {
            m_pEngine->DrawPrintf(250, 440, FONT_GOTHIC, D3DCOLOR_XRGB(255, 255, 255), "当たり!");
        }
    }
}

選択が完了していて、「結果フラグ」がtrueであれば「当たり!」と表示します。

それでは、実行して確認しましょう。

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

これで完成です。


何度も書きますが、最初なので直値にしていたり関数分割をしていません。

非常に修正に困るプログラムになっていますが、まずは作れるようになる事を目指します。

何か作ってみようと思った方は、自分でも色々試してみてください。


次へ

戻る

目次へ