先ほど1つ敵の移動制御関数を作りましたね。 今度は沢山作ってみましょう。

0〜10の11種類の行動パターンの制御関数を作ってみました。

また、今回、rang関数という関数を作りました。これはnを渡したら-n〜nまでの乱数がdoubleで返って来る関数です。

以下ちょっと長いですが、簡単な関数が多く並んでいるだけです。



--enemy_act_pattern.cppを変更--

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

//渡された-ang〜angまでのランダムな角度を返す
double rang(double ang){
    return ( -ang + ang*2 * GetRand(10000)/10000.0 );
}

//移動パターン0
//下がってきて停滞して上がっていく
void enemy_pattern0(int i){
    int t=enemy[i].cnt;
    if(t==0)
        enemy[i].vy=3;//下がってくる
    if(t==40)
        enemy[i].vy=0;//止まる
    if(t==40+enemy[i].wait)//登録された時間だけ停滞して
        enemy[i].vy=-3;//上がっていく
}

//移動パターン1
//下がってきて停滞して左下に行く
void enemy_pattern1(int i){
    int t=enemy[i].cnt;
    if(t==0)
        enemy[i].vy=3;//下がってくる
    if(t==40)
        enemy[i].vy=0;//止まる
    if(t==40+enemy[i].wait){//登録された時間だけ停滞して
        enemy[i].vx=-1;//左へ
        enemy[i].vy=2;//下がっていく
        enemy[i].muki=0;//左向きセット
    }
}

//移動パターン2
//下がってきて停滞して右下に行く
void enemy_pattern2(int i){
    int t=enemy[i].cnt;
    if(t==0)
        enemy[i].vy=3;//下がってくる
    if(t==40)
        enemy[i].vy=0;//止まる
    if(t==40+enemy[i].wait){//登録された時間だけ停滞して
        enemy[i].vx=1;//右へ
        enemy[i].vy=2;//下がっていく
        enemy[i].muki=2;//右向きセット
    }
}

//行動パターン3
//すばやく降りてきて左へ
void enemy_pattern3(int i){
    int t=enemy[i].cnt;
    if(t==0)
        enemy[i].vy=5;//下がってくる
    if(t==30){//途中で左向きに
        enemy[i].muki=0;
    }
    if(t<100){
        enemy[i].vx-=5/100.0;//左向き加速
        enemy[i].vy-=5/100.0;//減速
    }
}

//行動パターン4
//すばやく降りてきて右へ
void enemy_pattern4(int i){
    int t=enemy[i].cnt;
    if(t==0)
        enemy[i].vy=5;//下がってくる
    if(t==30){//途中で右向きに
        enemy[i].muki=2;
    }
    if(t<100){
        enemy[i].vx+=5/100.0;//右向き加速
        enemy[i].vy-=5/100.0;//減速
    }
}

//行動パターン5
//斜め左下へ
void enemy_pattern5(int i){
    int t=enemy[i].cnt;
    if(t==0){
        enemy[i].vx-=1;
        enemy[i].vy=2;
        enemy[i].muki=0;
    }
}

//行動パターン6
//斜め右下へ
void enemy_pattern6(int i){
    int t=enemy[i].cnt;
    if(t==0){
        enemy[i].vx+=1;
        enemy[i].vy=2;
        enemy[i].muki=2;
    }
}

//移動パターン7
//停滞してそのまま左上に
void enemy_pattern7(int i){
    int t=enemy[i].cnt;
    if(t==enemy[i].wait){//登録された時間だけ停滞して
        enemy[i].vx=-0.7;//左へ
        enemy[i].vy=-0.3;//上がっていく
        enemy[i].muki=0;//左向き
    }
}

//移動パターン8
//停滞してそのまま右上に
void enemy_pattern8(int i){
    int t=enemy[i].cnt;
    if(t==enemy[i].wait){//登録された時間だけ停滞して
        enemy[i].vx=+0.7;//右へ
        enemy[i].vy=-0.3;//上がっていく
        enemy[i].muki=2;//右向き
    }
}

//移動パターン9
//停滞してそのまま上に
void enemy_pattern9(int i){
    int t=enemy[i].cnt;
    if(t==enemy[i].wait)//登録された時間だけ停滞して
        enemy[i].vy=-1;//上がっていく
}


//移動パターン10
//下がってきてウロウロして上がっていく
void enemy_pattern10(int i){
    int t=enemy[i].cnt;
    if(t==0) enemy[i].vy=4;//下がってくる
    if(t==40)enemy[i].vy=0;//止まる
    if(t>=40){
        if(t%60==0){
            int r=cos(enemy[i].ang)< 0 ? 0 : 1;
            enemy[i].sp=6+rang(2);
            enemy[i].ang=rang(PI/4)+PI*r;
            enemy[i].muki=2-2*r;
        }
        enemy[i].sp*=0.95;
    }
    if(t>=40+enemy[i].wait){
        enemy[i].vy-=0.05;
    }
}


とりあえず作ったこれらの関数がどんな動作をするのか見てみましょう。

エクセルデータを以下のように変更して下さい。ダウンロードして差し替えてもらってもOKです。

