C言語:迷路を一筆書きで抜けるゲーム

C言語初級

C言語を使ったコンソール画面で動くゲームです。
Pythonでつくる ゲーム開発 入門講座」という書籍で紹介されていたPythonのtkinter(GUIライブラリ)を使ったプログラムをC言語に移植したものです。

2次元配列を使った勉強にもいいのでは?と思いアップしてみました。

ゲームのイメージ

元のイメージ(Pythonで作ったGUI版。今回のC言語版はこれではないですよ!)

ルールは簡単で、迷路をキーボードの上下左右で動き回り、床を全て塗ればゲームクリアです。ただし、元のGUI画面をC言語を使ったCUI画面に置き換えているので見た目は以下の通りとなります。(わたしはこうしたものが結構好きです)

C言語版のイメージ(Windowsコマンドプロンプト上で実行)

C言語版のルール

 人 ・・・ プレイヤー
 ■ ・・・ 壁
 × ・・・ 塗った床

 矢印キーの上下左右でプレイヤーの移動。
 プレイヤーが動けなくなってしまったら ESC キーで最初の状態に戻ります。
 床を全て塗ったらゲームクリアです。

動作環境:Windows
コンパイラ:Borland C++ Compiler 5.5

動作上の注意点

動作環境をWindowsとしたのは一か所だけ Windows独自のコマンドを使っているからです。

system("cls");

clsはコマンドプロンプト上の命令なので、もしLinux上のC言語で実行するなら

system("clear");

とすれば動くと思います。

ソースコード全体

プログラム解説

迷路データは単純な2次元配列で宣言します。1が壁で、0が床(動き回れる場所)です。

/* 迷路データ */
int meiro[GYO][RETU] = {
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
	{1, 0, 0, 0, 0, 0, 1, 0, 0, 1},
	{1, 1, 1, 1, 1, 0, 1, 0, 0, 1},
	{1, 0, 0, 0, 0, 0, 1, 0, 0, 1},
	{1, 0, 0, 0, 1, 1, 1, 0, 0, 1},
	{1, 0, 0, 0, 0, 0, 0, 0, 0, 1},
	{1, 1, 1, 1, 1, 1, 1, 1, 0, 1},
	{1, 1, 0, 0, 0, 0, 0, 0, 0, 1},
	{1, 1, 0, 0, 0, 0, 0, 0, 0, 1},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};

迷路の初期状態では、0と1の数値しか入っていません。
これを以下のような関数を作って画面に表示させます。

/* 迷路を描く */
void draw_meiro(void)
{
	int x, y;
	for(y=0; y<GYO; y++){
		for(x=0; x<RETU; x++){
			if(meiro[y][x] == 0)	/* 移動可能な床 */
				printf(" ");	/* ← 注)全角スペース */
			else if(meiro[y][x] == 1)	/* 壁 */
				printf("■");
			else if(meiro[y][x] == 2)	/* 塗った床 */
				printf("×");
		}
		printf("\n");
	}
}

実行するとこんな風に表示されます。(ただし上記のプログラムではプレイヤーが表示されないなどの問題がありますので、最終的にはもっと長くなります)

