画像(logo)

HOME/[C言語DXライブラリ]STGの作り方 目次/二十日目 簡単な弾幕

広告

[C言語 DXライブラリ STGの作り方]
二十日目 簡単な弾幕

広告

↓発売日:2018年09月22日↓

たった1日で基本が身に付く! C言語 超入門

新品価格
¥2,225から
(2018/10/8 23:10時点)

目次へ戻る

簡単な弾幕

今回はボス用のショットとして簡単な弾幕を3種類作ってみたいと思います。

下準備

下準備としてボスショット用の構造体を新たに作ります。

#define BOSS_SHOT_MAX 10

struct BOSS_SHOT{
	double first_x;
	double first_y;
	double x[300];
	double y[300];
	double draw_x[300];
	double draw_y[300];
	double angle[300];
	double angle2[300];
	double var[300];
	int init_flag;
	int move_flag;
	int move_type;
	int max_bullet;
	int gamecount_point[5];
	double range;
	int boss_num;
};

struct BOSS_SHOT boss_shot[BOSS_SHOT_MAX];

座標やら角度やらがやたら増えただけで基本的には通常の敵ショットと内容は変わりありません。

それではこちらを使って弾幕ショットを作っていきましょう。

ショットの起動部分なども通常の敵ショットとほぼ変わりないのでいきなりそれぞれの弾幕ショットの解説から入りたいと思います。

ばら撒きショット

まずはばら撒きショットです。

ランダムな方向と速度で全方位に弾をばら蒔きます。

初期化部分

if (boss_shot[i].init_flag == 0){
	boss_shot[i].gamecount_point[0] = gamecount;
	boss_shot[i].gamecount_point[1] = gamecount;
	boss_shot[i].max_bullet = 1;
	for (int j = 0; j < 200; j++){
		boss_shot[i].x[j] = boss_shot[i].first_x;
		boss_shot[i].y[j] = boss_shot[i].first_y;
	}
	for (int j = 0; j < 200; j++){
		boss_shot[i].angle[j] = ((rand() % 360) * (DX_PI / 180));
	}
	for (int j = 0; j < 200; j++){
		boss_shot[i].var[j] = (rand() % 5) + 1;
	}
	boss_shot[i].init_flag = 1;
}
else{
	/*移動部分*/
}

初期化部分は連射ショットを作る時と同じように基準となる「gamecount」を2つ分保存、最大弾数である「max_bullet」を「1」に、全ての座標をその時のボスがいる座標で初期化、そしてランダムな方位、ランダムな速度を任意の数だけ用意します。

移動部分

for (int j = 0; j < boss_shot[i].max_bullet; j++){
	boss_shot[i].x[j] += cos(boss_shot[i].angle[j]) * boss_shot[i].var[j];
	boss_shot[i].y[j] += sin(boss_shot[i].angle[j]) * boss_shot[i].var[j];
}
if (gamecount == boss_shot[i].gamecount_point[1] + 2
	&& boss[boss_shot[i].boss_num].move_flag == 1){
	if (boss_shot[i].max_bullet < 200){
		boss_shot[i].max_bullet++;
		boss_shot[i].gamecount_point[1] += 2;
	}
}

あとは一定時間ごとに最大弾数である「max_bullet」を増加、その最大弾数分だけ初期化時に取得した方向とスピードでそれぞれの座標を移動させれば完了です!

画像(cdxs_20_1)

ばら撒きショット!

連続リングショット

続きまして連続リングショットです。

変な名前ですが気にしないでください。

連続して円形のショットを放ちます。

リングが広がっていく後半さらにそれぞれの弾が枝分かれしていくところもポイントです。

初期化部分

if (boss_shot[i].init_flag == 0){
	boss_shot[i].gamecount_point[0] = gamecount;
	boss_shot[i].gamecount_point[1] = gamecount;
	boss_shot[i].max_bullet = 40;
	for (int j = 0; j < 20; j++){
		boss_shot[i].angle[j] = (18 * j) * DX_PI / 180;
		boss_shot[i].angle[j + 20] = (18 * j) * DX_PI / 180;
		boss_shot[i].angle2[j] = ((18 * j) + 4) * DX_PI / 180;
		boss_shot[i].angle2[j + 20] = ((18 * j) - 4) * DX_PI / 180;
	}
	for (int j = 0; j < 300; j++){
		boss_shot[i].x[j] = 0;
		boss_shot[i].y[j] = 0;
	}	
	boss_shot[i].init_flag = 1;
}
else{
	/*移動部分*/
}

