画像(logo)

HOME/[C言語DXライブラリ]STGの作り方 目次/六日目 敵を表示

広告

[C言語 DXライブラリ STGの作り方]
六日目 敵を表示

広告

↓2016年02月29日発売↓

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

目次へ戻る

敵を表示

今回は敵を表示させてみます。

敵の構造体から見てみましょう。

#define DISP_ENEMY_MAX 5

struct ENEMY{
	int serial_num;
	int appear_point;
	double first_x, first_y;
	double x, y;
	double draw_x, draw_y;
	double m_x,m_y;
	double a_x,a_y;
	double f_x,f_y;
	int init_flag;
	int move_flag;
	double angle;
	int move_type;
	int shot_type;
	int pattern;
	double var[5];
	int gamecount_point;
	int power;
	double range;
	int item_flag;
};

struct ENEMY enemy[DISP_ENEMY_MAX];

プレイヤーの時と少し違い敵はプログラムによって動かすのでその為の情報が少し増えます。

わかりやすいトコロから見てみましょう。

double first_x, first_y;
double x, y;
double draw_x, draw_y;

「first_x,first_y」は敵の出現する座標の初期位置になります。

あとはプレイヤーの時と同じく計算座標「x,y」と表示する時の座標「draw_x,draw_y」になります。

int serial_num;

いちよう識別番号的なものになります。

ここでつけた番号が敵表示の時にテキスト表示されます。

int appear_point;

敵が出現するポイントになります。

「gamecount」がこのポイントに到達すると敵が現れます。

int init_flag;
int move_flag;
int move_type;
int gamecount_point;

順番が前後して申し訳ないですが、プレイヤーショットの時と同じく「init_flag」が初期化用のフラグ、「move_flag」が「敵の入れ物」が使用中かどうかのフラグ、「move_type」が敵の動き方、「gamecount_point」が基準となるその時のgamecountを記録するものになります。

int shot_type;

こちらはその敵の持つショットの種類、

int power;
double range;
int item_flag;

続けて敵の体力と当たり判定の範囲とアイテムの有無のフラグ、

double angle;
int pattern;
double var[5];
double m_x,m_y;
double a_x,a_y;
double f_x,f_y;

残りのものは実際動かす時に必要な変数になります。

ざっと説明しましたが、これがいわゆる「敵の入れ物」になります。

この構造体の数が「同時に画面に表示できる敵の最大数」になります。

最初はとりあえず

#define DISP_ENEMY_MAX 5
struct ENEMY enemy[DISP_ENEMY_MAX];

5体分だけ用意しました。

初期化

初期化します。

void my_init_enemy(){
	for (int i = 0; i < DISP_ENEMY_MAX; i++){
		enemy[i].serial_num = 0;
		enemy[i].appear_point = 0;
		enemy[i].first_x = 0;
		enemy[i].first_y = 0;
		enemy[i].x = 0;
		enemy[i].y = 0;
		enemy[i].draw_x = 0;
		enemy[i].draw_y = 0;
		enemy[i].m_x = 0;
		enemy[i].m_y = 0;
		enemy[i].a_x = 0;
		enemy[i].a_y = 0;
		enemy[i].f_x = 0;
		enemy[i].f_y = 0;
		enemy[i].init_flag = 0;
		enemy[i].move_flag = 0;
		enemy[i].angle = 0;
		enemy[i].move_type = 0;
		enemy[i].shot_type = 0;
		enemy[i].pattern = 0;
		enemy[i].gamecount_point = 0;
		enemy[i].power = 0;
		enemy[i].range = 0;
		enemy[i].item_flag = 0;
		for(int j = 0;j < 5;j++){
			enemy[i].var[j] = 0;
		}
	}
}

全て「0」で初期化しているだけです。

当たり判定範囲の「range」もあとで敵のデータから読み込んでいくのでここでは「0」にしております。

敵のデータ

敵のデータは別の場所にまとめておいて、ここから先ほどの「敵の入れ物」である構造体にデータを読み込んでいきます。

