前回作った迷路にギミックを加えてみます。
例として、ゴール前に「扉」を設置してみます。
「扉」はenumに「DOOR」を追加し、迷路用二次元配列に1箇所変更を加えます。
※好きな場所に設置してください。
表示はそのまま「扉」という文字にしましょう。
<sample program 095-01>
#include <stdio.h> #include <stdlib.h> #include <conio.h> #define ROW 10 #define COL 10 enum { WALL, FLOOR, GOAL, DOOR, }; int main(void) { int maze[ROW][COL] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 1, 1, 1, 0, 1, 1, 0, 2, 0 }, { 0, 1, 0, 1, 1, 1, 1, 0, 1, 0 }, { 0, 1, 0, 1, 0, 1, 0, 0, 1, 0 }, { 0, 1, 0, 1, 0, 1, 1, 0, 1, 0 }, { 0, 1, 0, 1, 0, 1, 1, 0, 1, 0 }, { 0, 1, 0, 1, 0, 1, 1, 3, 1, 0 }, { 0, 1, 0, 1, 0, 1, 0, 0, 1, 0 }, { 0, 1, 0, 1, 1, 1, 1, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; int i; int j; int px = 1; int py = 8; int key; for (;;) { if (maze[py][px] == GOAL) { printf("ゴール!\n"); break; } if (_kbhit()) { key = _getch(); switch (key) { case 'a': if (maze[py][px - 1] != WALL) { px--; } break; case 's': if (maze[py + 1][px] != WALL) { py++; } break; case 'd': if (maze[py][px + 1] != WALL) { px++; } break; case 'w': if (maze[py - 1][px] != WALL) { py--; } break; } } system("cls"); for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (i == py && j == px) { printf("P"); } else{ switch (maze[i][j]) { case WALL: printf("■"); break; case FLOOR: printf("□"); break; case GOAL: printf("G"); break; case DOOR: printf("扉"); break; } } } printf("\n"); } } return 0; } |
<実行結果>
■■■■■■■■■■ ■□□□■□□■G■ ■□■□□□□■□■ ■□■□■□■■□■ ■□■□■□□■P■ ■□■□■□□■□■ ■□■□■□□扉□■ ■□■□■□■■□■ ■□■□□□□■□■ ■■■■■■■■■■ 続行するには何かキーを押してください・・・
とりあえず、表示しただけなので「扉」の上も通過出来てしまいます。
「扉」は通過出来ないように、移動するプログラムに変更を加えます。
<sample program 095-02>
#include <stdio.h> #include <stdlib.h> #include <conio.h> #define ROW 10 #define COL 10 enum { WALL, FLOOR, GOAL, DOOR, }; int main(void) { int maze[ROW][COL] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 1, 1, 1, 0, 1, 1, 0, 2, 0 }, { 0, 1, 0, 1, 1, 1, 1, 0, 1, 0 }, { 0, 1, 0, 1, 0, 1, 0, 0, 1, 0 }, { 0, 1, 0, 1, 0, 1, 1, 0, 1, 0 }, { 0, 1, 0, 1, 0, 1, 1, 0, 1, 0 }, { 0, 1, 0, 1, 0, 1, 1, 3, 1, 0 }, { 0, 1, 0, 1, 0, 1, 0, 0, 1, 0 }, { 0, 1, 0, 1, 1, 1, 1, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; int i; int j; int px = 1; int py = 8; int key; for (;;) { if (maze[py][px] == GOAL) { printf("ゴール!\n"); break; } if (_kbhit()) { key = _getch(); switch (key) { case 'a': if (maze[py][px - 1] != WALL && maze[py][px - 1] != DOOR) { px--; } break; case 's': if (maze[py + 1][px] != WALL && maze[py + 1][px] != DOOR) { py++; } break; case 'd': if (maze[py][px + 1] != WALL && maze[py][px + 1] != DOOR) { px++; } break; case 'w': if (maze[py - 1][px] != WALL && maze[py - 1][px] != DOOR) { py--; } break; } } system("cls"); for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (i == py && j == px) { printf("P"); } else{ switch (maze[i][j]) { case WALL: printf("■"); break; case FLOOR: printf("□"); break; case GOAL: printf("G"); break; case DOOR: printf("扉"); break; } } } printf("\n"); } } return 0; } |
<実行結果>
■■■■■■■■■■ ■□□□■□□■G■ ■□■□□□□■□■ ■□■□■□■■□■ ■□■□■□□■□■ ■□■□■□□■□■ ■□■□■□P扉□■ ■□■□■□■■□■ ■□■□□□□■□■ ■■■■■■■■■■ 続行するには何かキーを押してください・・・
「壁」と「扉」は通れないようプログラムを変更しました。
ただし、このプログラムはあまり良いプログラムとは言えません・・・
通過出来ないオブジェクトを追加する度に、この箇所のプログラムは長くなっていきます。
もっと効率の良い(プログラムの追加や変更に強い)プログラムを考える必要があります。
例えば、次のように考えてはどうでしょう。
ある数より小さい箇所は通れない、ある数より大きい箇所は通れる。
今回のプログラムで説明すると、
壁 0 扉 1 床 2 ゴール 3
の順番にenumの宣言を変更すると、1以下が通れない、2以上が通れる、ことになります。
上のプログラムのenumの部分をこのように変更した場合、迷路データも全て変更しなければなりません。
作る前に「今後の拡張要素」などを考慮して「設計」する必要があることが分かります。
「拡張を考えて設計する」のであれば、このような方法があります。
enum { WALL, DOOR, FLOOR = 10, GOAL, }; |
こうしておけば、
壁 0 扉 1 床 10 ゴール 11
となり、後から「壁」や「扉」以外にも通れないオブジェクトが追加出来そうです。
後のことを考え、今のうちに書き換えておきましょう。
<sample program 095-03>
#include <stdio.h> #include <stdlib.h> #include <conio.h> #define ROW 10 #define COL 10 enum { WALL, DOOR, FLOOR = 10, GOAL, }; int main(void) { int maze[ROW][COL] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 10, 10, 10, 0, 10, 10, 0, 11, 0 }, { 0, 10, 0, 10, 10, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 0, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 1, 10, 0 }, { 0, 10, 0, 10, 0, 10, 0, 0, 10, 0 }, { 0, 10, 0, 10, 10, 10, 10, 0, 10, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; int i; int j; int px = 1; int py = 8; int key; for (;;) { if (maze[py][px] == GOAL) { printf("ゴール!\n"); break; } if (_kbhit()) { key = _getch(); switch (key) { case 'a': if (maze[py][px - 1] >= FLOOR) { px--; } break; case 's': if (maze[py + 1][px] >= FLOOR) { py++; } break; case 'd': if (maze[py][px + 1] >= FLOOR) { px++; } break; case 'w': if (maze[py - 1][px] >= FLOOR) { py--; } break; } } system("cls"); for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (i == py && j == px) { printf("P"); } else{ switch (maze[i][j]) { case WALL: printf("■"); break; case DOOR: printf("扉"); break; case FLOOR: printf("□"); break; case GOAL: printf("G"); break; } } } printf("\n"); } } return 0; } |
<実行結果>
■■■■■■■■■■ ■□□□■□□■G■ ■□■□□□□■□■ ■□■□■□■■□■ ■□■□■□□■□■ ■□■□■□□■□■ ■□■□■□P扉□■ ■□■□■□■■□■ ■□■□□□□■□■ ■■■■■■■■■■ 続行するには何かキーを押してください・・・
念のため、表示順も入れ替えておきました。
では次に、この扉を開けるための「鍵」を設置しましょう。
「鍵」は通過できないと取ることが出来ませんので、enumの最後に「KEY」を追加します。
さらに、マップデータに「鍵」を示す12を入れておいてください。
表示はそのまま「鍵」という文字で表示します。
<sample program 095-04>
#include <stdio.h> #include <stdlib.h> #include <conio.h> #define ROW 10 #define COL 10 enum { WALL, DOOR, FLOOR = 10, GOAL, KEY, }; int main(void) { int maze[ROW][COL] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 10, 10, 10, 0, 10, 10, 0, 11, 0 }, { 0, 10, 0, 10, 10, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 0, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 1, 10, 0 }, { 0, 10, 0, 10, 0, 10, 0, 0, 10, 0 }, { 0, 10, 0, 10, 10, 10, 12, 0, 10, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; int i; int j; int px = 1; int py = 8; int key; for (;;) { if (maze[py][px] == GOAL) { printf("ゴール!\n"); break; } if (_kbhit()) { key = _getch(); switch (key) { case 'a': if (maze[py][px - 1] >= FLOOR) { px--; } break; case 's': if (maze[py + 1][px] >= FLOOR) { py++; } break; case 'd': if (maze[py][px + 1] >= FLOOR) { px++; } break; case 'w': if (maze[py - 1][px] >= FLOOR) { py--; } break; } } system("cls"); for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (i == py && j == px) { printf("P"); } else{ switch (maze[i][j]) { case WALL: printf("■"); break; case DOOR: printf("扉"); break; case FLOOR: printf("□"); break; case GOAL: printf("G"); break; case KEY: printf("鍵"); break; } } } printf("\n"); } } return 0; } |
<実行結果>
■■■■■■■■■■ ■□□□■□□■G■ ■□■□□□□■□■ ■□■□■□■■□■ ■□■□■□□■□■ ■□■□■□□■□■ ■□■P■□□扉□■ ■□■□■□■■□■ ■□■□□□鍵■□■ ■■■■■■■■■■ 続行するには何かキーを押してください・・・
この「鍵」を取って、「扉」を開けてゴールという流れにしたいです。
「鍵」を取る、ということはプレイヤーが「鍵」に重なったことを調べれば良さそうです。
「鍵」と重なったかどうかは、「ゴール」と同じように考えれば出来そうです。
「鍵」を取る、という行為は、次のように考えてみます。
プレイヤーが「鍵」を取ったというフラグを作成し、ONにする
迷路から「鍵」を無くす
まずは、「鍵」取得フラグgetKeyFlagを追加し、「鍵」と重なったらフラグをONにするプログラムを書きましょう。
<sample program 095-05>
#include <stdio.h> #include <stdlib.h> #include <conio.h> #define ROW 10 #define COL 10 enum { WALL, DOOR, FLOOR = 10, GOAL, KEY, }; int main(void) { int maze[ROW][COL] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 10, 10, 10, 0, 10, 10, 0, 11, 0 }, { 0, 10, 0, 10, 10, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 0, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 1, 10, 0 }, { 0, 10, 0, 10, 0, 10, 0, 0, 10, 0 }, { 0, 10, 0, 10, 10, 10, 12, 0, 10, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; int i; int j; int px = 1; int py = 8; int key; int getKeyFlag; getKeyFlag = 0; for (;;) { if (maze[py][px] == GOAL) { printf("ゴール!\n"); break; } if (maze[py][px] == KEY) { if (getKeyFlag == 0) { getKeyFlag = 1; } } if (_kbhit()) { key = _getch(); switch (key) { case 'a': if (maze[py][px - 1] >= FLOOR) { px--; } break; case 's': if (maze[py + 1][px] >= FLOOR) { py++; } break; case 'd': if (maze[py][px + 1] >= FLOOR) { px++; } break; case 'w': if (maze[py - 1][px] >= FLOOR) { py--; } break; } } system("cls"); for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (i == py && j == px) { printf("P"); } else{ switch (maze[i][j]) { case WALL: printf("■"); break; case DOOR: printf("扉"); break; case FLOOR: printf("□"); break; case GOAL: printf("G"); break; case KEY: printf("鍵"); break; } } } printf("\n"); } } return 0; } |
<実行結果>
■■■■■■■■■■ ■□□□■□□■G■ ■□■□□□□■□■ ■□■□■□■■□■ ■□■□■□□■□■ ■□■□■□□■□■ ■□■P■□□扉□■ ■□■□■□■■□■ ■□■□□□鍵■□■ ■■■■■■■■■■ 続行するには何かキーを押してください・・・
特に見た目は変わりませんが、「鍵」を取ったかどうかはフラグを調べれば分かるようになりました。
次は迷路から「鍵」を消さなければなりません。
イメージ的には「床」に「鍵」が落ちており、それを拾うというイメージで考えています。
要は「鍵」であった箇所を「床」にすれば迷路から「鍵」は無くなります。
<sample program 095-06>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#define ROW 10
#define COL 10
enum {
WALL,
DOOR,
FLOOR = 10,
GOAL,
KEY,
};
int main(void)
{
int maze[ROW][COL] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 10, 10, 10, 0, 10, 10, 0, 11, 0 },
{ 0, 10, 0, 10, 10, 10, 10, 0, 10, 0 },
{ 0, 10, 0, 10, 0, 10, 0, 0, 10, 0 },
{ 0, 10, 0, 10, 0, 10, 10, 0, 10, 0 },
{ 0, 10, 0, 10, 0, 10, 10, 0, 10, 0 },
{ 0, 10, 0, 10, 0, 10, 10, 1, 10, 0 },
{ 0, 10, 0, 10, 0, 10, 0, 0, 10, 0 },
{ 0, 10, 0, 10, 10, 10, 12, 0, 10, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
int i;
int j;
int px = 1;
int py = 8;
int key;
int getKeyFlag;
getKeyFlag = 0;
for (;;) {
if (maze[py][px] == GOAL) {
printf("ゴール!\n");
break;
}
if (maze[py][px] == KEY) {
if (getKeyFlag == 0) {
maze[py][px] = FLOOR;
getKeyFlag = 1;
}
}
if (_kbhit()) {
key = _getch();
switch (key) {
case 'a':
if (maze[py][px - 1] >= FLOOR) {
px--;
}
break;
case 's':
if (maze[py + 1][px] >= FLOOR) {
py++;
}
break;
case 'd':
if (maze[py][px + 1] >= FLOOR) {
px++;
}
break;
case 'w':
if (maze[py - 1][px] >= FLOOR) {
py--;
}
break;
}
}
system("cls");
for (i = 0; i < ROW; i++) {
for (j = 0; j < COL; j++) {
if (i == py && j == px) {
printf("P");
}
else{
switch (maze[i][j]) {
case WALL:
printf("■");
break;
case DOOR:
printf("扉");
break;
case FLOOR:
printf("□");
break;
case GOAL:
printf("G");
break;
case KEY:
printf("鍵");
break;
}
}
}
printf("\n");
}
}
return 0;
}
|
<実行結果>
■■■■■■■■■■ ■□□□■□□■G■ ■□■□□□□■□■ ■□■□■□■■□■ ■□■□■□□■□■ ■□■□■P□■□■ ■□■□■□□扉□■ ■□■□■□■■□■ ■□■□□□□■□■ ■■■■■■■■■■ 続行するには何かキーを押してください・・・
これで「鍵」が取れるようになりました。
迷路から「鍵」も無くなりましたが、プレイヤーが「鍵」を持っているかどうかはフラグで調べることが出来ます。
最後に「扉」を開くことを考えましょう。
「扉」は通過できませんので、重なったかどうかを調べることは出来ません。
1歩先を調べ、「扉」であった場合、「鍵」を持っていれば「扉」が開くように作ります。
このプログラムの迷路は進行方向右側に「扉」がありますので、右に歩くプログラムで説明します。
if (maze[py][px + 1] >= FLOOR) { px++; } |
今は↑のように組んであります。
これは、1歩先が「床」以上(床、ゴール、鍵)であれば移動する、というプログラムです。
1歩先が「扉」の場合は、このif文は成り立ちません。
そこでelse文を追加します。
if (maze[py][px + 1] >= FLOOR) { px++; } else { } |
1歩先が「扉」の場合は、このelse文に入ります。
else文に入るのは、「扉」だけでなく「壁」の時にも入りますから「扉」かどうか調べなければなりません。
if (maze[py][px + 1] >= FLOOR) { px++; } else { if (maze[py][px + 1] == DOOR) { } } |
これで「扉」かどうか調べられます。
次に「鍵」を持っているかどうか調べなければなりません。
if (maze[py][px + 1] >= FLOOR) { px++; } else { if (maze[py][px + 1] == DOOR) { if (getKeyFlag == 1) { } } } |
フラグを調べれば分かります。
最後に「扉」を開けます。
要領としては「鍵」の時と同じです。
データの「扉」を「床」に変えれば良いですね。
<sample program 095-07>
if (maze[py][px + 1] >= FLOOR) { px++; } else { if (maze[py][px + 1] == DOOR) { if (getKeyFlag == 1) { maze[py][px + 1] = FLOOR; } } } |
<実行結果>
■■■■■■■■■■ ■□□□■□□■G■ ■□■□□□□■□■ ■□■□■□■■□■ ■□■□■□□■P■ ■□■□■□□■□■ ■□■□■□□□□■ ■□■□■□■■□■ ■□■□□□□■□■ ■■■■■■■■■■ 続行するには何かキーを押してください・・・
これで「鍵」を拾い、「扉」を開けて、ゴール出来るようになりました。
当然、迷路が変われば「扉」への方向が変わりますから、全方向に対してプログラムを追加しなければなりません。
上、下、左も同じように「扉」を開くプログラムを追加してください。
解答例です。
<sample program 095-07>
#include <stdio.h> #include <stdlib.h> #include <conio.h> #define ROW 10 #define COL 10 enum { WALL, DOOR, FLOOR = 10, GOAL, KEY, }; int main(void) { int maze[ROW][COL] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 10, 10, 10, 0, 10, 10, 0, 11, 0 }, { 0, 10, 0, 10, 10, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 0, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 0, 10, 0 }, { 0, 10, 0, 10, 0, 10, 10, 1, 10, 0 }, { 0, 10, 0, 10, 0, 10, 0, 0, 10, 0 }, { 0, 10, 0, 10, 10, 10, 12, 0, 10, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; int i; int j; int px = 1; int py = 8; int key; int getKeyFlag; getKeyFlag = 0; for (;;) { if (maze[py][px] == GOAL) { printf("ゴール!\n"); break; } if (maze[py][px] == KEY) { if (getKeyFlag == 0) { maze[py][px] = FLOOR; getKeyFlag = 1; } } if (_kbhit()) { key = _getch(); switch (key) { case 'a': if (maze[py][px - 1] >= FLOOR) { px--; } else { if (maze[py][px - 1] == DOOR) { if (getKeyFlag == 1) { maze[py][px - 1] = FLOOR; } } } break; case 's': if (maze[py + 1][px] >= FLOOR) { py++; } else { if (maze[py + 1][px] == DOOR) { if (getKeyFlag == 1) { maze[py + 1][px] = FLOOR; } } } break; case 'd': if (maze[py][px + 1] >= FLOOR) { px++; } else { if (maze[py][px + 1] == DOOR) { if (getKeyFlag == 1) { maze[py][px + 1] = FLOOR; } } } break; case 'w': if (maze[py - 1][px] >= FLOOR) { py--; } else { if (maze[py - 1][px] == DOOR) { if (getKeyFlag == 1) { maze[py - 1][px] = FLOOR; } } } break; } } system("cls"); for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (i == py && j == px) { printf("P"); } else{ switch (maze[i][j]) { case WALL: printf("■"); break; case DOOR: printf("扉"); break; case FLOOR: printf("□"); break; case GOAL: printf("G"); break; case KEY: printf("鍵"); break; } } } printf("\n"); } } return 0; } |
<実行結果>
■■■■■■■■■■ ■□□□■□□■P■ ■□■□□□□■□■ ■□■□■□■■□■ ■□■□■□□■□■ ■□■□■□□■□■ ■□■□■□□□□■ ■□■□■□■■□■ ■□■□□□□■□■ ■■■■■■■■■■ ゴール! 続行するには何かキーを押してください・・・
他にも工夫次第で色々なギミックが配置出来そうです。
各自で考え、試してみましょう。