>> シューティングゲーム作成入門トップに戻る
今回はプレイヤーとボスの当たり判定を実装します。
当たり判定を行うにはボスの弾の座標を取得する必要があるので、
ボスクラスにそれ専用の関数を作ります。
GetShotPosition関数は指定したindexの弾の座標をポインタ経由で返す関数です。
通常の敵のEnemy関数とあまり変わりませんが、
弾の構造体である、E_SHOT構造体のメンバにtypeという弾の種類を表す変数を追加してます。
後で弾の種類によって当たり判定の半径を変える必要があるからです。
関数自体は単純で、該当の弾のフラグが立っていれば、
ポインタに座標と弾の種類を代入し、trueを返すだけです。
フラグが立っていなければfalseを返してます。
その下の二つの関数は、グレイズ用の関数です。
グレイズが発生した弾は何回もグレイズが発生してはおかしいので、
一度グレイズが発生したらその弾はもうグレイズを発生させないようにする必要があります。
そのためのフラグ設定用の関数です。
後はCONTROLクラス内での当たり判定部分のコードを書くだけです。
今まで当たり判定は、CollisionAll関数に全てまとめてましたが、
ボスを追加したので、
通常の敵の当たり判定はEnemyCollisionAll関数へ移動し、
ボスの当たり判定はBossCollisionAll関数という名前で新たに作るようにします。
仕組み的にはプレイヤーと敵のショットの当たり判定とほぼ一緒です。
まず最初のif文で、プレイヤーが生きてるかチェックし、
プレイヤーの座標を取得した後、ボスの弾数分だけループさせてます。
次に先ほどのGetShotPosition関数で、
ボスの弾の座標と弾の種類を取得した後、switch分に入ってます。
typeによって処理を分け、当たり判定用のCircleCollision関数を
使ってグレイズと実際に弾がプレイヤーに当たったかどうかの当たり判定を
実行しています。
グレイズがあればgflagを弾が当たってればhflagを立ててます。
さらにその下の処理で、gflagが立っていれば、
グレイズ時の処理を行ってます。
hflagが立っていれば、プレイヤーに弾がヒットしたときの処理を行ってます。
この部分の処理は関数名が違うだけで、通常の敵との当たり判定と
まったく一緒なので説明は省略します。
その下にアイテム用の当たり判定もありますが、
これも敵の当たり判定の時に説明したものとまったく一緒なので、
説明は省略します。
ライフは毎回変動する可能性があるので、
最後に一応取得してスコアにセットしています。
最後にこのBossCollisionAll関数を呼び出すAll関数は
現在以下のようになってます。
if(!boss.GetFlag())のelse文の処理を見てください。
この部分でBossCollisionAll関数を呼び出しています。
ボスが出現しているときしか実行する必要ないですしね。
これでボスのショットとプレイヤーの当たり判定を実装することが出来ました。
以下の動画のようになります。
ちゃんとグレイズとボスのショットがヒットするようになりましたね?
次回はプレイヤーのショットとボスとの当たり判定を実装します。
>> 【ボスとプレイヤーのショットとの当たり判定】に進む
>> シューティングゲーム作成入門トップに戻る
プレイヤーとボスの弾との当たり判定
当たり判定を行うにはボスの弾の座標を取得する必要があるので、
ボスクラスにそれ専用の関数を作ります。
bool BOSS::GetShotPosition(int index,double *x,double *y,int *type) { if(shot[index].flag){ *x=shot[index].x; *y=shot[index].y; *type=shot[index].type; return true; }else{ return false; } } bool BOSS::GetGrazeFlag(int index) { return shot[index].gflag; } void BOSS::SetGrazeFlag(int index) { shot[index].gflag=true; }
GetShotPosition関数は指定したindexの弾の座標をポインタ経由で返す関数です。
通常の敵のEnemy関数とあまり変わりませんが、
弾の構造体である、E_SHOT構造体のメンバにtypeという弾の種類を表す変数を追加してます。
後で弾の種類によって当たり判定の半径を変える必要があるからです。
関数自体は単純で、該当の弾のフラグが立っていれば、
ポインタに座標と弾の種類を代入し、trueを返すだけです。
フラグが立っていなければfalseを返してます。
その下の二つの関数は、グレイズ用の関数です。
グレイズが発生した弾は何回もグレイズが発生してはおかしいので、
一度グレイズが発生したらその弾はもうグレイズを発生させないようにする必要があります。
そのためのフラグ設定用の関数です。
後はCONTROLクラス内での当たり判定部分のコードを書くだけです。
今まで当たり判定は、CollisionAll関数に全てまとめてましたが、
ボスを追加したので、
通常の敵の当たり判定はEnemyCollisionAll関数へ移動し、
ボスの当たり判定はBossCollisionAll関数という名前で新たに作るようにします。
void CONTROL::BossCollisionAll() { double px,py,bx,by,ix,iy; //出すアイテム数 int itemnum=0; //グレイズとヒットしたかのフラグ bool hflag=false,gflag=false; //ボスの弾の種類 int type; //ボスのショットとプレイヤーとの当たり判定 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());
仕組み的にはプレイヤーと敵のショットの当たり判定とほぼ一緒です。
まず最初のif文で、プレイヤーが生きてるかチェックし、
プレイヤーの座標を取得した後、ボスの弾数分だけループさせてます。
次に先ほどのGetShotPosition関数で、
ボスの弾の座標と弾の種類を取得した後、switch分に入ってます。
typeによって処理を分け、当たり判定用のCircleCollision関数を
使ってグレイズと実際に弾がプレイヤーに当たったかどうかの当たり判定を
実行しています。
グレイズがあればgflagを弾が当たってればhflagを立ててます。
さらにその下の処理で、gflagが立っていれば、
グレイズ時の処理を行ってます。
hflagが立っていれば、プレイヤーに弾がヒットしたときの処理を行ってます。
この部分の処理は関数名が違うだけで、通常の敵との当たり判定と
まったく一緒なので説明は省略します。
その下にアイテム用の当たり判定もありますが、
これも敵の当たり判定の時に説明したものとまったく一緒なので、
説明は省略します。
ライフは毎回変動する可能性があるので、
最後に一応取得してスコアにセットしています。
最後にこのBossCollisionAll関数を呼び出すAll関数は
現在以下のようになってます。
void CONTROL::All() { //サウンドフラグを初期化 eshot_flag=pshot_flag=edead_flag=pdead_flag=graze_flag=item_flag=bshot_flag=false; //描画領域を指定 SetDrawArea(MARGIN,MARGIN,MARGIN+380,MARGIN+460); back->All(); player->All(); //プレイヤーショットサウンドフラグチェック if(player->GetShotSound()){ pshot_flag=true; } if(!boss.GetFlag()){ for(int i=0;i<ENEMY_NUM;++i){ if(enemy[i]!=NULL){ //敵ショットサウンドフラグチェック if(enemy[i]->GetShotSound()){ eshot_flag=true; } if(enemy[i]->All()){ delete enemy[i]; enemy[i]=NULL; } } } //敵との当たり判定 EnemyCollisionAll(); }else{ boss.All(); if(boss.GetShotSound()){ bshot_flag=true; } //ボスとの当たり判定 BossCollisionAll(); } //グレイズ描画 for(int i=0;i<GRAZE_NUM;++i){ graze[i]->All(); } //敵消滅エフェクト for(int i=0;i<EFFECT_EDEADNUM;++i){ if(effect_edead[i]->GetFlag()){ effect_edead[i]->All(); } } //アイテム描画 for(int i=0;i<ITEM_NUM;++i){ if(item[i]->GetFlag()){ item[i]->All(); } } //描画領域を指定 SetDrawArea(0,0,640,480); //スコア描画 score->All(); SoundAll(); ++g_count; }
if(!boss.GetFlag())のelse文の処理を見てください。
この部分でBossCollisionAll関数を呼び出しています。
ボスが出現しているときしか実行する必要ないですしね。
これでボスのショットとプレイヤーの当たり判定を実装することが出来ました。
以下の動画のようになります。
ちゃんとグレイズとボスのショットがヒットするようになりましたね?
次回はプレイヤーのショットとボスとの当たり判定を実装します。
>> 【ボスとプレイヤーのショットとの当たり判定】に進む
>> シューティングゲーム作成入門トップに戻る