>> シューティングゲーム作成入門トップに戻る

ボスを作ろう3(ショット発射)

今回はボスの「ショットの発射について説明します。

仕組み的には通常の敵のショットとほぼ一緒です。
ボスはショットを繰り返し撃つのでその部分の仕組みを付け加えるだけです。
ショット部分のコードを説明する前に、
ショットを発射するまでの流れを説明します。
ボスが上から出現してから一定のところまで移動すると上下にゆらゆらと滞在を続けますが、
その滞在し始めたときに弾を発射し始めるようにします。
そのためのフラグとしてshotflagというフラグを新たにBOSSクラスに持たせます。
コンストラクタではfalseで初期化しますが、
登場時の移動パターンを示すappear関数が終了したときにフラグを立てるようにします。

void BOSS::Appear()
{
	double temp;

	angle+=2;

	temp=sin(angle*PI/180);

	x = 200;
	y = prev_y+temp*movey;

	//提位置まで移動したら移動パターンを1に変更
	if(angle==90){
		move_pattern=1;
		angle=0;
		shotflag=true;
	}
}

angleが90に達したときにshotflagをtrueにしています。
90に達してからはボスは上下移動を開始しますが、
その直後から弾の発射が行われることになります。
今回はこのタイミングにしましたが、
それぞれ自分の好きなタイミングでフラグを立てるタイミングを変えてください。

次にshot関数です。

void BOSS::Shot()
{
	//何発発射したか
	int num=0;
	//空いてる弾の添え字
	int index;

	//scountを戻すかどうかのフラグ
	bool scflag=false;

	
	CONTROL &control = CONTROL::GetInstance();

	double px,py;
	static double trad;

	if(!damageflag){

		
		control.GetPlayerPosition(&px,&py);

		if(scount==0)
			trad=atan2(py-y,px-x);
			
		//サウンドフラグを戻す
		s_shot=false;
		
		//弾のセット
		switch(shot_pattern){

			case 0:
				if(scount%5==0 && scount<=15){

					while((index=ShotSearch())!=-1){

						shot[index].gh=gh_shot[1];
						shot[index].pattern=0;
						shot[index].speed=6;

						if(num==0){
							shot[index].rad=trad-(10*PI/180);
						}else if(num==1){
							shot[index].rad=trad-(5*PI/180);
						}else if(num==2){
							shot[index].rad=trad;
						}else if(num==3){
							shot[index].rad=trad+(5*PI/180);
						}else if(num==4){
							shot[index].rad=trad+(10*PI/180);
						}


						++num;
						
						s_shot=true;

						if(num==5){
							break;
						}
					}
				}
				
				break;
			case 1:


				break;
			case 2:
				break;
			case 3:
				break;
		}

		for(int i=0;i<BOSS_SHOTNUM;++i){
			if(shot[i].flag){
				switch(shot[i].pattern){

					case 0:
						shot[i].x+=shot[i].speed*cos(shot[i].rad);
						shot[i].y+=shot[i].speed*sin(shot[i].rad);

						if(scflag==false && scount==40){
							scflag=true;
						}

						break;
					case 1:
						break;
					case 2:
						break;
					case 3:
						break;
				}

				//弾がはみ出てたらフラグを戻す
				if(ShotOutCheck(i)){
					shot[i].flag=false;
				}
			}
		}

		++scount;

		if(scflag){
			scount=0;
		}

	}
}

最初に一時変数をいくつか宣言してますが、
敵クラスを作ったときと同じか、書いてあるとおりです。
まずdamageflagがfalseの時に処理するようにif文で制御しています。
このフラグはボスが一定ダメージを負ったら攻撃パターンを変化させるんですが、
その変化するまでの間に立てておくフラグです。
別の章で説明しますが、このフラグが立ってるときは攻撃を停止したり、ダメージを負わないようにします。
まず最初にプレイヤーの座標を取得し、
scountが0の時だけプレイヤーとボスがなす角度を求めてます。
scountはボスが弾を発射し始めたらカウントを増やしていく変数です。
s_shotは弾の発射音用のフラグです。敵クラスの時と一緒なので説明は省略します。

次にswitch文に入り、shot_patternによって処理を分岐させています。
今回はとりあえず0の1パターンだけ作ってます。
そのif文の条件は、
scountが5で割り切れ、かつ15以下となっています。
つまり5ループに一回実行され、0,5,10,15の計4回実行されることになります。
その下のwhile文ではShotSearch関数を実行しています。
以下のような関数です。

