C言語ゲームプログラミング  ブロック崩し編 第4回




2014年10月より個人の方を対象に、Study C無料提供を開始しました。
C言語を勉強中の方は、学習・教育に最適なC言語インタープリタのStudy Cを使ってみてください(個人の方は無料です)。
大学・高専・高校などの教育機関での採用実績も多数あるロングセラー商品Study Cが、個人向けに無料提供を始めました。
インタープリタの手軽さに加え、ゲームや3Dタートルグラフィックで楽しく勉強したりと、C言語の学習を協力にサポートします。
ブロック崩しゲーム 3Dツリー クリスマスツリー
また、このようなボタンの用意されているページでは、掲載しているプログラムをStudy Cに直接ロードし実行したりすることができます。
Study Cにロードする Study Cにロードし編集する Study Cにロードし実行する
Study C無料利用についての詳細は、このページを参照してください。



1.ゲーム画面のサイズ調整


今回は、ブロック崩し編 第3回で作成したプログラムでブロックが表示されるように改造していきたいと思います。 まず、今まで小さめのウィンドウサイズだったので、大きなウィンドウサイズにするとともに#defineを使ってゲームの画面サイズを定義するように変更します。 #defineについては、C言語の基礎講座(第10回)を参照してください。

#define GAME_SX		900
#define GAME_SY		700
#define BLOCK_XX	15
#define BLOCK_YY	43
#define RACKET_PY	670
GAME_SX、GAME_SYはボールが移動するゲーム部分のサイズ、BLOCK_XX、BLOCK_YYはブロックを配置できる横・縦の最大数、RACKET_PYはラケットが表示される縦方向の座標とします。 また、ブロックのサイズは、横:60、縦:20ドットとします。 エディタの置換機能を使って440などの数値をRACKET_PYなどに置き換えていきます。
また、これらの変更とともに元になるウィンドウの大きさも下記のように変更します。また、表示位置も(10, 10)固定とします。
gl_openwin(10, 10, 980, 700, 0);

以上の変更を行ったプログラムが下記になります。

#include <graph.h>


#define GAME_SX		900
#define GAME_SY		700
#define BLOCK_XX	15
#define BLOCK_YY	43
#define RACKET_PY	670


