画像(logo)

HOME/[C言語DXライブラリ]ブロックパズルの作り方 目次/二日目 背景とブロックと壁を表示

広告

[C言語 DXライブラリ ブロックパズルの作り方]
二日目 背景とブロックと壁を表示

広告

↓2016年02月29日発売↓

12歳からはじめる ゼロからのC言語 ゲームプログラミング教室

目次へ戻る

背景を表示

ちょっとさみしいコンソールプログラミングから直接こちらに来られた方の為に、せっかくDXライブラリを使っているので何か背景画像を表示したいと思います。

背景なんかいらないよ!という方はここは読み飛ばしていってください。

土台のプログラム

まずは今回のブロックパズル作りの土台のプログラムをご紹介します。

#include "DxLib.h"

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE);
	DxLib_Init();
	SetDrawScreen(DX_SCREEN_BACK);

	/*ここにプログラムを書いていきます*/

	ScreenFlip();
	
	WaitKey();

	DxLib_End();
	return 0;
}

■実行結果■

画像(cdxb_2_1)

何も表示されないDxLibというウィンドウが現れれば成功です。

DXライブラリ、またはWin32アプリケーション初体験の方に少し説明しますと、まず今まで見慣れていたであろう「main()」が「WinMain()」というものに変わります。

それと一緒に「WinMain()」の戻り値、引数なども意味不明なモノに変わるのですが、ここのトコロは特に気にせず丸ごと覚えてしまいましょう。

そしてDXライブラリを使う為のいくつかの手順を踏みます。

ChangeWindowMode(TRUE);

表示されるプレイ画面を640×480のウィンドウ表示にします。

もちろん全画面表示にする事もできますのでもし興味ある方はDXライブラリのリファレンスを参考にしてみてください。

DxLib_Init();

DXライブラリを初期化します。

SetDrawScreen(DX_SCREEN_BACK);

グラフィック表示のチラつきをなくす為に裏画面に描画するという設定に変えます。

逆にこれをしないとグラフィックを表示する時にチラつきができます。

そしてその裏画面を目に見えるように表画面に反映させなきゃいけないので

ScreenFlip();

こちらの関数で表画面に裏画面を反映させます。

そしてこのままではプログラムが終了次第すぐにウィンドウが閉じてしまうので今回は「WaitKey()」というキー入力まで一時停止する関数で足止めをしてます。

DxLib_End();

DXライブラリを終了します。

これでDXライブラリを使う為の簡単な手順はおしまいです。

ここにゲームループなどを加えていろいろなゲームを作っていきます。

背景画像表示

では説明はこのぐらいにしてさっそく画像を表示したいと思います。

まずはお好みの背景画像(640×480)を用意しましょう。

画像(cdxb_2_2)

素晴らしき田園風景です!

そして用意したファイルを「back_img1.jpg」という名前でソースプログラムが置いてあるフォルダに置きます。

特別な設定をしなければたぶん

ドキュメント

Visual Studio 2013

Projects

dxlib_sample(プロジェクト名)

dxlib_sample(プロジェクト名)

これでソースプログラムが置いてあるフォルダにたどり着きます。

次はプログラムの方です。

まず画像用の変数を用意します。

int back_img1;

さきほどのファイル名と同じ名前ですがもちろん違う名前で大丈夫です。

画像ファイルですが「int」型で大丈夫です。

次にこの変数に実際の画像を読み込みます。

back_img1 = LoadGraph("./back_img1.jpg");

関数「LoadGraph("画像ファイルへのパス/画像ファイル名");」になります。

これで変数「back_img1」に先ほどの画像が読み込まれました。

後は表示するだけです。

いちよう座標の確認ですが

画像(cdxb_2_3)

左上が(x0,y0)で右下が(x639,y479)になります。

それでは表示してみます。

DrawGraph(0,0,back_img1,TRUE);

関数「DrawGraph(左上のx座標,左上のy座標,画像の変数,TRUE)」になります。

画像の左上の角に合わせて指定した座標に画像を表示します。

最後の「TRUE」というのは画像の透明度を使う時は「TRUE」にするのですが基本的には「TRUE」のままで大丈夫です。

