15パズルの作成 - C/C++言語とDXライブラリでゲーム作成入門
目次
- C/C++言語とDXライブラリでゲーム作成入門
- シューティングゲームの作成(チュートリアル)
- ドットイートゲームの作成(チュートリアル)
- 15パズルの作成
- 神経衰弱の作成
- オセロの作成
- オブジェクト指向を活用したシューティングゲーム
今回の目的
- パズルゲームの作成
- 関数に分割する
- タイトル画面・メイン画面・クリア画面を分ける
ソースファイル
今回の講座のソースを全て含んだプロジェクトファイル(Visual C++ 2010)を以下に置いておきます。15puzzle.zip
ゲームの説明
最初に出てくる絵をよく覚えておいて左クリックし、バラバラになった絵をマウスで移動して揃えればクリアです。パズル画像の作成
「15puzzle」というプロジェクトを作成し、320×320ピクセルのPNG画像を用意し、「pic.png」という名前でプロジェクトフォルダに保存してください。パズル画像の例
プログラムの入力・実行
プロジェクトに「15puzzle.cpp」という名前の新規ソースファイルを追加し以下のプログラムを入力してください。
#include "DxLib.h"
// グローバル変数
int pics[16];
int pic_all;
int panel[16];
enum {TITLE, MAIN, CLEAR} status = TITLE;
// 選択したパネルと空白を入れ替える
void change(int x, int y) {
int p1 = y * 4 + x;
int p2 = -1;
if (x > 0 && panel[p1 - 1] == 15) p2 = p1 - 1;
if (x < 3 && panel[p1 + 1] == 15) p2 = p1 + 1;
if (y > 0 && panel[p1 - 4] == 15) p2 = p1 - 4;
if (y < 3 && panel[p1 + 4] == 15) p2 = p1 + 4;
if (p2 != -1) {
panel[p2] = panel[p1];
panel[p1] = 15;
}
}
// タイトル画面
void gameTitle() {
if (GetMouseInput() & MOUSE_INPUT_LEFT) {
// 初期パネルのシャッフル
for (int i = 0; i < 16; i++) panel[i] = i;
for (int i = 0; i < 1000; i++) {
change(GetRand(3), GetRand(3));
}
status = MAIN;
}
DrawGraph(0, 0, pic_all, FALSE);
DrawString(102, 142, "CLICK TO START", GetColor(255, 0, 0));
}
// メイン画面
void gameMain() {
if (GetMouseInput() & MOUSE_INPUT_LEFT) {
int x, y;
GetMousePoint(&x, &y);
change(x / 80, y / 80);
// パネルが全部揃ったか判定
bool clear = true;
for (int i = 0; i < 16; i++) {
if (panel[i] != i) clear = false;
}
if (clear) status = CLEAR;
}
// パネルの描画
for (int i = 0; i < 16; i++) {
if (panel[i] < 15) {
DrawGraph((i % 4) * 80, (i / 4) * 80, pics[panel[i]], FALSE);
}
}
}
// 終了画面
void gameClear() {
DrawGraph(0, 0, pic_all, FALSE);
DrawString(115, 142, "GAME CLEAR!", GetColor(255, 0, 0));
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
SetGraphMode(320, 320, 32);
ChangeWindowMode(TRUE);
DxLib_Init();
SetDrawScreen(DX_SCREEN_BACK);
LoadDivGraph("pic.png", 16, 4, 4, 80, 80, pics);
pic_all = LoadGraph("pic.png");
while (!ProcessMessage()) {
ClearDrawScreen();
switch (status) {
case TITLE: gameTitle(); break;
case MAIN: gameMain(); break;
case CLEAR: gameClear(); break;
}
ScreenFlip();
}
DxLib_End();
return 0;
}
入力が完了したらメニューの「デバッグ」→「デバッグなしで開始」を選び、プログラムを実行します。最初の画面で左クリックするとゲームが始まります。16分割されたパネルを空きパネルを利用して移動して、画像を元に戻すとクリアとなります。プログラムの解説
グローバル変数
int pics[16];
各パネル画像の入るグラフィックハンドルです。16分割された80×80ピクセルの画像が各要素に入ります。
int pic_all;
開始・終了用に、完成画像の入るグラフィックハンドルです。
int panel[16];
15パズルでは、画像を縦と横にそれぞれ4分割しますが、どこにどの画像が入っているかを表わす配列がpanelです。各要素は0~15の値を取り、0~14がパネルを分割した画像、15がパネルを移動するための空きです。配列の各要素がどの位置のパネルを表わすかは、以下の通り。panel[0] | panel[1] | panel[2] | panel[3] |
panel[4] | panel[5] | panel[6] | panel[7] |
panel[8] | panel[9] | panel[10] | panel[11] |
panel[12] | panel[13] | panel[14] | panel[15] |
enum {TITLE, MAIN, CLEAR} status = TITLE;
ゲームの現在の状態を表す変数です。タイトル画面・メイン画面・クリア画面の3つで遷移します。change関数
void change(int x, int y) {
int p1 = y * 4 + x;
int p2 = -1;
if (x > 0 && panel[p1 - 1] == 15) p2 = p1 - 1;
if (x < 3 && panel[p1 + 1] == 15) p2 = p1 + 1;
if (y > 0 && panel[p1 - 4] == 15) p2 = p1 - 4;
if (y < 3 && panel[p1 + 4] == 15) p2 = p1 + 4;
if (p2 != -1) {
panel[p2] = panel[p1];
panel[p1] = 15;
}
}
(x, y)にクリックしたパネルの位置(それぞれ0~3)を渡すと、そのパネルと空きパネルの位置を交換する関数です。変数p1にクリックされたパネルの位置(0~15)、p2に空きパネルの位置が入ります。クリックされたパネルの上下左右に空きパネルがあるときのみ移動します。タイトル画面
void gameTitle() {・・・}
タイトル画面の処理を行う関数です。
if (GetMouseInput() & MOUSE_INPUT_LEFT) {
// 初期パネルのシャッフル
for (int i = 0; i < 16; i++) panel[i] = i;
for (int i = 0; i < 1000; i++) {
change(GetRand(3), GetRand(3));
}
status = MAIN;
}
GetMouseInputはマウスの入力状態を調べる関数です。左クリックされていれば、change関数を使って任意のパネルを1000回クリックしてシャッフルしています。ランダムにパネルを当てはめると絶対に完成しないパズルができる可能性があるので、完成した状態からシャッフルしているのです。GetRandは0から引数で指定した範囲の乱数を取得する関数です。シャッフルした後、メイン画面に遷移します。
DrawGraph(0, 0, pic_all, FALSE);
DrawString(102, 142, "CLICK TO START", GetColor(255, 0, 0));
タイトル画面では完成画像とメッセージを表示しています。メイン画面
void gameMain() {・・・}
メイン画面の処理を行う関数です。
int x, y;
GetMousePoint(&x, &y);
change(x / 80, y / 80);
GetMousePointはマウスの座標を取得する関数です。クリックされた画像と空きパネルを交換します。
bool clear = true;
for (int i = 0; i < 16; i++) {
if (panel[i] != i) clear = false;
}
if (clear) status = CLEAR;
panel配列をチェックして完成画像の位置になっていれば、クリア画面に遷移します。
for (int i = 0; i < 16; i++) {
if (panel[i] < 15) {
DrawGraph((i % 4) * 80, (i / 4) * 80, pics[panel[i]], FALSE);
}
}
panel配列から適切な位置のパネル画像を表示します。要素の値が15の場合は空きパネルなので表示しません。クリア画面
void gameClear() {・・・}
ゲームクリア画面の処理を行う関数です。
DrawGraph(0, 0, pic_all, FALSE);
DrawString(115, 142, "GAME CLEAR!", GetColor(255, 0, 0));
クリア画面では完成画像とメッセージを表示しています。WinMain関数
switch (status) {
case TITLE: gameTitle(); break;
case MAIN: gameMain(); break;
case CLEAR: gameClear(); break;
}
現在の状態に応じた画面の関数を呼び出しています。