初期化部分は「gamecount」を2つ分保存、一度に40発弾を放つので最大弾数である「max_bullet」は「40」にします。

そして角度を設定する部分ですが、最初20方向に、後半それが枝分かれしていくようにしたいので「18度」区切りで最初の20方向、そしてそれらにプラスマイナス4度したような角度を設定します。

このあたりはお好みでいろいろな値を試してみてください。

最後に座標を初期化すれば完了です。

ボスの位置に関わらず画面中央から発生されるショットになりますので初期座標は中央の(0,0)で初期化します。

移動部分

for (int j = 0; j < boss_shot[i].max_bullet; j++){
	if (boss_shot[i].x[j] < 80 && boss_shot[i].x[j] > -80 && boss_shot[i].y[j] < 80 && boss_shot[i].y[j] > -80){
		boss_shot[i].x[j] += cos(boss_shot[i].angle[j % 40]) * 1;
		boss_shot[i].y[j] += sin(boss_shot[i].angle[j % 40]) * 1;
	}
	else{
		boss_shot[i].x[j] += cos(boss_shot[i].angle2[j % 40]) * 1;
		boss_shot[i].y[j] += sin(boss_shot[i].angle2[j % 40]) * 1;
	}
}
if (gamecount == boss_shot[i].gamecount_point[1] + 80
	 && boss[boss_shot[i].boss_num].move_flag == 1){
	if (boss_shot[i].max_bullet < 280){
		boss_shot[i].max_bullet += 40;
		boss_shot[i].gamecount_point[1] += 80;
	}
}

続いて移動部分です。

ポイントは先にも言った通り最初20方向、後ほど枝分かれする点です。

まずは最初の20方向の部分です。

if (boss_shot[i].x[j] < 80 && boss_shot[i].x[j] > -80 && boss_shot[i].y[j] < 80 && boss_shot[i].y[j] > -80){
	boss_shot[i].x[j] += cos(boss_shot[i].angle[j % 40]) * 1;
	boss_shot[i].y[j] += sin(boss_shot[i].angle[j % 40]) * 1;
}

言い方が少しおかしいかもしれませんが、ひとまず前後左右中心から80ピクセルに達するまで最初に設定した20方向に40発分の弾を飛ばします。

40発分で20方向なので2発の弾が重なりながら移動していくようなカタチになります。

それではここから枝分かれさせていきます。

else{
	boss_shot[i].x[j] += cos(boss_shot[i].angle2[j % 40]) * 1;
	boss_shot[i].y[j] += sin(boss_shot[i].angle2[j % 40]) * 1;
}

枝分かれと言っても特に難しい事をするワケではなく初期化時に設定したもう一つのずらした方の角度に変えるだけです。

if (gamecount == boss_shot[i].gamecount_point[1] + 80
	 && boss[boss_shot[i].boss_num].move_flag == 1){
	if (boss_shot[i].max_bullet < 280){
		boss_shot[i].max_bullet += 40;
		boss_shot[i].gamecount_point[1] += 80;
	}
}

あとは一定時間ごとに「max_bullet」を増やす処理を加えれば完了です。

画像(cdxs_20_2)

連続リングショット!

連続リングショット2

今度は画面下方から上方に向けて円を描くように、円形のショットを連続して放つというショットになります。

初期化部分

if (boss_shot[i].init_flag == 0){
	boss_shot[i].gamecount_point[0] = gamecount;
	boss_shot[i].gamecount_point[1] = gamecount;
	boss_shot[i].max_bullet = 12;
					
	int angle[12] = {270,240,300,210,330,180,0,150,30,120,60,90};
	for (int j = 0; j < 12; j++){
		boss_shot[i].angle[j] = angle[j] * (DX_PI / 180);
	}
	for (int k = 0; k < 12; k++){
		for (int j = 0; j < 12; j++){
			boss_shot[i].x[j + (k * 12)] = cos(boss_shot[i].angle[k]) * 150;
			boss_shot[i].y[j + (k * 12)] = sin(boss_shot[i].angle[k]) * 150;
		}
	}
	boss_shot[i].init_flag = 1;
}
else{
	/*移動部分*/
}

