>> シューティングゲーム作成入門トップに戻る
今回は前回とは逆のボスとプレイヤーのショットとの当たり判定を実装します。
これも基本的には敵との当たり判定の時とあまり変わりません。
違うところと言えば、ボスにはHPが存在するので、
弾があたったらそのHPを減らし、HPが0になったら消滅させる必要があります。
まずそのHPを設定する関数が必要になりますね。
さらに、プレイヤーのショットの方ですが、
プレイヤーのショットには追跡弾(ホーミング弾)を実装していたと思いますが、
これは通常の敵に対して動作するようにしかコードを書いてないので、
ボスの時にも動作させるようにコードを書き加える必要があります。
また、それに伴ってPLAYERクラスからもボスの座標を取得する必要がありますので、
CONTROLクラスにボス座標取得用の関数を作成します。
これがボスのHP取得用の関数です。
あらかじめボスクラスにはhpという変数を定義して初期値を500に設定してます。
この関数では引数に渡されたダメージ数をhpから引き、
そのhpを戻り値として返しているだけです。
次にCONTROLクラスにボス座標取得用の関数を作成します。
特に説明は不要ですね。
ボスクラスのGetPosition関数で座標を取得し、
それを引数のポインタ経由で値を返してるだけです。
追跡弾のコードは後回しにして、
先に当たり判定部分のコードをお見せします。
先ほどのボスのショットとプレイヤーの当たり判定のコードは無視してください。
まずプレイヤーの弾数分だけループさせて、弾の座標を取得し、
ボスの座標を取得した後、BossCollisionAll関数で当たり判定を行っています。
BOSS_COLLISIONという定数はdefine.hで以下のように定義しています。
#define BOSS_COLLISION 60
60なのでボスの画像の約半分ですね。
弾がボスに当たったら、先ほどのHpSet関数を使ってHPを1減らしています。
さらに、当たった弾のフラグを戻して弾を消し、スコアに得点を加算しています。
さらにその下のif文を見てください。
bhpという変数はHpSet関数の戻り値であるボスのHPが入った変数です。
それが0以下、つまり倒したときにこのif文内の処理が実行されるわけです。
if文内では、SetFlag関数でボスのフラグを戻し、
消滅エフェクト、消滅音、得点の設定などを行っています。
最後にアイテムも出現させていますが、
ボスなので少し多めにアイテムを出現させようと思います。
少しばらけて出現させたほうがよいので、初期座標をすこしずらしています。
(rand()%100-51)の計算をするだけで、-50~50までのランダムな値が取得できます。
これにbx,byつまりボスの座標を足すことで、
ボスの座標から-50~50の範囲でかつランダムにアイテムの初期座標が
設定されることになります。
またアイテムの、SetFlag関数の第三引数はアイテムの種類を表していますが、
0と1の2種類しかないので、rand()%2の計算だけで、
0か1のランダムな値を設定することができます。
さらにその下のif文でitemnumという変数、
これはアイテムを何個出現させたかを表す変数ですが、
これが10個に達したらループを抜けるようにしています。
これで、ボスの座標から-50~50の範囲で、かつランダムに、
10個のアイテムを出現させることができるようになりました。
当たり判定の部分の説明は以上です。
次にプレイヤーの追跡弾部分のコードです。
ちょっと長いですが、
追加した部分は、「ボスがいる場合はボスとの角度を取得」と書かれている、
3行のif文だけです。
controlクラスのGetBossFlagを実行して、
ボスのフラグを取得し、ボスフラグが立っていれば、
ボスが出現していることになるので、
その場合は、先ほど作成したGetBossPosition関数を使って、
ボスの座標を取得しています。
そしてatan2関数を使って、ボスの座標とプレイヤーのショットがなす角度を
求め、trad変数に代入してます。
trad変数はその下を見れば分かるように、
弾構造体の実際の角度radに代入しているので、
これが実際の弾の角度になるわけです。
ボスが出現していない場合は、ここのif文は実行されないので、
通常の敵とのなす角度が代入されることになります。
以上のコードを書いて、当たり判定を追加すると、
以下の動画のようにプレイヤーのショットとボスとの当たり判定を実装できます。
正直、音とかエフェクトが弱いせいでかなりしょぼく見えますが、
その辺はご了承下さい。
ボス撃破時に画面を揺らしたり、もっと音を変えたり、エフェクトを激しくしたりすれば、
もっとかっこよくなると思います。
今回の説明は以上です。
次回の内容は未定ですのでしばらくお待ち下さい。
>> 【ボスの状態遷移】に進む
>> シューティングゲーム作成入門トップに戻る
ボスとプレイヤーのショットとの当たり判定
これも基本的には敵との当たり判定の時とあまり変わりません。
違うところと言えば、ボスにはHPが存在するので、
弾があたったらそのHPを減らし、HPが0になったら消滅させる必要があります。
まずそのHPを設定する関数が必要になりますね。
さらに、プレイヤーのショットの方ですが、
プレイヤーのショットには追跡弾(ホーミング弾)を実装していたと思いますが、
これは通常の敵に対して動作するようにしかコードを書いてないので、
ボスの時にも動作させるようにコードを書き加える必要があります。
また、それに伴ってPLAYERクラスからもボスの座標を取得する必要がありますので、
CONTROLクラスにボス座標取得用の関数を作成します。
int BOSS::HpSet(int damage) { hp-=damage; return hp; }
これがボスのHP取得用の関数です。
あらかじめボスクラスにはhpという変数を定義して初期値を500に設定してます。
この関数では引数に渡されたダメージ数をhpから引き、
そのhpを戻り値として返しているだけです。
次にCONTROLクラスにボス座標取得用の関数を作成します。
void CONTROL::GetBossPosition(double *x,double *y) { double bx,by; boss.GetPosition(&bx,&by); *x=bx; *y=by; }
特に説明は不要ですね。
ボスクラスのGetPosition関数で座標を取得し、
それを引数のポインタ経由で値を返してるだけです。
追跡弾のコードは後回しにして、
先に当たり判定部分のコードをお見せします。
void CONTROL::BossCollisionAll() { double px,py,bx,by,ix,iy; int bhp=0; //出すアイテム数 int itemnum=0; //グレイズとヒットしたかのフラグ bool hflag=false,gflag=false; //ボスの弾の種類 int type; //プレイヤーのショットとボスとの当たり判定 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); //もしボスのHPが0以下なら 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; } } } } } } } //ボスのショットとプレイヤーとの当たり判定 if(!player->GetDamageFlag()){ player->GetPosition(&px,&py); for(int i=0;i<BOSS_SHOTNUM;++i){ if(boss.GetShotPosition(i,&bx,&by,&type)){ switch(type){ case 0: if(CircleCollision(GRAZE_COLLISION,ESHOT0_COLLISION,px,bx,py,by)){ gflag=true; } if(CircleCollision(PLAYER_COLLISION,ESHOT0_COLLISION,px,bx,py,by)){ hflag=true; } break; case 1: if(CircleCollision(GRAZE_COLLISION,ESHOT1_COLLISION,px,bx,py,by)){ gflag=true; } if(CircleCollision(PLAYER_COLLISION,ESHOT1_COLLISION,px,bx,py,by)){ hflag=true; } break; case 2: if(CircleCollision(GRAZE_COLLISION,ESHOT2_COLLISION,px,bx,py,by)){ gflag=true; } if(CircleCollision(PLAYER_COLLISION,ESHOT2_COLLISION,px,bx,py,by)){ hflag=true; } break; } //グレイズフラグが立ってたら if(gflag){ //該当の弾が既にグレイズしているかチェック if(!boss.GetGrazeFlag(i)){ boss.SetGrazeFlag(i); //まだ使われてないグレイズエフェクトを探す for(int z=0;z<GRAZE_NUM;++z){ if(!graze[z]->GetFlag()){ graze[z]->SetFlag(px,py); break; } } //スコアを加算 score->SetScore(GRAZE_SCORE,1); score->SetScore(CURRENT_SCORE,20); graze_flag=true; } //次の弾のグレイズをチェックするためフラグを戻す。 gflag=false; } if(hflag){ //操作キャラのdamageflagを立てる player->SetDamageFlag(); //弾を消す boss.SetShotFlag(i,false); //プレイヤー消滅音フラグを立てる pdead_flag=true; //一時フラグを元に戻す。 hflag=false; //一つでも当たっていたらプレイヤーは消滅するので、 //他の弾をチェックする必要ないのでループを抜ける。 break; } } } } //アイテムとプレイヤーの当たり判定 for(int i=0;i<ITEM_NUM;++i){ if(item[i]->GetFlag()){ item[i]->GetPosition(&ix,&iy); if(CircleCollision(PLAYER_COLLISION,ITEM_COLLISION,px,ix,py,iy)){ switch(item[i]->GetType()){ case 0: score->SetScore(CURRENT_SCORE,300); break; case 1: player->SetPower(1); score->SetScore(POWER_SCORE,player->GetPower()); //パワーを増やす break; } item[i]->Delete(); //アイテム取得音をセット item_flag=true; } } } //ライフは毎回取得 score->SetScore(LIFE_SCORE,player->GetLife()); }
先ほどのボスのショットとプレイヤーの当たり判定のコードは無視してください。
まずプレイヤーの弾数分だけループさせて、弾の座標を取得し、
ボスの座標を取得した後、BossCollisionAll関数で当たり判定を行っています。
BOSS_COLLISIONという定数はdefine.hで以下のように定義しています。
#define BOSS_COLLISION 60
60なのでボスの画像の約半分ですね。
弾がボスに当たったら、先ほどのHpSet関数を使ってHPを1減らしています。
さらに、当たった弾のフラグを戻して弾を消し、スコアに得点を加算しています。
さらにその下のif文を見てください。
bhpという変数はHpSet関数の戻り値であるボスのHPが入った変数です。
それが0以下、つまり倒したときにこのif文内の処理が実行されるわけです。
if文内では、SetFlag関数でボスのフラグを戻し、
消滅エフェクト、消滅音、得点の設定などを行っています。
最後にアイテムも出現させていますが、
ボスなので少し多めにアイテムを出現させようと思います。
少しばらけて出現させたほうがよいので、初期座標をすこしずらしています。
(rand()%100-51)の計算をするだけで、-50~50までのランダムな値が取得できます。
これにbx,byつまりボスの座標を足すことで、
ボスの座標から-50~50の範囲でかつランダムにアイテムの初期座標が
設定されることになります。
またアイテムの、SetFlag関数の第三引数はアイテムの種類を表していますが、
0と1の2種類しかないので、rand()%2の計算だけで、
0か1のランダムな値を設定することができます。
さらにその下のif文でitemnumという変数、
これはアイテムを何個出現させたかを表す変数ですが、
これが10個に達したらループを抜けるようにしています。
これで、ボスの座標から-50~50の範囲で、かつランダムに、
10個のアイテムを出現させることができるようになりました。
当たり判定の部分の説明は以上です。
次にプレイヤーの追跡弾部分のコードです。
void PLAYER::Shot() { s_shot=false; int num=0; if(!damageflag){ //キーが押されててかつ、6ループに一回発射 if(key[KEY_INPUT_Z]==1 && count%6==0){ for(int i=0;i<PSHOT_NUM;++i){ if(shot[i].flag==false){ if(power<5){ shot[i].flag=true; shot[i].x=x; shot[i].y=y; shot[i].rad=-PI/2; shot[i].type=0; break; }else if(power>=5 && power<10){ //0の時が前方発射 if(num==0){ shot[i].flag=true; shot[i].x=x; shot[i].y=y; shot[i].rad=-1.57; shot[i].type=0; }else if(num==1){ shot[i].flag=true; shot[i].x=x; shot[i].y=y; shot[i].rad=-1.69; shot[i].type=0; }else if(num==2){ shot[i].flag=true; shot[i].x=x; shot[i].y=y; shot[i].rad=-1.45; shot[i].type=0; } ++num; if(num==3){ break; } }else if(power==10){ //0の時が前方発射 if(num==0){ shot[i].flag=true; shot[i].x=x; shot[i].y=y; shot[i].rad=-1.57; shot[i].type=0; }else if(num==1){ shot[i].flag=true; shot[i].x=x; shot[i].y=y; shot[i].rad=-1.69; shot[i].type=0; }else if(num==2){ shot[i].flag=true; shot[i].x=x; shot[i].y=y; shot[i].rad=-1.45; shot[i].type=0; }else if(num>2){ BallShotSet(i); } ++num; if(num==5){ break; } /* BallShotSet(i); ++num; if(num==2) break;*/ } } } //ショットサウンドフラグを立てる s_shot=true; } } //一番近い敵との角度 double trad; //一番近い敵の添え字 int index; double ex,ey; //controlクラスの参照変数 CONTROL &control = CONTROL::GetInstance(); //一番近い敵の添え字取得 index=NearEnemySearch(); //弾を移動させる処理 for(int i=0;i<PSHOT_NUM;++i){ //発射してる弾だけ if(shot[i].flag){ if(shot[i].type==0){ shot[i].x+=cos(shot[i].rad)*PSHOT_SPEED; shot[i].y+=sin(shot[i].rad)*PSHOT_SPEED; }else if(shot[i].type==1){ //戻り値が-1なら敵はもう居ないので、まっすぐ前に発射 if(index==-1){ trad=-PI/2; }else{ //一番近い敵との角度を取得 control.GetEnemyPosition(index,&ex,&ey); trad=atan2(ey-shot[i].y,ex-shot[i].x); } //ボスがいる場合はボスとの角度を取得 if(control.GetBossFlag()){ control.GetBossPosition(&ex,&ey); trad=atan2(ey-shot[i].y,ex-shot[i].x); } shot[i].rad=trad; shot[i].x+=cos(trad)*PSHOT_SPEED; shot[i].y+=sin(trad)*PSHOT_SPEED; } //画面の外にはみ出したらフラグを戻す if(shot[i].y<-10 || shot[i].x<-10 || shot[i].x>410 || shot[i].y>510){ shot[i].flag=false; } } } }
ちょっと長いですが、
追加した部分は、「ボスがいる場合はボスとの角度を取得」と書かれている、
3行のif文だけです。
controlクラスのGetBossFlagを実行して、
ボスのフラグを取得し、ボスフラグが立っていれば、
ボスが出現していることになるので、
その場合は、先ほど作成したGetBossPosition関数を使って、
ボスの座標を取得しています。
そしてatan2関数を使って、ボスの座標とプレイヤーのショットがなす角度を
求め、trad変数に代入してます。
trad変数はその下を見れば分かるように、
弾構造体の実際の角度radに代入しているので、
これが実際の弾の角度になるわけです。
ボスが出現していない場合は、ここのif文は実行されないので、
通常の敵とのなす角度が代入されることになります。
以上のコードを書いて、当たり判定を追加すると、
以下の動画のようにプレイヤーのショットとボスとの当たり判定を実装できます。
正直、音とかエフェクトが弱いせいでかなりしょぼく見えますが、
その辺はご了承下さい。
ボス撃破時に画面を揺らしたり、もっと音を変えたり、エフェクトを激しくしたりすれば、
もっとかっこよくなると思います。
今回の説明は以上です。
次回の内容は未定ですのでしばらくお待ち下さい。
>> 【ボスの状態遷移】に進む
>> シューティングゲーム作成入門トップに戻る