/カウンタ 移動パターン 敵の種類 x座標 y座標 スピード 発射時間 弾幕種類 弾の色 体力 弾種類 待機時間 アイテム1 2 3 4 5 6
/cnt pattern knd x y sp bltime blknd col hp blknd2 wait item_n
100 0 0 180 -20 0 150 0 0 100 0 120 0 -1 -1 -1 -1 -1
180 1 0 200 -20 0 150 0 0 100 0 120 0 -1 -1 -1 -1 -1
260 2 0 180 -20 0 150 0 0 100 0 120 0 -1 -1 -1 -1 -1
340 3 0 200 -20 0 150 0 0 100 0 120 0 -1 -1 -1 -1 -1
420 4 0 180 -20 0 150 0 0 100 0 120 0 -1 -1 -1 -1 -1
500 5 0 200 -20 0 150 0 0 100 0 120 0 -1 -1 -1 -1 -1
580 6 0 180 -20 0 150 0 0 100 0 120 0 -1 -1 -1 -1 -1
660 7 0 200 150 0 150 0 0 100 0 120 0 -1 -1 -1 -1 -1
740 8 0 180 150 0 150 0 0 100 0 120 0 -1 -1 -1 -1 -1
820 9 0 200 150 0 150 0 0 100 0 120 0 -1 -1 -1 -1 -1
900 10 0 180 -20 0 150 0 0 100 0 360 0 -1 -1 -1 -1 -1


エクセルデータのダウンロード


ここで、敵の行動パターン情報はenemy[i].patternに入っているわけですが、これに入っている0〜10の値によって

enemy_pattern0関数〜enemy_pattern10関数へ自動的に処理を渡すにはどうしたらいいでしょうか?

ここで関数ポインタというものを使ってみます。

(*enemy_pattern[ENEMY_PATTERN_MAX])(int) = { enemy_pattern0, enemy_pattern1, .....

という代入を行えば、この配列に関数のアドレスが代入されます。

後は配列要素番号に先ほどのenemy[i].patternを指定してやれば処理が渡せます。

実際に関数ポインタを使って関数を呼んでいるのは赤字の部分です。



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

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

#define ENEMY_PATTERN_MAX 11

extern void enemy_pattern0(int);extern void enemy_pattern1(int);extern void enemy_pattern2(int);
extern void enemy_pattern3(int);extern void enemy_pattern4(int);extern void enemy_pattern5(int);
extern void enemy_pattern6(int);extern void enemy_pattern7(int);extern void enemy_pattern8(int);
extern void enemy_pattern9(int);extern void enemy_pattern10(int);

void (*enemy_pattern[ENEMY_PATTERN_MAX])(int) = {
    enemy_pattern0,    enemy_pattern1,    enemy_pattern2,    enemy_pattern3,    enemy_pattern4,
    enemy_pattern5,    enemy_pattern6,    enemy_pattern7,    enemy_pattern8,    enemy_pattern9,
    enemy_pattern10,
};

//空いている敵番号を検索
int enemy_num_search(){
    for(int i=0;i<ENEMY_MAX;i++){//フラグのたって無いenemyを探す
        if(enemy[i].flag==0){
            return i;//使用可能番号を返す
        }
    }
    return -1;//全部埋まっていたらエラーを返す
}

//敵情報を登録
void enemy_enter(){//敵の行動を登録・制御する関数
    int i,j,t;
    for(t=0;t<ENEMY_ORDER_MAX;t++){
        if(enemy_order[t].cnt==stage_count){//現在の瞬間がオーダーの瞬間なら
            if((i=enemy_num_search())!=-1){
                enemy[i].flag   =1;//フラグ
                enemy[i].cnt    =0;//カウンタ
                enemy[i].pattern=enemy_order[t].pattern;//移動パターン
                enemy[i].muki   =1;//向き
                enemy[i].knd    =enemy_order[t].knd;//敵の種類
                enemy[i].x      =enemy_order[t].x;//座標
                enemy[i].y      =enemy_order[t].y;
                enemy[i].sp     =enemy_order[t].sp;//スピード
                enemy[i].bltime =enemy_order[t].bltime;//弾の発射時間
                enemy[i].blknd  =enemy_order[t].blknd;//弾幕の種類
                enemy[i].blknd2 =enemy_order[t].blknd2;//弾の種類
                enemy[i].col    =enemy_order[t].col;//色
                enemy[i].wait   =enemy_order[t].wait;//停滞時間
                enemy[i].hp     =enemy_order[t].hp;//体力
                enemy[i].hp_max =enemy[i].hp;//体力最大値
                enemy[i].vx     =0;//水平成分の速度
                enemy[i].vy     =0;//鉛直成分の速度
                enemy[i].ang    =0;//角度
                for(j=0;j<6;j++)
                    enemy[i].item_n[j]=enemy_order[t].item_n[j];//落とすアイテム
            }
        }
    }
}

//敵の行動制御
void enemy_act(){
    int i;
    for(i=0;i<ENEMY_MAX;i++){
        if(enemy[i].flag==1){//その敵のフラグがオンになってたら
            if(0<=enemy[i].pattern && enemy[i].pattern<ENEMY_PATTERN_MAX){
                enemy_pattern[enemy[i].pattern](i);
                enemy[i].x+=cos(enemy[i].ang)*enemy[i].sp;
                enemy[i].y+=sin(enemy[i].ang)*enemy[i].sp;
                enemy[i].x+=enemy[i].vx;
                enemy[i].y+=enemy[i].vy;
                enemy[i].cnt++;
                enemy[i].img=enemy[i].muki*3+(enemy[i].cnt%18)/6;
                //敵が画面から外れたら消す
                if(enemy[i].x<-20 || FIELD_MAX_X+20<enemy[i].x || enemy[i].y<-20 || FIELD_MAX_Y+20<enemy[i].y)
                    enemy[i].flag=0;
            }
            else
                printfDx("enemy[i].patternの%d値が不正です。",enemy[i].pattern);
        }
    }
}

//敵処理メイン
void enemy_main(){
    enemy_enter();
    enemy_act();
}



後は角度計算の為の円周率の定義を設定してやります。

円周率はπも2πもよく使うので、予め掛け算して値も定義しておきます。



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

//円周率
#define PI 3.1415926535898
#define PI2 (PI*2)


実行結果



実行結果のように色々な行動を敵が行えば成功です。



- Remical Soft -