敵に当たり判定を付けたので、今度は自機に当たり判定を付けてみましょう。

まず、具体的な処理に入る前に、前処理をしておきます。

弾に当たり判定処理をさせるなら、弾に当たり判定範囲などの情報を持たせなければ成りません。

そこで以下のようにしてみます。



---- struct.h に以下を追加 ----

//弾の情報
typedef struct{
        int size_x,size_y,col_num;
        double range;
}bullet_info_t;


---- GV.h に以下を追加 ----

GLOBAL bullet_info_t bullet_info[10];//弾情報


---- load.cpp load関数内に以下を追加 ----

sound_se[3]=LoadSoundMem("../dat/se/char_death.wav");//自機死に音
ChangeVolumeSoundMem( 80, sound_se[3] ) ;


---- ini.cpp に以下を追加 ----

//渡された情報を構造体に入れる関数
void input_bullet_info(bullet_info_t *binfo,int size_x,int size_y,int col_num,double range){
        binfo->size_x =size_x;  binfo->size_y =size_y;
        binfo->col_num=col_num; binfo->range  =range;
}


---- ini.cpp first_ini()関数内以下追加 ----

        //例:弾0は76x76ピクセルで、5色あり、当たり範囲17ピクセル
        input_bullet_info(&bullet_info[0],76, 76, 5,17.0);
        input_bullet_info(&bullet_info[1],22, 22, 6, 4.0);
        input_bullet_info(&bullet_info[2], 5,120,10, 2.5);
        input_bullet_info(&bullet_info[3],19, 34, 5, 2.0);
        input_bullet_info(&bullet_info[4],38, 38,10, 2.0);
        input_bullet_info(&bullet_info[5],14, 16, 3, 3.5);
        input_bullet_info(&bullet_info[6],14, 18, 3, 2.0);
        input_bullet_info(&bullet_info[7],16, 16, 9, 2.5);
        input_bullet_info(&bullet_info[8],12, 18,10, 1.5);
        input_bullet_info(&bullet_info[9],13, 19, 3, 2.0);


first_ini関数に弾情報を格納していますが、この当たり判定範囲をちょうどいい大きさに決めるのに時間がかかりました・・。

まだこの範囲では納得いかん!という人は試行錯誤で調整して下さい。

この関数で弾の大きさ、色の種類数、当たり判定範囲を持たせます。

さて、準備が出来たので、本処理にはいっていきましょう。

と言っても、前回当たり判定のモジュールを作ったので今回も同様に判定すればいいから簡単ですね。

ほとんどコピペでOKです。

ほとんど同じ関数を複数個書くのは無駄な気がしますが、ここを汎用的にすると少し確認しづらくなるので、

関数の汎用化は後々することにし、今はコピペして少し変更してみましょう。

基本敵ショットと自機との当たり判定処理と同じです。


---- out.cpp を変更 ----

#include "../include/GV.h"

#define ENEMY_RANGE_MAX 4
#define CSHOT_RANGE_MAX 2
#define CRANGE 2.0

//敵の当たり判定範囲
int enemy_range[ENEMY_RANGE_MAX]={16,30,16,50};
//自機ショットの当たり判定範囲
int cshot_range[CSHOT_RANGE_MAX]={6,};

//敵と自機ショットが当たったかどうかを判定する
int out_judge_cshot(int i,int s){
    int j;
    if(cshot[i].cnt>0){//ショットの軌道が1度でも計算されていたら
        double x=cshot[i].x-enemy[s].x;//敵と自機ショットとの距離
        double y=cshot[i].y-enemy[s].y;
        //オーバーフロー対策
        if(cshot[i].knd>=CSHOT_RANGE_MAX || enemy[s].knd>=ENEMY_RANGE_MAX)
            printfDx("out_judge_cshot内オーバーフロー");
        //敵の当たり判定と自機ショットの当たり判定の合計範囲
        double r=cshot_range[cshot[i].knd]+enemy_range[enemy[s].knd];
        //中間を計算する必要があれば
        if(cshot[i].spd>r){
            //1フレーム前にいた位置を格納する
            double pre_x=cshot[i].x+cos(cshot[i].angle+PI)*cshot[i].spd;
            double pre_y=cshot[i].y+sin(cshot[i].angle+PI)*cshot[i].spd;
            double px,py;
            for(j=0;j<cshot[i].spd/r;j++){//進んだ分÷当たり判定分ループ
                px=pre_x-enemy[s].x;
                py=pre_y-enemy[s].y;
                if(px*px+py*py<r*r)
                    return 1;
                pre_x+=cos(cshot[i].angle)*r;
                pre_y+=sin(cshot[i].angle)*r;
            }
        }
        if(x*x+y*y<r*r)//当たり判定内なら
            return 1;//当たり
    }
    return 0;
}

//自機と敵ショットが当たったかどうかを判定する
int out_judge_enemyshot(int s,int n){
    int j;
    if(shot[s].bullet[n].cnt>0){//ショットの軌道が1度でも計算されていたら
        double x=shot[s].bullet[n].x-ch.x;//敵と自機ショットとの距離
        double y=shot[s].bullet[n].y-ch.y;
        //オーバーフロー対策
        if(shot[s].bullet[n].knd>=10)
            printfDx("out_judge_enemyshot内オーバーフロー\n");
        //敵ショットと自機の当たり判定の合計範囲
        double r=bullet_info[shot[s].bullet[n].knd].range+CRANGE;
        //中間を計算する必要があれば
        if(shot[s].bullet[n].spd>r){
            //1フレーム前にいた位置を格納する
            double pre_x=shot[s].bullet[n].x+cos(shot[s].bullet[n].angle+PI)*shot[s].bullet[n].spd;
            double pre_y=shot[s].bullet[n].y+sin(shot[s].bullet[n].angle+PI)*shot[s].bullet[n].spd;
            double px,py;
            for(j=0;j<shot[s].bullet[n].spd/r;j++){//進んだ分÷当たり判定分ループ
                px=pre_x-ch.x;
                py=pre_y-ch.y;
                if(px*px+py*py<r*r)
                    return 1;
                pre_x+=cos(shot[s].bullet[n].angle)*r;
                pre_y+=sin(shot[s].bullet[n].angle)*r;
            }
        }
        if(x*x+y*y<r*r)//当たり判定内なら
            return 1;//当たり
    }
    return 0;
}