main()
{
	HBITMAP	hbmp_ball;
	//ボール用の変数
	int	b_xx = GAME_SX/2, bd_xx = 10,
		b_yy = GAME_SY/3, bd_yy = 10;
	int	bold_xx = -1, bold_yy = -1;

	//ラケット用の変数
	int	r_xx = 320, rold_xx = -1;
	long	xx, yy;

	int	score = 0;
	char	buff[100];

	unsigned long
		tick;

	hbmp_ball = gl_loadbitmap("ball.bmp");
	if(hbmp_ball == NULL){
		printf("ビットマップファイルが見つかりません.\n");
		exit(0);
	}
	gl_openwin(10, 10, 980, 700, 0);

	//縦の黄色線を描画
	gl_fillrect(GAME_SX, 0, GAME_SX+5, GAME_SY, RGB(200, 200, 0));

	//スコアの初期値を描画
	gl_textout("MSゴシック", 90, 0, 0, GAME_SX+20, 30, "SCORE", RGB(255,255,255), RGB(0,0,0), 0);
	gl_textout("MSゴシック", 90, 0, 0, GAME_SX+20, 50, "00000", RGB(255,255,255), RGB(0,0,0), 0);

	tick = GetTickCount();
	for( ; ; ){
		if(GetTickCount() >= tick && GetTickCount() < tick + 50)
			continue;
		tick = GetTickCount();

		//ESCキーを押したらプログラムが終了する処理も追加
		if(gl_getkeystate(VK_ESCAPE) < 0)
			break;

		//ラケットをマウスで動かす
		gl_getmousepos(&xx, &yy);
		r_xx = xx;
		if(r_xx < 0)
			r_xx = 0;
		if(r_xx > GAME_SX - 80)
			r_xx = GAME_SX - 80;

		//横方向の処理
		b_xx += bd_xx;
		if(b_xx >= GAME_SX-16 || b_xx <= 0){
			//壁に当たったら効果音を鳴らす
			gl_playsound("c:\\windows\\media\\ding.wav");
			bd_xx = -bd_xx;
		}
		if(b_xx < 0){
			b_xx = 0;
		}
		if(b_xx > GAME_SX-16){
			b_xx = GAME_SX-16;
		}

		//縦方向の処理
		b_yy += bd_yy;
		if(b_yy >= RACKET_PY-16 || b_yy <= 0){
			bd_yy = -bd_yy;
		}
		if(b_yy <= 0){
			gl_playsound("c:\\windows\\media\\ding.wav");
			b_yy = 0;
		}

		//ボールがウィンドウ下部に来たときの処理
		if(b_yy > RACKET_PY-16){
			//if文にはボールのサイズを加味した方が良いですが
			//ここでは省略します。
			if(b_xx < r_xx - 16 || b_xx > r_xx + 80 + 16){
				break;
			}
			MessageBeep(0);
			b_yy = RACKET_PY-16;
			//スコアを描画する

			score++;
			sprintf(buff, "%05d", score);
			gl_textout("MSゴシック", 90, 0, 0, GAME_SX+20, 50, buff,
				RGB(255,255,255), RGB(0,0,0), 0);
		}

		//ボールの消去・描画処理
		if(bold_xx != b_xx || bold_yy != b_yy){
			//前に描画したボールを消去
			if(bold_xx != -1 && bold_yy != -1){
				gl_fillrect(bold_xx, bold_yy,
					bold_xx + 16, bold_yy + 16, RGB(0, 0, 0));
			}
			//ボールの旧位置(bold_xx、bold_yy)を更新
			bold_xx = b_xx;
			bold_yy = b_yy;
			//新しい位置にボールを描画
			gl_drawbitmap(hbmp_ball, b_xx, b_yy, 16, 16, 0, 0);
		}

		//ラケットの消去・描画処理
		if(rold_xx != r_xx){
			//前に描画したラケットを消去
			if(rold_xx != -1){
				gl_fillrect(rold_xx, RACKET_PY,
					rold_xx + 80, RACKET_PY + 10, RGB(0, 0, 0));
			}
			//ラケットの旧位置(rold_xx)を更新
			rold_xx = r_xx;
			//新しい位置にラケットを描画
			gl_fillrect(r_xx, RACKET_PY, r_xx + 80, RACKET_PY + 10, RGB(255, 255, 255));
		}

		gl_refresh();
	}
}
大きなサイズのゲーム画面

大きなサイズのゲーム画面になります(スナップショットは縮小していますが)。

2.ブロック表示


次にゲーム画面の上部にブロックにブロックが表示されるように改造していきます。 ブロックは、Study Cをインストールすると「C:\StudyC\RES」にコピーされるblock1.bmpを使用します(特に用意する必要はありません)。
まず、gl_loadbitmap()関数でビットマップをロードします。

hbmp_block = gl_loadbitmap("block1.bmp");
将来、色々なブロックの配置ができるように配列で指定できるようにします(1の部分がブロック有です)。 上部に3列分ブロックの無いエリアを作ってあります。
int	block[BLOCK_YY][BLOCK_XX] = {
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
	};
この配置データを元にgl_drawbitmap()関数でブロックを描画していきます。
	for(i2 = 0; i2 < BLOCK_YY; i2++)
	{
		for(i1 = 0; i1 < BLOCK_XX; i1++)
		{
			if (block[i2][i1] != 0)
			{
				gl_drawbitmap(hbmp_block, i1 * 60, i2 * 20, 60, 20, 0, 0);
			}
		}
	}
以上の変更を行ったプログラムが下記になります。

#include <graph.h>

#define GAME_SX		900
#define GAME_SY		700
#define BLOCK_XX	15
#define BLOCK_YY	43
#define RACKET_PY	670


int	block[BLOCK_YY][BLOCK_XX] = {
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
	};