int BOSS::ShotSearch()
{
	bool check=false;
	int i;
		
	for(i=0;i<BOSS_SHOTNUM;++i){
		if(shot[i].flag==false){
			check=true;
			break;
		}
	}
	if(check){
		shot[i].flag=true;
		shot[i].x=x;
		shot[i].y=y;
	}else{
		i=-1;
	}

	return i;
}

この関数はあらかじめ宣言しておいた弾構造体shotの配列の中から、
現在フラグが立っていない(使われていない)弾を探し出して、
座標やフラグを立てたあと、その添え字を戻り値として返す関数です。
仕組みは簡単で、弾の数だけforループでループし、
フラグが立っていないやつが見つかれば一時フラグcheckを立てループを抜けます。
その後フラグが立っていれば、弾構造体のフラグを立て、座標をセットします。
もし立っていなければ現在弾に空きがないということでiに-1を代入します。
最後にiを戻り値として返しています。
これでちゃんと弾があればその添え字が、弾がなければ-1が返るというわけです。

では先ほどのshot関数に戻ります。
whileループ文でこのShotSearch関数を実行しています
戻り値をindexという変数に格納し、-1ならループを抜けるようにしています。
次に弾のグラフィックハンドル、ショットパターン、弾のスピードを代入してます。
その下のif分は弾の角度の代入です。
numという変数は何発弾をセットしたかを表しています。
tradが丁度プレイヤーにまっすぐ向かう角度になるので、
その角度を基準に左に5度、10度、右に5度、10度ずつずらした角度を
それぞれ代入しています。
numはwhileループ中に加算され、5になったらループを抜けています。
こうすることでプレイヤーの方に向かって5列の弾が発射されることになります。
scount5回に一回、15まで実行されるので、この5列の弾が4連続で発射されることになります。
ループ内では発射音を鳴らすためにs_shotフラグを立てています。
ここまでで弾のセットは終わりです。

次にその下のforループ文を見てください。
ここも弾の数だけ弾構造体をループさせてフラグが立っているもののみを処理しています。
つまり弾の移動処理を行っています。
その下のswitch文で弾のパターンごとに処理を分岐させています。
座標の計算は今までと変わりません。
xはcos関数に弾の角度(ラジアン)を指定しスピードをかける。
xはsin関数に弾の角度(ラジアン)を指定しスピードをかける。
同じですね。
その下のif文では一時フラグ変数scflagがfalseかつscountが40になったら
scflagをtrueにするようにしてます。
もっと下の方にあるコードを見てもらうと、scflagがtrueだとscountをゼロにしてます。
これはボスにもう一度最初から弾をセットして撃たせるための仕組みです。
パターン1の弾のセットはscountが15までセットを行うので、
15の時に最後の弾がセットされてから、40までの25カウント分の間ショットが待機されます。
40になったらscountをゼロにするので、また最初からショットが発射されるという仕組みです。

ループ内でShotOutCheck関数を実行していますが、
これは敵のショットのところでも説明したとおり、弾が外にはみ出していないかをチェックする関数です。
まったく一緒ですので説明は省略します。

次にDraw関数とAll関数です。

void BOSS::Draw()
{

	//弾から最初に描画
	for(int i=0;i<BOSS_SHOTNUM;++i){
		if(shot[i].flag){
			DrawRotaGraph(shot[i].x,shot[i].y,1.0,shot[i].rad+90*PI/180,shot[i].gh,TRUE);
		}
	}


	//弾があたったときはダメージ用の画像を描画、
	if(damageflag){
		DrawRotaGraph(x,y,1.0,0,gh_face[1],TRUE);
	}else{
	//何も無いときは通常描画
		DrawRotaGraph(x,y,1.0,0,gh_face[0],TRUE);
	}


	damageflag=false;
}
void BOSS::All()
{
	Move();

	if(shotflag){
		Shot();
	}

	Draw();

	++count;
}

弾の描画については敵のショットと一緒なので説明は省略します。
All関数については、shotflagがtrueの時だけshot関数を実行するようにしています。
また、ショット音フラグ取得用のGetShotSound関数も別で作ってあります。
敵クラスの時と一緒なので説明は省きます。
これでControlクラスでボスクラスのインスタンスのAll関数を実行すると以下の動画のように
ショットが発射されます。(※まだ当たり判定はつけてません)

今回の説明は以上です。
次回はショットパターンを増やしてみましょう。

