★スロットマシン★


今回は配列を使ったゲームを1つ作ってみます。

3つのリールを持ったスロットマシンを作ります。

画像は「丁半ゲーム」で使った「Dice.png」を使いますので、コピーしておいてください。

※サイコロの「1から6」をスロットマシンの絵柄と見なします。


画像とフォントの準備


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

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

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

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

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

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

今回は背景はありません。


画面構成


サイコロの大きさは「100×100ピクセル」です。

等間隔で配置したいので座標を考えます。

今回は適当に図を書いていますが、ここから計算で座標を出します。

3つのリール(サイコロ)の幅はそれぞれ「100ピクセル」です。

画面の幅は「640ピクセル」ですので、リール3つ分の幅を引くと、

 640 - 300 = 340

になります。

上の図で言うと「黄色」の線の長さの合計が「340ピクセル」と言う事です。

黄色の線は「4つに分割」されていますので、

 340 / 4 = 85

黄色の線1本の長さが「85ピクセル」に設定すれば、均等に割り付けられます。

縦方向は真ん中に配置したいので、

 (480 - 100) / 2 = 190

になります。

これを踏まえて正しく書き直した図が↓です。

等間隔になりましたので、配置座標にも法則が見いだせると思います。

リール左座標
85
270
455

これだけ見ると法則性が無いように見えますが、最初の基準を「0」にするため「85」を引いてみます。

リール左座標
0
185
370

これなら式に出来そうです。

リール左座標
0 * 185
1 * 185
2 * 185

この式は後で使いましょう。


リールの表示


まずはリール用の配列を用意しましょう。

「SceneGame.h」を開き、privateに配列を用意します。

private:
    
    enum { REEL_MAX = 3 };

    int m_reel[REEL_MAX];
};

C++編で紹介した「enumハック」を使いました。

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

リールの初期値は全て「1」にしておきます。

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

    for (int i = 0; i < REEL_MAX; i++) {
        m_reel[i] = 1;
    }
}

サイコロの「目」から転送元座標を計算する方法もやりましたので「Draw関数」で3つのリールを転送します。

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

        SetRect(&m_sour, (m_reel[i] - 1) * 100, 200, (m_reel[i] - 1) * 100 + 100, 300);

        SetRect(&m_dest, 85 + i * 185, 190, 85 + i * 185 + 100, 290);

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

上で転送先座標の計算式に法則性がある事を確認しましたので、for文を使って座標の設定と転送を行っています。

転送先座標の計算式は、

左座標
085 + 0 * 185 = 85
185 + 1 * 185 = 270
285 + 2 * 185 = 455

と、変数iの値の変化によってちゃんと計算されています。

では、実行してみましょう。

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


リールを回す


今回の仕組みは実際のリールとは違うので回す事は出来ません・・・「丁半ゲーム」と同じく乱数を設定し直す事で変化させていきます。

「Update関数」に次のコードを書きましょう。

//=============================================================================
// シーンの実行時に繰り返し呼び出される更新処理関数
//=============================================================================
void SceneGame::Update()
{
    for (int i = 0; i < REEL_MAX; i++) {
        m_reel[i] = rand() % 6 + 1;
    }
}

1回の更新(Update)につき、3つのリールを変化させなければなりませんので、for文を使いました。

実行して「回っている」かどうか確認しましょう。

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


リールを止める


リールを止めるには、止めるためのフラグ(停止フラグ)を使用します。

一度に全部止まる方法もありますが、今回はリール1本につき1つフラグを用意し別々に止められるようにします。

「SceneGame.h」を開き、privateにフラグ配列を追加しましょう。

private:
    
    enum { REEL_MAX = 3 };

    int m_reel[REEL_MAX];

    bool m_bStop[REEL_MAX];
};

「SceneGame.cpp」を開き「Start関数」でフラグの初期値を設定します。

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

    for (int i = 0; i < REEL_MAX; i++) {
        m_reel[i] = 1;
        m_bStop[i] = false;
    }
}

このフラグがfalseの間、乱数が発生するように「Update関数」に次のコードを追加します。

//=============================================================================
// シーンの実行時に繰り返し呼び出される更新処理関数
//=============================================================================
void SceneGame::Update()
{
    for (int i = 0; i < REEL_MAX; i++) {
        if (!m_bStop[i]) {
            m_reel[i] = rand() % 6 + 1;
        }
    }
}

後はどうやって「停止フラグ」をtrueにするかを考えます。


キーボード入力


今回は3つのリールを止める必要があるため、キーボードのキーを使います。

キーボードのキーの状態を取得するにはエンジンクラスの「GetKeyState関数」「GetKeyStateSync関数」を使います。

このゲームでは「押しっぱなし」をチェックする必要はありませんから「GetKeyStateSync関数」を使います。

これらの関数はscanf関数と異なり、プログラムの実行を止める事はありません。

ただ、文字列や数値の入力は出来ず、基本的に「キーが押されているかどうか」しか分かりません。

