★さめがめ フィールドの作成★


今回は、昔からあるミニゲームの「さめがめ」を作ろうと思います。

ルールは単純ですが、「マインスイーパー」と同じように再帰呼び出しが必要なゲームです。


ゲームのルール


プレイしたことが無い人もいると思いますが、インターネットで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です。


次へ

戻る

目次へ