さて、段々それっぽくなってきましたね。

自機ショットと当たり判定を付ければ大分骨格が完成します。

自機ショットも基本的に敵がショットを撃つのと考え方は変わりません。

弾情報を格納する入れ物をつくって、そこに種類、スピード、角度などの情報を持たせるだけでいいのです。

数値さえ設定しておけば計算部が勝手に軌道や当たり判定を計算してくれます。

では、いつものように入れ物を用意します。


---- struct.h に以下(赤字部)を追加 ----

//キャラクターショットに関する構造体
typedef struct{
        int flag,power,cnt,knd;//フラグ、パワー、カウンタ、種類
        double x,y,angle,spd;//座標、角度、スピード
}cshot_t;

//キャラクターに関する構造体
typedef struct{
        int flag;               //フラグ
        int cnt;                //カウンタ
        int power;              //パワー
        int point;              //ポイント
        int score;              //スコア
        int num;                //残機数
        int mutekicnt;  //無敵状態とカウント
        int shot_mode;  //ショットモード
        int money;              //お金
        int img;
        int slow;               //スローかどうか
        double x,y;             //座標
        int shot_cnt;           //ショットのカウンタ
}ch_t;


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

//自機ショットの登録最大数
#define CSHOT_MAX 200


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

GLOBAL int img_cshot[2];        //自機ショット用画像
GLOBAL cshot_t cshot[CSHOT_MAX];//自機ショット登録用変数


---- load.cpp の load()関数に以下を追加 ----

img_cshot[0]=LoadGraph("../dat/img/char/bl_00.png");
img_cshot[1]=LoadGraph("../dat/img/char/bl_01.png");

sound_se[2]=LoadSoundMem("../dat/se/cshot.wav");


---- ini.cpp の ini関数に以下を追加 ----

memset(cshot,0,sizeof(cshot_t)*CSHOT_MAX);
ch.power=500;


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

GLOBAL void cshot_main();


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

void graph_cshot(){
        for(int i=0;i<CSHOT_MAX;i++){
                if(cshot[i].flag>0){
                        DrawRotaGraphF(cshot[i].x+FIELD_X,cshot[i].y+FIELD_Y,1,0,
                                       img_cshot[cshot[i].knd],TRUE);
                }
        }
}


---- graph.cpp の graph_main関数を変更 ----

void graph_main(){
        graph_enemy();
        graph_cshot();
        graph_ch();
        graph_bullet();
        graph_board();
}


---- main.cpp のメイン関数内のswitch分の以下の部分を変更 ----

            case 100://通常処理
                calc_ch();    //キャラクタ計算
                ch_move();    //キャラクタの移動制御
                cshot_main();//自機ショットメイン
                enemy_main();//敵処理メイン
                shot_main();//ショットメイン
                graph_main();//描画メイン
                stage_count++;
                break;


最後に肝心な自機ショット登録部についてです。

1回に、自機パワーが200以下なら2発、それ以上なら4発撃たせてみます。

その4発の位置を以下のようにしてみます。



int cshot0pos_x[4]={-10, 10,-30, 30};
int cshot0pos_y[4]={-30,-30,-10,-10};


1発目は(-10,-30)の位置、2発目は(10,-30)の位置、3発目は(-30,-10)。。。ということです。

今回も前回までと同様、登録格納庫を用意しているので、ショットを登録する時は

int search_cshot()

で空いている番号を探して登録します。


---- cshot.cpp を変更 ----


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

int cshot0num[2]  ={2,4};
int cshot0pos_x[4]={-10, 10,-30, 30};
int cshot0pos_y[4]={-30,-30,-10,-10};

//自機ショットの登録可能番号を返す
int search_cshot(){
        for(int i=0;i<CSHOT_MAX;i++){
                if(cshot[i].flag==0)
                        return i;
        }
        return -1;
}

//通常ショット登録
void ch0_shot_pattern(){
        int k;
        for(int i=0;i<cshot0num[ch.power<200?0:1];i++){
                if((k=search_cshot())!=-1){
                        cshot[k].flag=1;
                        cshot[k].cnt=0;
                        cshot[k].angle=-PI/2;
                        cshot[k].spd=20;
                        cshot[k].x=ch.x+cshot0pos_x[i];
                        cshot[k].y=ch.y+cshot0pos_y[i];
                        cshot[k].power=23;
                        cshot[k].knd=0;
                }
        }
        se_flag[2]=1;//発射音オン
}

//低速通常ショット登録
void ch1_shot_pattern(){
        int k;
        for(int i=0;i<cshot0num[ch.power<200?0:1];i++){
                if((k=search_cshot())!=-1){
                        cshot[k].flag=1;
                        cshot[k].cnt=0;
                        cshot[k].angle=-PI/2;
                        cshot[k].spd=20;
                        cshot[k].x=ch.x+cshot0pos_x[i]/3;//低速中なら位置を中心側へ
                        cshot[k].y=ch.y+cshot0pos_y[i]/2;
                        cshot[k].power=23;
                        cshot[k].knd=0;
                }
        }
        se_flag[2]=1;
}

//ショット登録部
void enter_shot(){
        //ショットボタンが押されていたら
        if(CheckStatePad(configpad.shot)>0){
                ch.shot_cnt++;
                if(ch.shot_cnt%3==0){//3カウントに1回
                        if(CheckStatePad(configpad.slow)>0)//低速移動中なら
                                ch1_shot_pattern();
                        else
                                ch0_shot_pattern();
                }
        }
        else
                ch.shot_cnt=0;
}

//ショットの移動計算
void calc_cshot(){
        for(int i=0;i<CSHOT_MAX;i++){
                if(cshot[i].flag==1){
                        int dranx=cshot[i].spd+11/2,drany=cshot[i].spd+55/2;
                        cshot[i].x+=cos(cshot[i].angle)*cshot[i].spd;
                        cshot[i].y+=sin(cshot[i].angle)*cshot[i].spd;
                        cshot[i].cnt++;
                        if(cshot[i].x<-dranx || cshot[i].x>FIELD_MAX_X+dranx ||
                                cshot[i].y<-drany || cshot[i].y>FIELD_MAX_Y+drany)//画面から外れたら
                                cshot[i].flag=0;
                }
        }
}

//キャラクタショットに関する関数
void cshot_main(){
        calc_cshot();//ショットの軌道計算
        enter_shot();//ショット登録
}


calc_cshot()関数でショットが画面から外れた時の判定で、何故

int dranx=cshot[i].spd+11/2,drany=cshot[i].spd+55/2;

として判定しているのか補足します。次の章で当たり判定について説明するので、

先にそちらの理解が必要ですが、次の章ではもうこのコードは出てこないので、今説明しておきます。

ショットは単純にピタゴラスの定理で接触しているかしていないかだけでは当たりを判定出来ません。

例えばショットの速さが200とかで、弾の当たり判定が10とかだと、190はあたらないエリアが出来てしまいます。

つまり弾の軌跡を計算して接触を判定する必要があります。

当たり判定はその軌跡をつないで計算するわけですが、

弾が画面から外に外れた時、弾を消してしまっては、最後の軌跡が計算出来ません。

つまり、前フレームで、画面外であり、今のフレームでも画面外の時に消す必要があります。

また、11,55というのはショットの画像サイズです。

画面よりスピード+画像サイズ/2分外にあれば、前回も画面外だったことがわかりますから、

この時に消してやる必要があるのです。

ここの処理ではそれを実現する為にこのようにかかれているのです。


実行結果


- Remical Soft -