main()
{
	HBITMAP	hbmp_ball;
	HBITMAP	hbmp_block;
	//ボール用の変数
	int	b_xx = GAME_SX/2, bd_xx = 10,
		b_yy = GAME_SY/3, bd_yy = 10;
	int	bold_xx = -1, bold_yy = -1;

	//ラケット用の変数
	int	r_xx = 320, rold_xx = -1;
	long	xx, yy;

	int	score = 0;
	char	buff[100];

	unsigned long
		tick;
	int	i1, i2;

	hbmp_ball = gl_loadbitmap("ball.bmp");
	hbmp_block = gl_loadbitmap("block1.bmp");
	if(hbmp_ball == NULL || hbmp_block == NULL){
		printf("ビットマップファイルが見つかりません.\n");
		exit(0);
	}
	gl_openwin(10, 10, 980, 700, 0);

	//縦の黄色線を描画
	gl_fillrect(GAME_SX, 0, GAME_SX+5, GAME_SY, RGB(200, 200, 0));

	//スコアの初期値を描画
	gl_textout("MSゴシック", 90, 0, 0, GAME_SX+20, 30, "SCORE", RGB(255,255,255), RGB(0,0,0), 0);
	gl_textout("MSゴシック", 90, 0, 0, GAME_SX+20, 50, "00000", RGB(255,255,255), RGB(0,0,0), 0);

	for(i2 = 0; i2 < BLOCK_YY; i2++)
	{
		for(i1 = 0; i1 < BLOCK_XX; i1++)
		{
			if (block[i2][i1] != 0)
			{
				gl_drawbitmap(hbmp_block, i1 * 60, i2 * 20, 60, 20, 0, 0);
			}
		}
	}


	tick = GetTickCount();
	for( ; ; ){
		if(GetTickCount() >= tick && GetTickCount() < tick + 50)
			continue;
		tick = GetTickCount();

		//ESCキーを押したらプログラムが終了する処理も追加
		if(gl_getkeystate(VK_ESCAPE) < 0)
			break;

		//ラケットをマウスで動かす
		gl_getmousepos(&xx, &yy);
		r_xx = xx;
		if(r_xx < 0)
			r_xx = 0;
		if(r_xx > GAME_SX - 80)
			r_xx = GAME_SX - 80;

		//横方向の処理
		b_xx += bd_xx;
		if(b_xx >= GAME_SX-16 || b_xx <= 0){
			//壁に当たったら効果音を鳴らす
			gl_playsound("c:\\windows\\media\\ding.wav");
			bd_xx = -bd_xx;
		}
		if(b_xx < 0){
			b_xx = 0;
		}
		if(b_xx > GAME_SX-16){
			b_xx = GAME_SX-16;
		}

		//縦方向の処理
		b_yy += bd_yy;
		if(b_yy >= RACKET_PY-16 || b_yy <= 0){
			bd_yy = -bd_yy;
		}
		if(b_yy <= 0){
			gl_playsound("c:\\windows\\media\\ding.wav");
			b_yy = 0;
		}

		//ボールがウィンドウ下部に来たときの処理
		if(b_yy > RACKET_PY-16){
			//if文にはボールのサイズを加味した方が良いですが
			//ここでは省略します。
			if(b_xx < r_xx - 16 || b_xx > r_xx + 80 + 16){
				break;
			}
			MessageBeep(0);
			b_yy = RACKET_PY-16;
			//スコアを描画する

			score++;
			sprintf(buff, "%05d", score);
			gl_textout("MSゴシック", 90, 0, 0, GAME_SX+20, 50, buff,
				RGB(255,255,255), RGB(0,0,0), 0);
		}

		//ボールの消去・描画処理
		if(bold_xx != b_xx || bold_yy != b_yy){
			//前に描画したボールを消去
			if(bold_xx != -1 && bold_yy != -1){
				gl_fillrect(bold_xx, bold_yy,
					bold_xx + 16, bold_yy + 16, RGB(0, 0, 0));
			}
			//ボールの旧位置(bold_xx、bold_yy)を更新
			bold_xx = b_xx;
			bold_yy = b_yy;
			//新しい位置にボールを描画
			gl_drawbitmap(hbmp_ball, b_xx, b_yy, 16, 16, 0, 0);
		}

		//ラケットの消去・描画処理
		if(rold_xx != r_xx){
			//前に描画したラケットを消去
			if(rold_xx != -1){
				gl_fillrect(rold_xx, RACKET_PY,
					rold_xx + 80, RACKET_PY + 10, RGB(0, 0, 0));
			}
			//ラケットの旧位置(rold_xx)を更新
			rold_xx = r_xx;
			//新しい位置にラケットを描画
			gl_fillrect(r_xx, RACKET_PY, r_xx + 80, RACKET_PY + 10, RGB(255, 255, 255));
		}

		gl_refresh();
	}
}

ブロックを表示
このプログラムを実行した結果です。早くもブロック崩しゲーム完成か?
ボールがブロックを突き抜ける
と思いきや、ボールがブロックを突き抜けてしまいます。


[an error occurred while processing this directive]