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

ボスの状態遷移

前回までで、ボスの移動&攻撃パターン、当たり判定は作ったので、
今回はボスがダメージを負うごとにその攻撃&移動パターンを変化させるようにしてみましょう。

パターンは三つ作ったので、ボスのHPが2/3以下になったら第二パターン、
1/3以下になったら第三パターンになるように変化させるようにします。
ボスのHPを計算をすることになるので、ボスのHPをdefine.hで定義しておきます。
#define BOSS_HP 500
また、boss.cppのコンストラクタでボスの初期HPもこれで設定しておきます。
hp=BOSS_HP;
さらに、ボスがパターンを変化させる際に、一度真ん中上部付近に戻ってから、
次の攻撃パターンに移るようにさせます。
その攻撃パターンが変化する間にプレイヤーの攻撃があたってしまうと卑怯なので、
その間はボスが無敵になるように無敵フラグを設けます。
//ダメージを負わないフラグ
bool no_damage;
//最初はfalseに設定
no_damage = false;
当然宣言はboss.hで、初期値の設定はboss.cppで行っています。
さらにこのフラグを取得できるように、bool GetNodamageFlagという関数を作ります。
bool GetNodamageFlag();
bool BOSS::GetNodamageFlag()
{
	return no_damage;
}
これも宣言はboss.hでpublicで宣言し、定義部分はboss.cppに書いています。

次にボスがパターンを変化させる際に、一度真ん中上部付近に移動しますが、
その部分の関数MoveToDefault関数を作ります。
void MoveToDefault();
//定位置に戻る
void BOSS::MoveToDefault()
{

	double temp;

	angle+=2;

	temp=sin(angle*PI/180);

	x = prev_x+temp*movex;
	y = prev_y+temp*movey;

	//指定した位置まで移動したら
	if(angle==90){

		//次の移動&ショットパターンに変更
		SetMovePattern(prev_move_pattern+1);
		SetShotPattern(prev_shot_pattern+1);
		//無敵フラグを戻す
		no_damage = false;

		//移動パターンが3なら
		if(move_pattern==3)
			MoveInit(200,160,2);
	}

}
これはprivateで宣言しています。外部から呼び出す必要はないので。
sin値を使って滑らかに移動させるために、
仕組み的には移動パターン3と同じにしてあります。
angleが90に達したら、指定した位置まで移動したことになるので、
SetMovePatternとSetShotPattern関数を使ってパターンを変更しています。
この関数は以下のようにprivateで定義しています。
void BOSS::SetMovePattern(int pattern)
{
	prev_move_pattern = move_pattern;
	move_pattern = pattern;
}
void BOSS::SetShotPattern(int pattern)
{

	prev_shot_pattern = shot_pattern;
	shot_pattern = pattern;

}
prev_move_patternとprev_shot_patternという変数は、
boss.hにint型で新たに宣言しておきました。
これにパターン変更前のパターンを保存しておき、
実際のパターンmove_patternとshot_patternの値を変更してます。
shot_patternが4の時の処理についてはshot関数内に書いてますが、
特に何もしていないので、shot関数のswitch文のところで、
default:
	//何もしない
	break;
のように書いてるだけです。
4の時の処理は書いてないのでdefaultの部分の処理が実行されます。

上記のMoveToDefault関数を見てもらうとわかるとおり、
前回のパターンであるprev~の変数に1を足した値を設定することで、
次のパターンへの変更を行っています。
no_damageフラグも指定した位置まで移動したらもう無敵である必要はなくなるので、
フラグを戻しています。
移動パターンが3の場合は、MoveInitでangle変数の初期化や次の移動位置等をセットしておく必要があるので、
上記のように設定しています。
このMoveToDefault関数は移動パターン4とします。
ゆえに移動パターン関数を制御しているMove関数は以下のようになります。
void BOSS::Move()
{

	switch(move_pattern){
		case 0:
			Appear();
			break;
		case 1:
			MovePattern1();
			break;
		case 2:	
			MovePattern2();
			break;
		case 3:
			MovePattern3();
			break;
		case 4:
			MoveToDefault();
			break;
	}

}
つまり、ボスが移動パターンを変化させる際はmove_pattern変数を4に変更する必要があります。
また、MoveToDefault関数はMovePattern3と同じ仕組みを使っているので、
angle変数やprev_x,prev_y,movey,movex等の変数の値のセットが必要です。
ですのでその設定を行うSetDamageSetting関数を作ります。
void BOSS::SetDamageSetting()
{

	prev_x=x;
	prev_y=y;

	movex=200-x;
	movey=80-y;

	angle=0;

	no_damage = true;

	SetMovePattern(4);
	SetShotPattern(4);

}
prev_x,prev_yはボスの座標をセットしておき前回座標とします。
movex,moveyは現在の位置から目的の位置までの距離を表す変数なので、
真ん中上部付近の定位置の座標である(200,80)までの距離を求めています。
そしてangle変数を0にし、無敵フラグno_damageフラグを立てています。
そして、MoveToDefault関数の移動パターンである4に移動パターンを設定しています。
同じくショットパターンも特に何もしない4に設定してあります。

