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

ボスを作ろう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)】に進む
>> シューティングゲーム作成入門トップに戻る