そしてこれらを先ほどのプログラムに追加すると・・・。

#include "DxLib.h"

int back_img1;

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE);
	DxLib_Init();
	SetDrawScreen(DX_SCREEN_BACK);

	back_img1 = LoadGraph("./back_img1.jpg");
	DrawGraph(0,0,back_img1,TRUE);

	ScreenFlip();
	
	WaitKey();

	DxLib_End();
	return 0;
}

■実行結果■

画像(cdxb_2_4)

背景がDXライブラリのウィンドウに現れました!

2重の「for」ループ

続けて壁とブロックを表示したいトコロですがその前にブロックパズル作りでよく使われる2重の「for」ループの動きの確認をしておきたいと思います。

for(y=0;y<4;y++){
	for(x=0;x<4;x++){
		
	}
}

こちらが2重の「for」ループです。

ループの内側にループがありますね!

イメージとしては最初に内側のループを終わらせてから、外側のループを一つ進めるような感じになります。

これが慣れないとなかなかややこしいので実際にプログラムで動きを確認したいと思います。

int block[4][4] = {
	{0,0,0,0},
	{0,1,1,0},
	{0,1,1,0},
	{0,0,0,0}
};

2重の「for」ループを使ってこちらのブロックを描いてみたいと思います。

次のプログラムを今は内容を気にせずにそのままコピーして実行してみてください。

一回一回入力待ちになるので何かキーを押してください。

#include "DxLib.h"

int block[4][4] = {
	{0,0,0,0},
	{0,1,1,0},
	{0,1,1,0},
	{0,0,0,0}
};

int Color_Red;
int back_img1;

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE);
	DxLib_Init();
	SetDrawScreen(DX_SCREEN_BACK);

	Color_Red = GetColor(255,0,0);
	back_img1 = LoadGraph("./back_img1.jpg");
	DrawGraph(0,0,back_img1,TRUE);

	for(int y=0;y<4;y++){
		for(int x=0;x<4;x++){
			DrawFormatString(x*20,y*20,
				Color_Red,"%d",block[y][x]);
			ScreenFlip();
			WaitKey();
		}
	}

	ScreenFlip();
	
	WaitKey();

	DxLib_End();
	return 0;
}

■実行結果■

画像(cdxb_2_5)

まずは内側のループで横一列を描きます。

0000

そして内側のループを終わらせて・・・。

0000
0...

外側のループを一つ進めて再び内側のループに入る

なんとなく動きが確認できましたでしょうか?

2重以上のループは内側から外側のループへと移ります。

これを使って2次元配列をいろいろといじるので動きをイメージできるようにしておきましょう。

広告

左上から右下へ

さきほどのプログラムでもそうでしたが、2次元配列は左上が(0,0)でそこから右下方向に展開します。

たとえば「stage[23][18]」という2次元配列の場合は

画像(cdxb_2_6)

左上が(0,0)、右下が(22,17)というように左上から右下にかけて展開していきます。

ここで気づかれた方いるかもしれませんが、これを「x,y」の座標で考えた時、普通は(x,y)の順番で座標を表しますが、当サイトでは2次元配列表記に合わせて(y,x)の順番で表記します。

ブロックと壁を表示

それでは長らくお待たせしましたが、ようやく本題の壁とブロックを表示したいと思います。

「0」・・・何もない

「1」・・・ブロック

「9」・・・壁

を頭に入れておきましょう!

よく使う数字は定数化

よく使う数字は定数化してしまいます。

/*ブロックの縦幅・横幅*/
#define BLOCK_HEIGHT 4
#define BLOCK_WIDTH 4

/*ステージの縦幅・横幅*/
#define STAGE_HEIGHT 23
#define STAGE_WIDTH 18

/*ブロック一つ分の幅*/
#define DRAW_BLOCK_WIDTH 20

ブロックの幅、ステージの幅などはよく登場するので定数化しました。

これをしておけば後でプログラムを確認する時や自分のオリジナリティを出すべくステージの拡張がしたい場合などにも便利ですね。

