今回は、昔からあるミニゲームの「さめがめ」を作ろうと思います。
ルールは単純ですが、「マインスイーパー」と同じように再帰呼び出しが必要なゲームです。
プレイしたことが無い人もいると思いますが、インターネットでFlashゲームとしてもプレイ出来ますので、一度プレイする事をお勧めします。
「さめがめ」とは、英語の「Same Game(セイムゲーム)」をローマ字読みした表現です。
同じ(Same)色のブロックを消していくゲームで、スコアを競ったり、残ったブロックの数で競ったり出来ます。
ゲーム画面は↓のようなイメージです。
マウスカーソルが指している箇所をみてください。
マウスカーソルは赤いブロックを指しており、そのブロックに隣接している赤いブロックが選択(網掛け)されています。
この状態でクリックすると選択された箇所が消え、上に積んであるブロックが落ちてきます。
※連鎖はしません。
ブロックは同じ色が2個以上隣接している場合のみ消す事が可能で、一度に消したブロックの数が多いほど高得点となります。
ブロックの色数が少ない方が難易度は低く、色数が多くなると難易度が上がります。
今回使う画像は↓の画像です。
ブロックは最大6色分(実際に作るのは4色で作ります)用意しました。
その下は「網掛け」用の画像です。
ブロックや網掛けは「32×32ピクセル」で作っています。
次の段に「スコア」用の数字があり、一番下の「STALEMATE」は「手詰まり」と言う意味です。
スコアは「23×32ピクセル」、「STALEMATE」の文字は「252×32ピクセル」です。
全体の画像サイズは「256×256ピクセル」にしています。
※プロジェクトフォルダの「Resource\\Textureフォルダ」に「Samegame.png」と言うファイル名で保存してください。
「GameBase.h」を開き、ファイル名を定数化します。
//-----------------------------------------------------------------------------
//ゲーム中で使用するテクスチャ、BGM、SE、フォントのパス付ファイル名を書きます。
//-----------------------------------------------------------------------------
namespace KeyString
{
const std::string TEXTURE_SAMEGAME = "Resource\\Texture\\Samegame.png";
}
|
「SceneGame.cpp」の「Start関数」で画像を追加します。
//=============================================================================
// シーンの実行時に1度だけ呼び出される開始処理関数
//=============================================================================
void SceneGame::Start()
{
m_pEngine->AddTexture(TEXTURE_SAMEGAME);
}
|
「Exit関数」で解放しても構いませんが、シーンを追加し画像を使う方は解放しなくても良いです。
今回もクラスオブジェクトを追加して、メンバ変数や定数、関数を使って作ります。
これまでの手順を参考に「Samegame.h」と「Samegame.cpp」をプロジェクトに追加してください。
先に「GameBase.h」を開き、ヘッダファイルをインクルードしておきます。
//-----------------------------------------------------------------------------
// オブジェクトのヘッダファイルをインクルードします。
//-----------------------------------------------------------------------------
#include "Object\\Samegame\\Samegame.h"
|
「Samegame.h」を開き、Samegameクラスの枠組みだけ作っておきましょう。
class Samegame { public: private: }; |
今回のクラスも大きくなると思います。
「さめがめ」のブロックには2つの変数が必要になります。
1つはブロックの色、もう1つは選択フラグです。
ブロックは最大6色用意しており、それぞれ番号を振ります。
ただし、「0」は「ブロック無し」に割り当てますので、1〜6が色の番号になります。
画面全体にブロックを配置したいと思いますので、縦15、横20の2次元配列を用意します。
「Samegame.h」を開き、定数とともに追加しましょう。
class Samegame {
public:
private:
//ブロック構造体
struct Block {
int m_color; //ブロックの色
bool m_bSelect; //選択フラグ
};
//ブロック無し定数
enum { NO_BLOCK };
//フィールド用定数
enum { ROW = 15, COL = 20 };
//フィールド(盤面)
Block m_field[ROW][COL];
};
|
次に、コンストラクタ関数と初期化用の関数を追加します。
class Samegame { public: Samegame(); //ゲームの初期化 void Initialize(const int colorNum); private: //ブロック構造体 struct Block { int m_color; //ブロックの色 bool m_bSelect; //選択フラグ }; //ブロック無し定数 enum { NO_BLOCK }; //フィールド用定数 enum { ROW = 15, COL = 20 }; //フィールド(盤面) Block m_field[ROW][COL]; }; |
初期化用の「Initialize関数」は、マインスイーパーの時と同じように引数があります。
今回の引数はゲームで使う「色の数」です。
色の数が多いほど難易度があがりますので、ゲームシーンで設定出来るように作ります。
※タイトルシーンは作りませんので、皆さんでチャレンジしてみてください。
「Samegame.cpp」を開き、関数の本体を追加しましょう。
Samegame::Samegame()
{
}
//ゲームの初期化
void Samegame::Initialize(const int colorNum)
{
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
m_field[i][j].m_color = rand() % colorNum + 1;
m_field[i][j].m_bSelect = false;
}
}
}
|
コンストラクタ関数には、今は何も書く必要はありません。
「Initialize関数」では、受け取った引数を元に乱数でブロックを設定しています。
引数が「4」であれば「1〜4」までの数値をフィールドに代入していきます。
次にフィールドを描画する準備をします。
「Samegame.h」に必要な定数や関数を用意しましょう。
class Samegame { public: Samegame(); //ゲームの初期化 void Initialize(const int colorNum); //ブロックの描画 void DrawBlock(Engine *pEngine); private: const int BLOCK_SOUR_X; //ブロックの転送元左座標 const int BLOCK_SOUR_Y; //ブロックの転送元上座標 const int BLOCK_DEST_X; //ブロックの転送先左座標 const int BLOCK_DEST_Y; //ブロックの転送先上座標 const int MESH_SOUR_X; //網掛けの転送元左座標 const int MESH_SOUR_Y; //網掛けの転送元左上座標 const int BLOCK_WIDTH; //ブロックの幅 const int BLOCK_HEIGHT; //ブロックの高さ //ブロック構造体 struct Block { int m_color; //ブロックの色 bool m_bSelect; //選択フラグ }; //ブロック無し定数 enum { NO_BLOCK }; //フィールド用定数 enum { ROW = 15, COL = 20 }; //フィールド(盤面) Block m_field[ROW][COL]; RECT m_sour; RECT m_dest; }; |
描画用の「DrawBlock関数」と転送座標用のRECT構造体と定数を追加しました。
「網掛け」用の「MESH_SOUR_X」「MESH_SOUR_Y」もここで用意しておきます。
「Samegame.cpp」のコンストラクタ関数で定数の初期値を設定しましょう。
Samegame::Samegame()
: BLOCK_SOUR_X(0)
, BLOCK_SOUR_Y(0)
, BLOCK_DEST_X(0)
, BLOCK_DEST_Y(0)
, MESH_SOUR_X(0)
, MESH_SOUR_Y(32)
, BLOCK_WIDTH(32)
, BLOCK_HEIGHT(32)
{
}
|
続いて「DrawBlock関数」を追加します。
//ブロックの描画
void Samegame::DrawBlock(Engine *pEngine)
{
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if (m_field[i][j].m_color != NO_BLOCK) {
SetRect(&m_sour,
BLOCK_SOUR_X + (m_field[i][j].m_color - 1) * BLOCK_WIDTH,
BLOCK_SOUR_Y,
BLOCK_SOUR_X + (m_field[i][j].m_color - 1) * BLOCK_WIDTH + BLOCK_WIDTH,
BLOCK_SOUR_Y + BLOCK_HEIGHT);
SetRect(&m_dest,
BLOCK_DEST_X + j * BLOCK_WIDTH,
BLOCK_DEST_Y + i * BLOCK_HEIGHT,
BLOCK_DEST_X + j * BLOCK_WIDTH + BLOCK_WIDTH,
BLOCK_DEST_Y + i * BLOCK_HEIGHT + BLOCK_HEIGHT);
pEngine->Blt(&m_dest, TEXTURE_SAMEGAME, &m_sour);
}
}
}
}
|
ブロック無しの場合は何も転送せず、ブロックがある箇所だけ転送します。
転送元の画像と色の番号の関係を見れば、
(m_field[i][j].m_color - 1) |
の意味は分かると思います。
※トランプでも使っています。
それでは、実際に動かしてみたいので「SceneGame.h」を開いてください。
privateにSamegameクラスの実体を作りましょう。
private: Samegame m_samegame; }; |
「SceneGame.cpp」の「Start関数」でSamegaraクラスの「Initialize関数」を呼び出します。
//============================================================================= // シーンの実行時に1度だけ呼び出される開始処理関数 //============================================================================= void SceneGame::Start() { m_pEngine->AddTexture(TEXTURE_SAMEGAME); m_samegame.Initialize(4); } |
今回のゲームでは色を4色使うと言う意味です。
「Draw関数」でSamegameクラスの「DrawBlock関数」を呼び出します。
//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
m_samegame.DrawBlock(m_pEngine);
}
|
それでは実行してみましょう。
<実行結果 クライアント領域のみ>
ランダムで4色のブロックが並んでいればOKです。