extern void enter_del_effect(int);

//敵が死ぬかどうかの決定
void enemy_death_judge(int s){
    int i;
    se_flag[8]=1;//敵に当たった音
    if(enemy[s].hp<0){//敵のHPが0未満になったら
        enemy[s].flag=0;//敵を消滅させる
        se_flag[1]=1;//敵のピチュり音
        enter_del_effect(s);
        for(i=0;i<SHOT_MAX;i++){//敵総数分
            if(shot[i].flag!=0){//登録されている弾幕データがあれば
                if(s==shot[i].num){//その敵が登録した弾幕があれば
                    shot[i].flag=2;//それ以上弾幕を続けないフラグを立てる
                    break;
                }
            }
        }
    }
}

//自機ショットと敵との処理
void cshot_and_enemy(){
    int i,s;
    for(i=0;i<CSHOT_MAX;i++){//自機ショット総数
        if(cshot[i].flag>0){
            for(s=0;s<ENEMY_MAX;s++){//敵総数
                if(enemy[s].flag>0){
                    if(out_judge_cshot(i,s)){//自機ショットと敵が当たっていれば
                        cshot[i].flag=0;//その自機ショットを消す
                        enemy[s].hp-=cshot[i].power;//弾の持つパワー分HPを減らす
                        enemy_death_judge(s);//敵が死ぬかどうかを決める
                    }
                }
            }
        }
    }
}

//敵ショットと自機との処理
void enemyshot_and_ch(){
    int s,n;
    for(s=0;s<SHOT_MAX;s++){//敵ショット総数
        if(shot[s].flag>0){//そのショットが登録されていたら
            for(n=0;n<SHOT_BULLET_MAX;n++){//弾総数
                if(shot[s].bullet[n].flag==1){//弾が登録されていたら
                    if(out_judge_enemyshot(s,n)){//自機にその弾が接触していたら
                        shot[s].bullet[n].flag=0;//弾をオフ

                        /*喰らいボム処理をここに追加*/

                        if(ch.flag==0 && ch.mutekicnt==0){//ステータスが通常で、無敵じゃなかったら
                            ch.flag    =2;    //1:喰らいボム受付中 2:死んで浮き上がり中
                            ch.cnt    =0;
                            se_flag[3]=1;//ピチュり音
                            return;
                        }
                    }
                }
            }
        }
    }
}

//当たり判定メイン
void out_main(){
    cshot_and_enemy();
    enemyshot_and_ch();
}




enemyshot_and_chは敵のショットと自機との当たり判定処理の関数ですが、

ここで、あたったらch.flag=2としていますね?

被弾したらキャラは2段階のステータスを踏みます。

まず、被弾した瞬間は「喰らいボム受付中」になります。この状態をch.flag=1の状態とします。

時間内にボムボタンが押されなかったら次に画面したから浮き上がってくる状態になります。

この状態をch.flag=2の状態とします。

浮き上がってきて画面内にいるときに、キー入力が行われたら、そこで元のステータスに戻します。

そしてキャラが死んだ瞬間からキャラを無敵状態にします。

無敵状態であるかどうかのフラグはまた別にあります。

ch.mutekicntが0では無い時が無敵です。 この時は被弾処理をしないようにします。

また、graph関数内でキャラを描画するとき、2回に1回描画してやります。

するとうすくチラチラして無敵である感じになります。

無敵状態は適度な時間経過後戻してやります。

それらの処理の変更・追加を行いましょう。


---- char.cpp の以下の関数を変更 ----


void calc_ch(){
        if(ch.cnt==0 && ch.flag==2){//今の瞬間死んだら
                ch.x=FIELD_MAX_X/2;//座標セット
                ch.y=FIELD_MAX_Y+30;
                ch.mutekicnt++;//無敵状態へ
        }
        if(ch.flag==2){//死んで浮上中なら
                unsigned int push=CheckStatePad(configpad.left)+CheckStatePad(configpad.right)
                        +CheckStatePad(configpad.up)+CheckStatePad(configpad.down);
                ch.y-=1.5;//キャラを上に上げる
                //1秒以上か、キャラがある程度上にいて、何かおされたら
                if(ch.cnt>60 || (ch.y<FIELD_MAX_Y-20 && push)){
                        ch.cnt=0;
                        ch.flag=0;//キャラステータスを元に戻す
                }
        }
        if(ch.mutekicnt>0){//無敵カウントが0じゃなければ
                ch.mutekicnt++;
                if(ch.mutekicnt>120)//2秒以上たったら
                        ch.mutekicnt=0;//戻す
        }

        ch.cnt++;//キャラクタカウントアップ
        ch.img=(ch.cnt%24)/6;//現在の画像決定
}


---- graph.cpp の以下の関数を変更 ----

//自機描画
void graph_ch(){
        if(ch.mutekicnt%2==0)
                DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,img_ch[0][ch.img],TRUE);
}




実行結果

注:再生FPSの関係で、上の動画ではうまく
キャラの点滅が伝えられていません。この動画と実行結果は異なります。
実際に自分でコンパイルして実行結果を確かめて下さい。


- Remical Soft -