プレイヤーは配列の左上方(meiroの添え字でいうとmeiro[1][1]からスタートします。

px = 1;
py = 1;

ゲーム開始前に塗るべき床の数をカウントしておきます。これは変数 goal_count に代入しています。

/* 塗りつぶすべき床の数をカウントする */
void goal_count_check(void)
{
	int x, y;
	goal_count = 0;
	for(y=0; y<GYO; y++)
		for(x=0; x<RETU; x++)
			if(meiro[y][x] == 0) goal_count++;		/* 移動可能な床の数をカウント */
}

ゲーム中はゲームループと呼ばれる無限ループです。
無限ループを抜けたときがゲームクリアとなります。if(count == goal_count){ … } の部分。

/* ゲームループ */
while(1){
	system("cls");	/* コンソール画面をクリア */
	draw_meiro();	/* 迷路を表示 */

	if(count == goal_count){	/* 床を全て塗りつぶしたかのチェック */
		printf("全て塗りました!\n");
		break;
	}

	key_input();		/* キー入力受付 */
}

コンソール画面でスクロールしてしまうとどうしても見た目が損なわれます。ですから迷路を表示する前にコンソール画面のクリアをします。(Windowsでのみ動作)

system("cls");	/* コンソール画面をクリア */

迷路表示は関数化していますが、2重のfor文を使って行ごとに表示させます。内側ループが横方向(行ごとのすべての列)の表示。外側ループが縦方向(行を上から下に)の表示です。

/* 迷路を描く */
void draw_meiro(void)
{
	int x, y;
	for(y=0; y<GYO; y++){
		for(x=0; x<RETU; x++){
			if(x == px && y == py){	/* プレイヤーの位置のとき */
				meiro[y][x] = 2;	/* 塗りつぶし済みにする */
				count ++;		/* 塗りつぶした数をカウント */
				printf("人");		/* プレイヤー */
			}
			else if(meiro[y][x] == 0)	/* 移動可能な床 */
				printf(" ");	/* ← 注)全角スペース */
			else if(meiro[y][x] == 1)	/* 壁 */
				printf("■");
			else if(meiro[y][x] == 2)	/* 塗った床 */
				printf("×");
		}
		printf("\n");
	}
	printf("move: ←↑→↓ restart: ESC\n");	/* 操作説明 */
}

ポイントは、if文の最初にプレイヤーの表示をしている部分です。

迷路を塗りつぶし済みにして

meiro[y][x] = 2;	/* 塗りつぶし済みにする */

塗りつぶした床の数をカウントし

count ++;		/* 塗りつぶした数をカウント */

プレイヤーを表示させます。

printf("人");		/* プレイヤー */

もし、このプレイヤーの「人」の表示箇所を塗りつぶしの床表示の後ろに持ってきてしまうと「人」の位置に「×」が表示されてしまうのでif文の記述順序は大事です。

迷路表示の後、ユーザからのキー入力を受け付けます。
scanfをつかってしまうといちいちEnterキーを押すことになり面倒です。今回は押した時にリアルタイムなキー入力判定が可能なkbhit関数を利用しています。(kbhit関数の利用にはconio.hのインクルードが必要です)

/* キー入力判定 */
void key_input(void)
{
	int key;
	while (1) {	/* キーが押されるまで待つ */
		if ( kbhit() ){
			key = getch();	/* 入力されたキー番号 */
			break ;
		}
	}
	if(key == 72 && meiro[py-1][px] == 0)			/* ↑キー */
		py --;	/* 上に移動 */
	else if(key == 80 && meiro[py+1][px] == 0)		/* ↓キー */
		py ++;	/* 下に移動 */
	else if(key == 75 && meiro[py][px-1] == 0)		/* ←キー */
		px --;	/* 左に移動 */
	else if(key == 77 && meiro[py][px+1] == 0)		/* →キー */
		px ++;	/* 右に移動 */
	else if(key == 27)								/* ESCキー */
		play_start();	/* 最初の状態に戻る */
	else											/* 上記以外のキーの場合は */
		key_input();								/* 再度キー入力受付 */
}

kbhit関数は、キー入力されたときに真となる関数です。実際にどのキーが押されたかを知るにはgetch関数を使います。

if ( kbhit() ){
	key = getch();	/* 入力されたキー番号 */
	break ;
}

キー番号はint型で返されるのでキー番号を知りたい場合は、以下のようにすれば表示できます。(ただし、その際は system(“cls”); 命令は消してください)

if ( kbhit() ){
	key = getch();	/* 入力されたキー番号 */
	printf("key = %d\n", key);
	break ;
}

コンソールプログラムでもアイデア次第でGUIっぽいゲームが出来るいい例だと思います。

コメント

タイトルとURLをコピーしました