39 章

今回はアイテムを出現させてみましょう。

アイテム関連を実装するときも、またいつものように、

構造体を用意し、変数を宣言したら、「登録・計算・描画」の三本柱を作ります。☆1

しかしアイテム関連の実装にはこれに加えて自機との当たり判定の計算と

アイテムと接触した時のステータス変化の処理が必要になってきます。

ですので、まずはこの章では「☆1」の部分のみ作ってみましょう。

では構造体を用意します。

アイテムはボーダーラインより上に上がった時に吸収されることがありますね。

これはアイテムの状態が変わったことを意味します。

ですから、今回は一般的な構造体の他に「state」変数も追加してみましょう。

初期化と画像のロードも書いておきます。


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

//アイテムの表示最大数
#define ITEM_MAX 100
//アイテムゲットボーダーライン
#define ITEM_GET_BORDER_LINE 100
//アイテムの吸収範囲
#define ITEM_INDRAW_RANGE 70


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

//アイテムの構造体
typedef struct{
        //フラグ、カウンタ、種類、状態
        int flag,cnt,knd,state;
        //速度、座標、大きさ
        double v,x,y,r;
}item_t;


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

GLOBAL int img_item[6][2];//アイテムの画像

GLOBAL item_t item[ITEM_MAX];//アイテム


---- ini.cpp の ini() に以下を追加 ----

memset(item,0,sizeof(item_t)*ITEM_MAX);


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

        LoadDivGraph( "../dat/img/item/p0.png" , 2 , 2 , 1 , 35 , 35 , img_item[0] ) ;
        LoadDivGraph( "../dat/img/item/p1.png" , 2 , 2 , 1 , 35 , 35 , img_item[1] ) ;
        LoadDivGraph( "../dat/img/item/p2.png" , 2 , 2 , 1 , 15 , 15 , img_item[2] ) ;
        LoadDivGraph( "../dat/img/item/p3.png" , 2 , 2 , 1 , 35 , 35 , img_item[3] ) ;
        LoadDivGraph( "../dat/img/item/p4.png" , 2 , 2 , 1 , 35 , 35 , img_item[4] ) ;
        LoadDivGraph( "../dat/img/item/p5.png" , 2 , 2 , 1 , 35 , 35 , img_item[5] ) ;


---- load.cpp の以下(赤字部)を変更 ----