0,100,-180,260,0,0,1,10,0,

1体分の敵のデータになります。

左から順に「serial_no」「appear_point」「first_x」「first_y」「move_type」「shot_type」「power」「range」「item_flag」となっております。

#define STAGE_ENEMY_MAX 10
#define ENEMY_DATA_MAX 9

int ary_enemy_data[STAGE_ENEMY_MAX * ENEMY_DATA_MAX] = {
	0,100,-180,260,0,0,1,10,0,
	1,300,-140,260,0,0,1,10,0,
	2,500,-100,260,0,0,1,10,0,
	3,700,-60,260,0,0,1,10,0,
	4,900,-20,260,0,0,1,10,0,
	5,1100,20,260,0,0,1,10,0,
	6,1300,60,260,0,0,1,10,0,
	7,1500,100,260,0,0,1,10,0,
	8,1700,140,260,0,0,1,10,0,
	9,1900,180,260,0,0,1,10,0
};

とりあえず10体分の敵のデータを用意しました。

1体9個のデータ「#define ENEMY_DATA_MAX 9」が10体分「#define STAGE_ENEMY_MAX 10」という事で計90個のデータになりますね。

この配列を毎回調べて「gamecount」が敵の出現ポイントである「appear_point」と一致した敵がいればその敵のデータを読み込んでいきます。

敵を表示する流れ

敵を表示する流れを見てみましょう。

1・敵データから「appear_point」が「gamecount」と一致する敵データを調べる

2・一致した敵データがあれば現在未使用の「敵の入れ物」を調べて使用中に

3・その時の敵の動き方やショットの種類など他の情報を登録

4・その「敵の動き方」に応じて出現・計算・表示

5・終わったら再び「敵の入れ物」を未使用に

最初に敵のデータからデータを読み出すトコロ以外はプレイヤーショットの時とよく似ていますね。

このように最初の手順が少し異なるだけで基本は同じような考え方で他の敵のショット、アイテム、爆発エフェクトなども作っていきます。

敵データから読み出す

では敵データ「ary_enemy_data」を調べて「敵の入れ物」にデータを読み出すまでを見てみたいと思います。

void my_set_enemy(){
	for (int i = 0; i < STAGE_ENEMY_MAX; i++){
		if (ary_enemy_data[(i * ENEMY_DATA_MAX) + 1] == gamecount && gamecount > 0){
			for (int j = 0; j < DISP_ENEMY_MAX; j++){
				if (enemy[j].move_flag == 0){
					enemy[j].move_flag = 1;
					enemy[j].serial_num = ary_enemy_data[i * ENEMY_DATA_MAX];
					break;
				}
			}
		}
	}
}

「ary_enemy_data」からデータを読み出す「my_set_enemy()」になります。

少し長いので省略しております。

一つ一つ見ていきましょう。

まずはさきほどの敵データを全て調べます。

for (int i = 0; i < STAGE_ENEMY_MAX; i++)

ここで要素数全てを調べるワケではなくあくまでも敵の合計数、この場合10体分だけ調べているトコロに注意です。

そして次の「if」文を少し工夫する事によって敵の出現するポイントである「appear_point」だけを調べていきます。

if (ary_enemy_data[(i * ENEMY_DATA_MAX) + 1] == gamecount)

まずそれぞれの敵データの先頭には「i * ENEMY_DATA_MAX」でアクセスできます。

あとはその次の要素が「appear_point」になるのでそこに「+1」すればたどり着く事ができるというワケです。

その後の「&& gamecount > 0」という部分は「gamecount」が「0」以上の場合はという事ですね。

これは今後ステージを増やした際ステージごとの敵の出現数が異なる時、「appear_point」を「0」に合わせる事によって敵の出現を防止する為の処理です。

ここまでくればあとはプレイヤーショットの時と同じように未使用の「敵の入れ物」を調べて使用中にしてそこにデータを登録するだけです。

