神経衰弱の作成 - C言語とelで様々なゲームを作ろう
目次
- C言語とelで様々なゲームを作ろう
- Visual C++ .NET での設定
- テンプレートファイルの解説
- シューティングゲームの作成(チュートリアル)
- パックマン的ゲームの作成(チュートリアル)
- ブロック崩しの作成
- 15パズルの作成
- 横スクロールジャンピングゲームの作成
- オセロの作成
- 神経衰弱の作成
- 7ならべの作成
- テトリスの作成
- ぷよぷよの作成
ソース・プロジェクトファイル
ソースファイルプロジェクトファイル(Visual C++ 6.0)
ゲームの説明
トランプの神経衰弱です。左クリックでカードをめくり、右クリックで間違えたカードを閉じます。全部のカードをめくるまでにかかった時間が表示されます。プログラムの解説
定数・グローバル変数
DDOBJ bmpCard, bmpMark, bmpNumber;
カード(表と裏)、マーク(4種類)、番号(13種類)のスプライトオブジェクトです。使用画像は以下のようになっていて、必要な部分を切り出してそれぞれのトランプを作り出しています。構造体
struct {
int mark;
int no;
bool rev;
} ba[4][13];
カードは x 方向に 13 枚、y 方向に 4 枚、計 52 枚のカードを並べますが、それぞれのカードの状態を表わすのが、二次元配列 ba です。構造体のメンバは以下のような意味を持ちます。- mark : カードのマーク。スペードが0、ハートが1、ダイヤが2、クローバーが3
- no : カードの番号。1 ~ 13 の番号を 1 引いて 0 ~ 12 で表わす。
- rev : カードのめくり状態を表わすフラグ。裏なら true、表なら false。
ウインドウ生成関数
bmpCard = elDraw::LoadObject("card.bmp");
bmpMark = elDraw::LoadObject("mark.bmp");
bmpNumber = elDraw::LoadObject("number.bmp");
カード・マーク・番号の各画像をスプライトに読み込んでいます。
int deal[4][13] = {0};
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 13; x++) {
int mk, no;
do {mk = rand() % 4; no = rand() % 13;} while (deal[mk][no]);
ba[y][x].mark = mk;
ba[y][x].no = no;
ba[y][x].rev = true;
deal[mk][no] = 1;
}
}
神経衰弱なので、まずカードをシャッフルして場に並べなければなりません。その処理を行なっているのが上の部分です。以下に説明を書きます。
int deal[4][13] = {0};
シャッフルをして、配り終えたカードをチェックする配列です。配り終えたカードの配列要素には 1 が入ります。
int mk, no;
do {mk = rand() % 4; no = rand() % 13;} while (deal[mk][no]);
マーク (mk) と番号 (no) をランダムでひとつ選んでいます。配り終えていないカードが選択されるまでループします。
ba[y][x].mark = mk;
ba[y][x].no = no;
ba[y][x].rev = true;
選んだマークと番号を場にセットし、さらに裏返しフラグをセットします。
deal[mk][no] = 1;
配り終えたチェックを入れます。メイン画面 変数
static int omote = 0;
確定していない、表になっているカードの枚数です。2枚揃えて表になったカードは含まれません。カードをめくっていないときは0、1枚めくったときは1、2枚めくって番号が揃わなかったときに2となります。
static int ox, oy;
1枚目にめくったカードの座標が入ります。
static int nx, ny;
現在めくろうとしているカードの座標が入ります。
static int nokori = 52;
残りカード(裏になっているカード)の枚数が入ります。これが0になったらゲームクリアです。
static DWORD startTime;
static DWORD pastTime;
ゲーム開始時間と経過時間です。単位はミリ秒。
if (elChangeScreen()) startTime = timeGetTime();
ゲーム開始時にゲーム開始時間をセットしています。elChangeScreen は、MainScreen が呼ばれる最初の一回だけ、true になる関数です。MainScreen の中で初期設定を行いたいときに使います。timeGetTime は、システムが起動してからの時間をミリ秒単位で返す関数です。メイン画面 カードめくり処理
if (MouseCL && omote < 2) {
マウスが左クリックされ、なおかつ1枚目か2枚目をめくろうとしているときに if 文の中の処理を行ないます。
nx = (MousePX - 60) / 40;
ny = (MousePY - 96) / 64;
nx にクリックされたカードの横方向の番号 (0 ~ 12)、ny にクリックされたカードの縦方向の番号 (0 ~ 3) が入ります。画面上のカードを表示位置が (60, 96) から開始しているのでまずそれを引き、さらにカードの大きさで割っています。
if (nx >= 0 && nx <= 12 && ny >= 0 && ny <= 3 && ba[ny][nx].rev) {
クリックされた場所がカードの上に乗っていて、なおかつそのカードが裏返っている場合、if 文の中の処理を行ないます。
if (omote == 0) {
ox = nx; oy = ny;
ba[oy][ox].rev = false;
omote = 1;
1枚目をめくったときの処理です。ox, oy に座標を保存して、カードを表向きにし、変数に omote に 1 を代入して1枚目をめくった状態にします。
} else {
ba[ny][nx].rev = false;
if (ba[ny][nx].no == ba[oy][ox].no) {
nokori -= 2;
omote = 0;
} else omote = 2;
}
2枚目をめくったときの処理です。まずカードを表向きにし、次に1枚目と2枚目の番号を比べています。番号が等しければ残り枚数を2枚減らし、omote に 0 を代入してカードをめくっていない状態にします。番号が等しくなければ omote に 2 を代入してカードを2枚めくった状態にします。
} else if (MouseCR && omote == 2) {
ba[oy][ox].rev = true;
ba[ny][nx].rev = true;
omote = 0;
}
マウスが右クリックされ、なおかつカードが2枚めくられているときは、2枚のカードを裏返し、カードをめくっていない状態にします。メイン画面 カード表示処理
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 13; x++) {
縦方向に 4 枚、横方向に 13 枚分、カードを表示する処理を繰り返します。
int x2 = x * 40 + 60;
int y2 = y * 64 + 96;
x2 と y2 に表示するカードの左上座標が入ります。
int xc = ba[y][x].rev ? 40 : 0;
xc には、カードが表だった場合には 0、裏だった場合には 40 が入り、これをビットマップ上の x 座標とすることによってカードの表か裏を正しく表示することができます。上の書き方は条件演算子というものを使って、if 文と同じような処理を行なっています。条件演算子の書き方は以下の通り。
条件 ? 処理1 : 処理2
条件が真の場合処理1が実行され、偽の場合処理2が実行されます。
elDraw::Layer(x2, y2, bmpCard, xc, 0, xc + 40, 64);
上で計算した座標を使ってカードを表示しています。
if (!ba[y][x].rev) {
elDraw::Layer(x2 + 6, y2 + 5, bmpMark,
ba[y][x].mark * 28, 0, ba[y][x].mark * 28 + 28, 28);
elDraw::Layer(x2 + 6, y2 + 33, bmpNumber,
ba[y][x].no * 28, 0, ba[y][x].no * 28 + 28, 26);
}
カードが表の場合、マークと番号をカードの上に表示しています。配列 ba のマークと番号のデータからビットマップ上の適切な x 座標を計算して表示します。メイン画面 経過時間処理
pastTime = (timeGetTime() - startTime) / 1000;
現在時間からゲーム開始時間を引くことによって、経過時間を計算しています。単位はミリ秒なので 1000 で割って秒に直しています。
SHOW2(8, 32, "経過時間:%d 秒", pastTime);
経過時間を画面に出力しています。SHOW2 は el の関数で、printf と同じ表記が使えます。メイン画面 ゲームクリア処理
if (nokori <= 0) {
MESG("クリア! 所要時間は %d 秒です。", pastTime);
elDraw::Exit();
}
残り枚数が0になったらゲームクリアのメッセージを表示して終了します。