void load_story(){
        int n,num,i,fp;
        char fname[32]={"../dat/csv/39章/storyH0.csv"};
        int input[64];
        char inputc[64];


はい、下準備出来ましたね。

ではまず登録してみましょう。

アイテムが出現するのは敵が倒された時か自機が死んだ時ですよね。

では敵が倒された時にアイテムを出現させるenter_item関数を呼びます。

何のアイテムを出現させるかという情報は敵が既にitem_nの中に持っています。

エクセルデータで決めているのでそちらを確認して下さい。

1つ目のアイテムは敵の位置に、1度に複数出現させる時は、2つ目から適当にちょっと離れた位置に出現させます。


---- out.cpp の赤字部を変更 ----

int serch_item(){
        for(int i=0;i<ITEM_MAX;i++)
                if(item[i].flag==0)
                        return i;
        return -1;
}

//アイテム登録
//アイテム 0:小パワー 1:小点 2:弾点 3:小金 4:大パワー 5:大金
void enter_item(double x, double y, int item_n[], int num){//x,y,アイテムの種類,個数
        int k;
        double r[6]={0.6,0.6,1.0,0.6,1.0,1.0};//dat/img/itemの画像の拡大率
        for(int i=0;i<num;i++){//1つの敵から出るアイテムの最大数は6個
                if(item_n[i]!=-1){//エクセルで指定したアイテムが-1(なし)なら終り
                        if((k=serch_item())!=-1){//登録出来る番号をさがす
                                item[k].flag=1;
                                item[k].v    =-3.5;     //速さ
                                item[k].cnt  =0;
                                item[k].state=0;
                                item[k].x    =x;
                                item[k].y    =y;
                                if(i>0){//複数なら適当にちらばらせる
                                        item[k].x+=rang(40);
                                        item[k].y+=rang(40);
                                }
                                item[k].knd =item_n[i];//指定したアイテムを出現させる
                                item[k].r   =r[item[k].knd];
                        }
                }
        }
}

//敵から出るアイテム
void enter_enemy_item(int s){
        enter_item(enemy[s].x,enemy[s].y,enemy[s].item_n, sizeof(enemy[s].item_n)/sizeof(int));
}

//キャラから出るアイテム
void enter_char_item(){
        int item_n[4]={4,4,4,4};
        enter_item(ch.x, ch.y, item_n, 4);
}

//敵が死ぬかどうかの決定
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);
        enter_enemy_item(s);//敵sのアイテムを出現させる(39章)
        for(i=0;i<SHOT_MAX;i++){//敵総数分


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

extern void enter_char_item();//(39章)

void calc_ch(){
    if(ch.flag==1){//喰らいボム受付中
        bright_set.brt=80;//暗く
        if(ch.cnt>20){//0.33秒受け付ける
            ch.flag   =2;    //1:喰らいボム受付中 2:死んで浮き上がり中
            ch.cnt    =0;
            bright_set.brt=255;
        }
    }
    if(ch.cnt==0 && ch.flag==2){//今の瞬間死んだら
        enter_char_item();//キャラのアイテム登録(39章)
        ch.x=FMX/2;//座標セット
        ch.y=FMY+30;
        ch.mutekicnt++;//無敵状態へ
    }


次は計算と描画です。

アイテムはステータスが0だと普通に降ってきます。

1だと吸収されます。

ITEM_GET_BORDER_LINEより上にいたら吸収されますから1にします。

低速移動中に半径ITEM_INDRAW_RANGE以内にいたら状態は0のまま少し吸収します。

普通に落ちる時はy方向に速度分座標を+すればいいだけですね。

吸収するときは、自機とアイテムとの角度をatan2で求めていつものようにサインコサインで計算します。

描画は「下地1,下地2,文字」の順番で3回描画します。

下地1は等倍で回転描画、下地2は0.8倍で反対の回転描画をしています。


---- calc.cpp に以下赤字を変更・追加 ----


//アイテムの吸収処理。
void calc_item_indraw(int i){
        double v = item[i].state ? 8 : 3;//state1ならスピード8、違うなら2
        double angle=atan2(ch.y-item[i].y,ch.x-item[i].x);//自機への角度を計算して
        item[i].x += cos(angle)*v;
        item[i].y += sin(angle)*v;
}

//アイテム移動など計算
void calc_item(){
        for(int i=0;i<ITEM_MAX;i++){
                if(item[i].flag>0){
                        if(item[i].state==0)
                                if(ch.y<ITEM_GET_BORDER_LINE)//吸収状態なら
                                        item[i].state=1;
                        if(item[i].state==0){//普通の状態なら
                                double x=ch.x-item[i].x,y=ch.y-item[i].y;
                                //低速状態で自機付近なら
                                if(CheckStatePad(configpad.slow)>0 &&
                                                x*x+y*y<ITEM_INDRAW_RANGE*ITEM_INDRAW_RANGE){
                                        calc_item_indraw(i);//ちょっと吸収
                                }
                                else{
                                        if(item[i].v<2.5)//速度アップ
                                                item[i].v+=0.06;
                                        item[i].y+=item[i].v;//移動
                                }
                        }
                        else{//吸収状態なら
                                calc_item_indraw(i);
                        }
                        item[i].cnt++;
                        if(item[i].y>FMY+50)
                                item[i].flag=0;
                }
        }
}

void calc_main(){
        calc_stage_title();
        calc_item();
}


---- graph.cpp に以下赤字を変更・追加 ----

void graph_item(){
        int i;
        for(i=0;i<ITEM_MAX;i++){
                if(item[i].flag==1){
                        DrawRotaGraphF(item[i].x+FX+dn.x,item[i].y+FY+dn.y,
                            item[i].r,PI2*(count%120)/120,img_item[item[i].knd][1],TRUE);
                        DrawRotaGraphF(item[i].x+FX+dn.x,item[i].y+FY+dn.y,
                            item[i].r*0.8,-PI2*(count%120)/120,img_item[item[i].knd][1],TRUE);
                        DrawRotaGraphF(item[i].x+FX+dn.x,item[i].y+FY+dn.y,
                            item[i].r,0,img_item[item[i].knd][0],TRUE);
                }
        }
}

void graph_main(){

        if(bright_set.brt!=255)SetDrawBright(bright_set.brt,bright_set.brt,bright_set.brt);

        graph_back_main();//背景描画メイン
        graph_effect(0);//敵が死ぬエフェクト

        if(bright_set.brt!=255)SetDrawBright(255,255,255);

        graph_effect(4);//喰らいボムのエフェクト

        if(bright_set.brt!=255)SetDrawBright(bright_set.brt,bright_set.brt,bright_set.brt);

        graph_child();
        graph_item();//アイテム描画
        graph_boss();
        graph_enemy();//敵の描画
        graph_cshot();//自機ショットの描画

実行結果

今はまだアイテムの当たり判定を作っていないため、アイテムに接触しても何もおこりませんし、

吸収したアイテムは自機にまとわり付いているだけでしょう。

次の章でそれを実装しましょう。

- Remical Soft -