変数は基本的にグローバル

ループ用に使う変数などを除いて今回はできるだけわかりやすくするべく変数などはグローバルで宣言していきます。

int block[BLOCK_HEIGHT][BLOCK_WIDTH];
int stage[STAGE_HEIGHT][STAGE_WIDTH];

/*最初は四角いブロックのみ*/
int blocks[BLOCK_HEIGHT][BLOCK_WIDTH] = {
	{0,0,0,0},
	{0,1,1,0},
	{0,1,1,0},
	{0,0,0,0}
};

/*ブロックの横位置*/
int block_x;

/*ブロックの縦位置(内部計算用)*/
int block_y;
/*ブロックの縦位置(表示用)*/
float block_y_count;

/*色*/
int Color_Red;
int Color_Black;

/*画像*/
int back_img1;

ブロックの横位置と縦位置は

↓この左上の部分
0000
0110
0110
0000

の座標になります。

縦位置の内部計算用と表示用に分かれているのはまた後ほど説明いたします。

DXライブラリの関数

そして今回のブロックパズル作りによく使うDXライブラリの関数を紹介しておきます。

int Color_Red;
Color_Red = GetColor(255,0,0);

色を設定する為の関数「GetColor()」になります。

「int」型の変数に指定した色を保存します。

GetColor(赤(0〜255),緑(0〜255),青(0〜255))

みたいな感じで指定します。

DrawFormatString(0,0,Color_Black,"表示する文字");

指定した座標に文字を表示する為の「DrawFormatString()」になります。

DrawFormatString(x座標,y座標,色の指定,"表示する文字");

こんな感じで使います。

「printf()」みたいに「%d」などで変数の内容を表示する事も可能です。

ひとまずよく使う関数を2つ紹介しました。

あとはその都度説明しながらいきたいと思います。

初期化

まずは変数などを初期化していきます。

void my_init_var(){
	int i,j;

	for(i=0;i<STAGE_HEIGHT;i++){
		for(j=0;j<STAGE_WIDTH;j++){
			stage[i][0] = 9;
			stage[i][1] = 9;
			stage[i][2] = 9;
			stage[20][j] = 9;
			stage[21][j] = 9;
			stage[22][j] = 9;
			stage[i][15] = 9;
			stage[i][16] = 9;
			stage[i][17] = 9;		
		}
	}

	block_x = 7;
	block_y = 0;
	block_y_count = 0;

	Color_Red = GetColor(255,0,0);
	Color_Black = GetColor(0,0,0);

	back_img1 = LoadGraph("./back_img1.jpg");
}

「stage[23][18]」に壁の部分を作るべく左右、下を壁である「9」にします。

上の部分には、はみ出さないように重ねあわせるので壁は作りません。

あとはブロックの横位置、縦位置、色、画像などを設定して初期化は終わりです。

ブロックを登録

次にブロックを登録します。

void my_make_block(){
	int x,y;

	for(y=0;y<BLOCK_HEIGHT;y++){
		for(x=0;x<BLOCK_WIDTH;x++){
			block[y][x] = blocks[y][x];
		}
	}
}

ブロックの元となる「blocks」からブロックをゲーム進行時のブロックの受け皿である「block」に移し替えます。

最初はわかりやすいように四角いブロックのみで話を進めていきます。

後半にブロックの元となる「blocks」でブロックの種類を増やして、ここでブロックをランダムに発生させていきます。

別々に画面表示

あとはこの「block[4][4]」と「stage[23][18]」を別々に画面に描いていきます。

一緒に描いても良いのですが、あとあとのラインクリア処理などの都合で別々に描きます。

まずはブロックです。

void my_draw_block(){
	int x,y;

	for(y=0;y<BLOCK_HEIGHT;y++){
		for(x=0;x<BLOCK_WIDTH;x++){
			if(block[y][x] == 1)DrawFormatString(block_x * DRAW_BLOCK_WIDTH + x * DRAW_BLOCK_WIDTH,
				block_y_count + y * DRAW_BLOCK_WIDTH,Color_Red,"■");
		}
	}
}		