どのキーを調べるかは、キーコードがあり「DIK_」で始まる定数が用意されています。

この中から数字の1,2,3キーをそれぞれ左、中、右のリールに割り当てて使ってみます。

//=============================================================================
// シーンの実行時に繰り返し呼び出される更新処理関数
//=============================================================================
void SceneGame::Update()
{
    for (int i = 0; i < REEL_MAX; i++) {
        if (!m_bStop[i]) {
            m_reel[i] = rand() % 6 + 1;
        }
    }

    if (m_pEngine->GetKeyStateSync(DIK_1)) {
        m_bStop[0] = true;
    }

    if (m_pEngine->GetKeyStateSync(DIK_2)) {
        m_bStop[1] = true;
    }

    if (m_pEngine->GetKeyStateSync(DIK_3)) {
        m_bStop[2] = true;
    }
}

それぞれのキーを押した時に「停止フラグ」がtrueになり、各リールを止める事が出来ます。

実際に実行して試してください。

※静止画では分からないので実行画面は載せません。


判定


今回はかなりシビアなのですが、3つの絵柄が揃わなかったらゲームオーバーです。

サイコロの絵柄は6種類ですから、めったに揃いません・・・

そこで、絵柄を3種類まで減らしましょう。

//=============================================================================
// シーンの実行時に繰り返し呼び出される更新処理関数
//=============================================================================
void SceneGame::Update()
{
    for (int i = 0; i < REEL_MAX; i++) {
        if (!m_bStop[i]) {
            m_reel[i] = rand() % 3 + 1;
        }
    }

    if (m_pEngine->GetKeyStateSync(DIK_1)) {
        m_bStop[0] = true;
    }

    if (m_pEngine->GetKeyStateSync(DIK_2)) {
        m_bStop[1] = true;
    }

    if (m_pEngine->GetKeyStateSync(DIK_3)) {
        m_bStop[2] = true;
    }
}

さて、判定のタイミングは3つのリールが全て止まった時です。

その時に3つの絵柄が揃っていればボーナスゲームに入ります。

「SceneGame.h」を開き、privateに「全て止まったフラグ」と「ボーナスフラグ」を追加します。

private:
    
    enum { REEL_MAX = 3 };

    int m_reel[REEL_MAX];

    bool m_bStop[REEL_MAX];

    bool m_bAllStop;

    bool m_bBonus;
};

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

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

    for (int i = 0; i < REEL_MAX; i++) {
        m_reel[i] = 1;
        m_bStop[i] = false;
    }

    m_bAllStop = false;

    m_bBonus = false;
}

「Update関数」で、リール別の3つの「停止フラグ」が全てtrueだった場合に「全て止まったフラグ」をtrueにします。

さらに、3つの絵柄が揃っているか調べ、揃っていれば「ボーナスフラグ」をtrueにします。

揃っていなければゲームオーバーです。

//=============================================================================
// シーンの実行時に繰り返し呼び出される更新処理関数
//=============================================================================
void SceneGame::Update()
{
    for (int i = 0; i < REEL_MAX; i++) {
        if (!m_bStop[i]) {
            m_reel[i] = rand() % 3 + 1;
        }
    }

    if (m_pEngine->GetKeyStateSync(DIK_1)) {
        m_bStop[0] = true;
    }

    if (m_pEngine->GetKeyStateSync(DIK_2)) {
        m_bStop[1] = true;
    }

    if (m_pEngine->GetKeyStateSync(DIK_3)) {
        m_bStop[2] = true;
    }

    if (m_bStop[0] && m_bStop[1] && m_bStop[2]) {
        m_bAllStop = true;
    }

    if (m_bAllStop) {
        if (m_reel[0] == m_reel[1] && m_reel[1] == m_reel[2]) {
            m_bBonus = true;
        }
        else {
            m_nowSceneData.Set(Common::SCENE_EXIT, false, NULL);
        }
    }
}

最後に「Draw関数」で結果を表示しましょう。

//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
    for (int i = 0; i < REEL_MAX; i++) {
        SetRect(&m_sour, (m_reel[i] - 1) * 100, 200, (m_reel[i] - 1) * 100 + 100, 300);
        SetRect(&m_dest, 85 + i * 185, 190, 85 + i * 185 + 100, 290);
        m_pEngine->Blt(&m_dest, TEXTURE_DICE, &m_sour);
    }

    if (m_bAllStop) {
        if (m_bBonus) {
            m_pEngine->DrawPrintf(210, 380, FONT_GOTHIC, D3DCOLOR_XRGB(255, 255, 255), "Bonus Start!");
        }
    }
}

実行して3つの絵柄を揃えてください。

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


ここまでシーンを増やす事無く無理やりプログラムを書いてきました。

シーンが増やせれば表現方法も広がりますので、次回はシーンの追加などについて説明しましょう。


次へ

戻る

目次へ