今回は配列を使ったゲームを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文を使って座標の設定と転送を行っています。
転送先座標の計算式は、
i 左座標 0 85 + 0 * 185 = 85 1 85 + 1 * 185 = 270 2 85 + 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つの絵柄を揃えてください。
<実行結果 クライアント領域のみ>
ここまでシーンを増やす事無く無理やりプログラムを書いてきました。
シーンが増やせれば表現方法も広がりますので、次回はシーンの追加などについて説明しましょう。