for (int j = 0; j < DISP_ENEMY_MAX; j++){
	if (enemy[j].move_flag == 0){
		enemy[j].move_flag = 1;
		enemy[j].serial_num = ary_enemy_data[i * ENEMY_DATA_MAX];
		enemy[j].appear_point = ary_enemy_data[(i * ENEMY_DATA_MAX) + 1];
		enemy[j].first_x = ary_enemy_data[(i * ENEMY_DATA_MAX) + 2];
		enemy[j].first_y = ary_enemy_data[(i * ENEMY_DATA_MAX) + 3];
		enemy[j].move_type = ary_enemy_data[(i * ENEMY_DATA_MAX) + 4];
		/*省略*/
		break;
	}
}

それぞれのデータも「i * ENEMY_DATA_MAX」に「+1」、「+2」と足してずらしていけばたどり着く事ができます。

これでデータの読み出し完了です。

気付いた方いるかもしれませんが、ここで「enemy[j].appear_point」に登録しなおした「appear_point」は実はその後は使う事はありません。

なんとなくプログラムの見た目上入れただけです。

「敵の動き方」に応じて出現・計算

敵の動き方を計算する「my_move_enemy()」です。

少し長いので分けて説明します。

まずは全ての敵の入れ物を調べてそれが使用中かどうか調べます。

for (int i = 0; i < DISP_ENEMY_MAX; i++){
	if (enemy[i].move_flag == 1){
	}
}

ここで使用中「enemy[i].move_flag == 1」の状態のものがあった場合、その敵は動いていなきゃいけないので動かします。

「switch」文で動き方を振り分けます。

switch (enemy[i].move_type){
case 0:
	break;
default:
	break;
}

今回は「case 0」上から現れて中間までまっすぐ下に降りてきて再び戻っていくような動きを作っていきます。

初期化部分と移動部分に分かれます。

初期化部分を見てみます。

if (enemy[i].init_flag == 0){
	enemy[i].x = enemy[i].first_x;
	enemy[i].y = 260;
	enemy[i].gamecount_point = gamecount;
	enemy[i].init_flag = 1;
}
else{
	/*移動部分*/
}

初期化用のフラグ「enemy[i].init_flag == 0」の時は初期化されていないので初期化をします。

初期x座標は先ほどの「ary_enemy_data」に入れた初期x座標がそのまま入ります。

初期y座標は上から現れるので少し隠れるぐらいの「260」からにします。

あとは基準となる「gamecount」を記録して初期化完了です。

では移動部分を見てみましょう。

先ほどの「else」部分ですね。

else{
	if (gamecount < enemy[i].gamecount_point + 500){
		if (gamecount < enemy[i].gamecount_point + 220){
			enemy[i].y--;
		}
		if (gamecount > enemy[i].gamecount_point + 260){
			enemy[i].y++;
		}
	}
	else{
		enemy[i].move_flag = 0;
		enemy[i].init_flag = 0;
	}
}

「gamecount_point」を基準に最初は「220」カウント下降して少し経過してから再び上昇しているだけですね。

そして「gamecount_point」から「500」カウント経過した時点で消滅します。

座標を中央に・表示

座標を中央に移動します。

for (int i = 0; i < DISP_ENEMY_MAX; i++){
	if (enemy[i].move_flag == 1 && enemy[i].init_flag == 1){
		enemy[i].draw_x = enemy[i].x + 220;
		enemy[i].draw_y = ((-1) * enemy[i].y) + 240;
	}
}

「my_to_center()」にこちらを追加して

void my_draw_enemy(){
	for (int i = 0; i < DISP_ENEMY_MAX; i++){
		if (enemy[i].move_flag == 1 && enemy[i].init_flag == 1){
			DrawFormatString(enemy[i].draw_x, enemy[i].draw_y, Color_Red, "e[%d]", enemy[i].serial_num);
		}
	}
}

表示します!

画像(cdxs_6_1)

敵が出現しました!

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

中間ソース5

次回は敵のショットを作ります。

次回

七日目 敵ショットを表示

□ページの先頭へ□

□目次へ戻る□

□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時点)