これでパターン変化の部分のコードは大体書けましたが、
最初に言ったとおりボスのHPの減少具合を基にパターンを変化させると言いました。
どう判定させるかというと以下のような判定方法をつかいます。
//ボスの前回HPがHPが2/3以上で、現HPが2/3以下なら
if(BOSS_HP*2/3 >= bhp && boss.GetPrevHp() > BOSS_HP*2/3){
これはcontrol.cppのコードの一部です。
bhpというのがボスの現在のHPでGetPrevHp関数がボスの前回のHPを返す変数です。
常にループしている状況下で、ボスの現HPがボス初期HPの2/3以下になり、
前回HPがボス初期HPの2/3より大きい条件は1度しか起こりえません。
この条件式ならそれをクリアできます。
パターンごとにフラグを設ける方法もありますけど、
このほうが手っ取り早いのでこの方法を使いました。
ということで、前回HPを取得するGetPrevHp関数をpublicで
BOSSクラスに定義します。
int GetPrevHp();
int BOSS::GetPrevHp()
{
	return prev_hp;

}
prev_hpという変数は新たにboss.hで宣言してます。
単純にこの値を返すだけです。
ではこのprev_hpの値はどこで設定しているのか?というと、
ボスのHPを減らすかんすうであるHpSet関数内で設定しています。
int BOSS::HpSet(int damage)
{
	prev_hp = hp;
	hp-=damage;

	return hp;
}
ダメージを減らす前にhpをprev_hpに代入しているので、
prev_hpが前回HPになるというわけです。

さて、これで準備は整ったので、後はCONTROLクラス内で、
色々と定義した関数を呼び出すだけです。
以下はBossCollisionAll関数の一部のコードです。
	//まず無敵フラグがたってないかをチェック。
	if(!boss.GetNodamageFlag()){
		//プレイヤーのショットとボスとの当たり判定
		for(int i=0;i<PSHOT_NUM;++i){
			if(player->GetShotPosition(i,&px,&py)){
				boss.GetPosition(&bx,&by);
					//当たり判定
					if(CircleCollision(PSHOT_COLLISION,BOSS_COLLISION,px,bx,py,by)){
						//当たっていれば、hpを減らす
						bhp=boss.HpSet(1);
						//当たった弾のフラグを戻す
						player->SetShotFlag(i,false);
						//得点を加える
						score->SetScore(CURRENT_SCORE,10);

						char buf[100];
						sprintf(buf,"%d",bhp);
						SetMainWindowText(buf);
						
						//ボスの前回HPがHPが2/3以上で、現HPが2/3以下なら
						if(BOSS_HP*2/3 >= bhp && boss.GetPrevHp() > BOSS_HP*2/3){
							//ダメージエフェクトを出す
							EnemyDeadEffect(bx,by);
							//ダメージ音を鳴らす
							edead_flag=true;
							//さらに得点を加える
							score->SetScore(CURRENT_SCORE,1000);
							//アイテムを出す。
							for(int z=0;z<ITEM_NUM;++z){
								if(!item[z]->GetFlag()){
									//アイテムの初期座標をばらけさせる。
									ix=(rand()%100-51)+bx;
									iy=(rand()%100-51)+by;
									item[z]->SetFlag(ix,iy,rand()%2);
									++itemnum;
									//5個出したらループを抜ける
									if(itemnum==5){
										break;
									}
								}
							}
							boss.SetDamageSetting();
						}else if(BOSS_HP/3 >= bhp && boss.GetPrevHp() > BOSS_HP/3){
							//ダメージエフェクトを出す
							EnemyDeadEffect(bx,by);
							//ダメージ音を鳴らす
							edead_flag=true;
							//さらに得点を加える
							score->SetScore(CURRENT_SCORE,1000);
							//アイテムを出す。
							for(int z=0;z<ITEM_NUM;++z){
								if(!item[z]->GetFlag()){
									//アイテムの初期座標をばらけさせる。
									ix=(rand()%100-51)+bx;
									iy=(rand()%100-51)+by;
									item[z]->SetFlag(ix,iy,rand()%2);
									++itemnum;
									//5個出したらループを抜ける
									if(itemnum==5){
										break;
									}
								}
							}
							boss.SetDamageSetting();
						}else if(bhp<=0){
							//フラグを戻す
							boss.SetFlag(false);
							//消滅エフェクトを出す
							EnemyDeadEffect(bx,by);
							//消滅音を鳴らす
							edead_flag=true;
							//さらに得点を加える
							score->SetScore(CURRENT_SCORE,10000);
							//アイテムを出す。
							for(int z=0;z<ITEM_NUM;++z){
								if(!item[z]->GetFlag()){
									//アイテムの初期座標をばらけさせる。
									ix=(rand()%100-51)+bx;
									iy=(rand()%100-51)+by;
									item[z]->SetFlag(ix,iy,rand()%2);
									++itemnum;
									//10個出したらループを抜ける
									if(itemnum==10){
										break;
									}
								}
							}
							
						}
					}
			}
		}

	}
まずGetNodamageFlag関数で無敵フラグがたっていないかチェックしてます。
立っていなければ通常の当たり判定の処理に移っています。
当たり判定で当たった場合の処理のところを見てもらうと、
先ほど説明したHPが2/3以下になった時のコードが書かれています。
ボスが消滅したときと同じようにエフェクトを出しています。
本当ならダメージを負ったときはダメージ専用のエフェクトを出したほうがいいんですが、
今回はボス消滅時と同じエフェクト、同じ音を使ってます。
さらに得点を少し加えたあと、アイテムを5個出現させてます。
消滅時は10個ですが、ダメージ時は5個と少なくしたほうがゲームっぽいですしね。

これと同じ処理をその下のBOSSのHPが1/3以下になったときの条件式のところでもやっています。
今回は1/3なので単純にBOSS初期HPを3で割った値で比較してるだけです。

ココまでのソースコードはコチラです
ソースコード(33章)
音声ファイルや画像ファイルは各自でそろえてください。
このソースコードで起動すると以下の動画のようにボス出現から、
移動&攻撃パターン変化~消滅まで動かせることができるようになります。


次回はボスダメージ時の画面揺らしを実装してみようかなと思っています。

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