ポイントは座標の初期化部分です。

int angle[12] = {270,240,300,210,330,180,0,150,30,120,60,90};
for (int j = 0; j < 12; j++){
	boss_shot[i].angle[j] = angle[j] * (DX_PI / 180);
}

この部分で規則的な12方向の角度を設定、さらにその角度をラジアル値変換、「boss_shot[].angle[]」に保存しなおします。

そしてその角度を使って座標を初期化していきます。

for (int k = 0; k < 12; k++){
	for (int j = 0; j < 12; j++){
		boss_shot[i].x[j + (k * 12)] = cos(boss_shot[i].angle[k]) * 150;
		boss_shot[i].y[j + (k * 12)] = sin(boss_shot[i].angle[k]) * 150;
	}
}

この部分ですね。

ちょっとこれだけだとわかりづらいと思いますので実際どのような値が「boss_shot.x」と「boss_shot.y」に入るのかを見てみましょう。

画像(cdxs_20_3)

非常に見にくいですが(x座標,y座標)として特定の値が保存されているのがかろうじて確認できるかと思います。

具体的に値を抜き出してみると

(0,-150)(-75,-129)(74,-129)(-129,-74)(129,-75)(-150,0)(150,0)(-129,75)(129,74)(-74,129)(75,129)(0,150)

このような値が入っております。

ではこの座標通りに点を打ってみると・・・、

画像(cdxs_20_4)

円形になりました!

中心から半径「150」ピクセルの円上の任意の位置に座標を設定していくという作業だったというワケです。

さらにこのそれぞれの点の位置から同じように円形にショットを飛ばしていきます。

移動部分

for (int j = 0; j < boss_shot[i].max_bullet; j++){
	boss_shot[i].x[j] += cos(boss_shot[i].angle[j % 12]) * 1;
	boss_shot[i].y[j] += sin(boss_shot[i].angle[j % 12]) * 1;
}
if (gamecount == boss_shot[i].gamecount_point[1] + 50
	 && boss[boss_shot[i].boss_num].move_flag == 1){
	if (boss_shot[i].max_bullet < 132){
		boss_shot[i].max_bullet += 24;
		boss_shot[i].gamecount_point[1] += 50;
	}
	else if (boss_shot[i].max_bullet == 132){
		boss_shot[i].max_bullet += 12;
	}
}

初期化の時に設定した角度を再利用します。

12方向分だけ用意してあるので「angle[j % 12]」と余りを取れば繰り返しその用意した角度を使う事ができますね。

あとは一定時間で同じように最大弾数「max_bullet」を増加させれば完了です。

頭だけ12発、その後24発ずつ増加し、最後再び12発分だけ増加させているトコロにも注目です。

画像(cdxs_20_5)

連続リングショット2!

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

中間ソース19

次回はボスの動きを追加したいと思います。

次回

二十一日目 ボスの動きを追加

□ページの先頭へ□

□目次へ戻る□

□HOME□

広告

↓発売日:2016年02月29日↓

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

新品価格
¥2,462から
(2018/10/8 23:25時点)

↓発売日:2018年06月22日↓

スッキリわかるC言語入門

新品価格
¥2,916から
(2018/10/8 23:15時点)

↓発売日:2018年03月09日↓

C言語本格入門 ~基礎知識からコンピュータの本質まで

新品価格
¥3,218から
(2018/10/8 23:16時点)

↓発売日:2017年06月14日↓

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

新品価格
¥2,700から
(2018/10/8 23:17時点)

↓発売日:2018年05月21日↓

作って身につく C言語入門

新品価格
¥2,462から
(2018/10/8 23:18時点)

↓発売日:2017年12月07日↓

新・標準プログラマーズライブラリ C言語 ポインタ完全制覇

新品価格
¥2,678から
(2018/10/8 23:19時点)

↓発売日:2017年02月08日↓

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

新品価格
¥2,700から
(2018/10/8 23:20時点)

↓発売日:2017年09月26日↓

かんたん C言語 [改訂2版] (プログラミングの教科書)

新品価格
¥2,916から
(2018/10/8 23:22時点)