「block[4][4]」を調べてブロックの部分「1」が出てきたら「■」に置き換えて表示します。

ちょっとだけ中身がややこしいので、余計な部分を整理して

if(block[y][x] == 1){
	DrawFormatString(x * DRAW_BLOCK_WIDTH,y * DRAW_BLOCK_WIDTH,
		Color_Red,"■");
}

これがまず基本になります。

「DRAW_BLOCK_WIDTH」で決めた幅ずつブロックを描いていきます。

これだけでブロックのカタチは描かれますが、ブロックはx方向、y方向に移動しますのでその分を足し合わせます。

if(block[y][x] == 1){
	DrawFormatString(block_x * DRAW_BLOCK_WIDTH + x * DRAW_BLOCK_WIDTH,
		block_y_count + y * DRAW_BLOCK_WIDTH,Color_Red,"■");
}

「block_x * DRAW_BLOCK_WIDTH」がx方向の移動分、「block_y_count」がy方向の移動分という事ですね。

これで移動分が反映されるのでここの値が変化すれば結果ブロックが動くというワケです。

このブロック移動分などを削除して表示するなりしてみるとわかりやすいかと思います。

続いてステージです。

void my_draw_stage(){
	int x,y;

	for(y=0;y<STAGE_HEIGHT;y++){
		for(x=0;x<STAGE_WIDTH;x++){
			if(stage[y][x] == 1)DrawFormatString(x * DRAW_BLOCK_WIDTH,
				y * DRAW_BLOCK_WIDTH,Color_Red,"■");
			else if(stage[y][x] == 9)DrawFormatString(x * DRAW_BLOCK_WIDTH,
				y * DRAW_BLOCK_WIDTH,Color_Black,"■");
		}
	}
}

同じように「stage[23][18]」を調べて壁の部分「9」が出てきたら「■」に置き換えて表示します。

ブロックの部分である「1」が出てきても置き換えて表示しておりますが、これは固定されたブロックの情報がこの後「stage[23][18]」にも保存されますのでこのようにしております。

メイン

メインです。

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE);
	DxLib_Init();
	SetDrawScreen(DX_SCREEN_BACK);

	my_init_var();
	my_make_block();
	my_draw_back();
	my_draw_variable();
	my_draw_block();
	my_draw_stage();

	ScreenFlip();
	
	WaitKey();

	DxLib_End();
	return 0;
}

今回はただ表示するだけなので今作った関数を並べるだけです。

「my_draw_back()」と「my_draw_variable()」は背景表示と変数の表示をしているだけなので説明は省かせて頂きます。

画像(cdxb_2_7)

壁とブロックが表示できました!

最初はわかりやすいように全体を描画してますが次回以降は内側の部分だけを描いていきます。

背景描画、変数表示などの部分もそれぞれ

「my_draw_back()」

「my_draw_variable()」

という名前でメソッドわけしておりますので詳しくは中間ソースをご覧ください。

ここまでの中間ソースになります。

中間ソース1

それでは次回は落下させてみたいと思います。

次回

三日目 落下

□ページの先頭へ□

□目次へ戻る□

□HOME□

広告

↓2017年06月16日発売↓

やさしいC 第5版 (「やさしい」シリーズ)

新品価格
¥2,700から
(2017/5/1 13:05時点)

↓2014年08月09日発売↓

新・明解C言語 入門編 (明解シリーズ)

新品価格
¥2,484から
(2017/5/1 13:08時点)

↓2016年02月20日発売↓

新・解きながら学ぶC言語

新品価格
¥2,160から
(2017/5/1 13:10時点)

↓2017年02月11日発売↓

C言語プログラミング基本例題88 88

新品価格
¥3,024から
(2017/5/1 13:12時点)

↓2016年12月15日発売↓

Cの絵本 第2版 C言語が好きになる新しい9つの扉

新品価格
¥1,490から
(2017/5/1 13:13時点)

↓2017年02月08日発売↓

新・明解C言語で学ぶアルゴリズムとデータ構造 (明解シリーズ)

新品価格
¥2,700から
(2017/5/1 13:15時点)