>> 【ボスを作ろう4(ショットパターン追加1)】に進む
>> シューティングゲーム作成入門トップに戻る
●更新履歴
2016/08/16 Java入門ページにページを幾つか追加
2016/04/08 Java入門ページ作成
2016/03/09 メニューレイアウト変更。ブラウザキャッシュのクリアをお願い致します。
2016/03/09 PDOトランザクション、自動コミットモードをオフ追加
2016/03/09 PDO 例外処理 try catch追加
2016/03/09 PDO update文実行追加
2016/03/09 PDO delete文実行追加
2016/03/09 PDO insert文実行追加
2016/03/09 PDO selectでデータを取得、fetchAll、queryメソッド追加
2016/03/09 PDO bindValueとbindParamの違い追加
2016/03/09 PDO prepare プリペアドステートメントの使い方追加
2016/03/04 ソースコードをクリップボードにコピーする機能を追加
2016/03/04 C言語、C++のページのソースコードを一部修正
2014/01/31 C言語関数一覧ページに11ページほど追加
2014/01/31 C言語関数一覧ページに30ページほど追加
2014/01/30 C言語関数一覧ページ作成中
2013/07/01 レイアウト変更に伴いブラウザキャッシュのクリアをお願いします。
2013/07/01 MySQL入門ページ作成
2013/07/01 PHP入門ページにSQLite学習項目追加
2013/06/25 ドメイン変更、レイアウトを一部変更
2013/03/14 レイアウトを一部変更
2012/08/13 C言語よくある課題・宿題ページ開設!
2012/08/13 シューティングゲーム作成第33章追加!
2012/08/11 ドメイン変更&サーバ移設完了
2012/04/21 シューティングゲームプログラミング第2,3章の内容を修正
2012/04/19 シューティングゲームプログラミング第2章の内容を修正
2012/04/03 Googleカスタム検索を設置!
2012/04/03 シューティングゲームプログラミング第32章追加!
2012/04/03 シューティングゲームプログラミング第31章追加!
2012/03/31 サイトをリニューアルしました!
2012/03/25 シューティングゲームプログラミング第30章追加!
2012/03/19 シューティングゲームプログラミング第29章追加!
2012/03/16 シューティングゲームプログラミング第28章追加!
2012/02/27 シューティングゲームプログラミング第27章追加!
2012/02/03 シューティングゲームプログラミング第26章追加!
2012/01/31 シューティングゲームプログラミング第25章追加!
2012/01/20 シューティングゲームプログラミング第23,24章追加!
2012/01/11 シューティングゲームプログラミング第22章追加!
2012/01/05 トップページ、ゲームプログラミング関連のトップページのデザインを変更
2012/01/04 シューティングゲームプログラミング第21章追加!
2012/01/01 シューティングゲームプログラミング第20章追加!
2011/12/25 シューティングゲームプログラミング第19章追加!
2011/12/22 シューティングゲームプログラミング第18章追加!
2011/12/18 シューティングゲームプログラミング第17章追加!
2011/12/17 シューティングゲームプログラミングページOPEN!
2011/11/21 ゲームプログラミングページOPEN!
2011/11/21 サイトデザインを大幅に変更
2011/11/17 TOPページのデザインを変更。相互リンクページに、複数サイト追加。
2011/11/06 WINAPI学習ページ(33~36章)追加
2011/11/05 WINAPI学習ページ(20~32章)追加
2011/10/27 WINAPI学習ページ(14~19章)追加
2011/10/21 WINAPI学習ページ(13章)追加
2011/10/21 サイトマップ、連絡ページ追加
2011/10/17 WINAPI学習ページ(6~11章)追加
2011/10/16 WINAPI学習ページ(1~5章)追加
2011/10/13 全体のレイアウト変更
2011/10/07 PHP学習ページ(8~11章)追加
2011/10/06 PHP学習ページ(1~7章)作成
2011/10/06 JavaScriptリファレンスページ作成
2011/10/05 C言語学習ページ発展編(10~14章)追加
2011/10/04 C言語学習ページ発展編(1~9章)追加。
2011/10/03 HTML/CSSリファレンスのページ追加。(個々の詳細ページは作成中)
2011/09/30 HTML学習ページ(8章)追加
2011/09/29 JavaScript学習ページ(12~17章)追加
2011/09/28 JavaScript学習ページ(1~11章)追加
2011/09/27 HTML学習ページ(4~7章)追加
2011/09/26 C言語学習ページ(27章)追加、C++学習ページ(17章)、HTML学習ページ(1~3章)追加
2011/09/25 C言語学習ページ(23~26章)を追加
2011/09/24 C++学習ページ(9~16章)追加
2011/09/23 C++学習ページ(3~8章)追加
2011/09/22 C言語の学習ページ(22章)とC++学習ページ(1~2章)追加
2011/09/21 C言語の学習ページ(15章~21章)を追加
2011/09/20 C言語の学習ページ(10章~14章)を追加
2011/09/19 サイト作成(随時更新予定)