STGを作るからには早く弾幕を作りたいですね。

弾幕データを登録するにはまず敵データを作らないといけないので、さっさと作ってしまいましょう。

とりあえず敵データの構造体を用意します。



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

//敵に関する構造体
typedef struct{
        //フラグ、カウンタ、移動パターン、向き、敵の種類、HP最大値、落とすアイテム、画像
        int flag,cnt,pattern,muki,knd,hp,hp_max,item_n[6],img;
        //座標、速度x成分、速度y成分、スピード、角度
        double x,y,vx,vy,sp,ang;
        //弾幕開始時間、弾幕の種類、弾の種類、色、状態、待機時間、停滞時間
        int bltime,blknd,blknd2,col,state,wtime,wait;
}enemy_t;



なんじゃこりゃ!

ってほど変数が混在していますが、別に覚える必要ありません。

flagはフラグ、cntはカウンタ、patternは敵の移動パターンと、そのままの意味を示す名前で用意していますし、

必要になったらなんの変数だったかその時思い出してもらえたらOKです。

敵には色んな情報を持たせないといけないので変数が沢山になってしまいましたが、難しい事は何もありません。

ゲーム中のカウンタを示すstage_countを用意し、敵の画像ハンドルを格納するimg_enemyを用意します。

defineには敵の同時表示最大数である30をENEMY_MAXとして定義しましょう。



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

GLOBAL int stage_count;
GLOBAL int img_enemy[3][9];//敵画像9枚分 X3体分
GLOBAL enemy_t enemy[ENEMY_MAX];


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

//敵の同時表示最大数
#define ENEMY_MAX 30


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

LoadDivGraph( "../dat/img/enemy/0.png" , 9 , 3 , 3 , 32 , 32 , img_enemy[0] ) ;



初期化関数は、起動後、1回だけしかしない初期化と、ゲームが始まるたびに行う初期化にわけます。

下の例で言えば、コンフィグの初期設定などはゲームが始まるたびに行う必要はありませんが、

自機データ、敵データの初期化などはゲームが始まるたびにしないといけませんよね。



--ini.cppを変更--

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

//一番最初の初期化
void first_ini(){
        configpad.down=0;
        configpad.left=1;
        configpad.right=2;
        configpad.up=3;
        configpad.bom=4;
        configpad.shot=5;
        configpad.slow=11;
        configpad.start=13;
        configpad.change=6;
}

//ゲームの初期化
void ini(){
        stage_count=1;
        memset(&ch,0,sizeof(ch_t));//自機データの初期化
        ch.x=FIELD_MAX_X/2;
        ch.y=FIELD_MAX_Y*3/4;
        memset(enemy,0,sizeof(enemy_t)*ENEMY_MAX);//敵データの初期化
}



敵データ処理の大きな流れとして

ゲームカウンタがあるポイントに達した時、敵データを登録する(enemy_enter)。

登録された敵データがあれば、登録された移動パターンにそった敵の移動制御を行う(enemy_pattern)関数に処理を渡し

敵の移動制御を行う。

敵が画面から外れると、登録フラグを落とす(enemy_act)。

という感じです。

下の例では試しにゲームカウンタが100の時に0番の敵を登録し、下に下がって、上に上がっていくようにしています。



--enemy.cppを変更--

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

//敵の移動パターン0での移動制御
void enemy_pattern0(int i){
    if(enemy[i].cnt<60){
        enemy[i].y+=2.0;
    }
    if(enemy[i].cnt>60+240){
        enemy[i].y-=2.0;
    }
}

//敵データの登録
void enemy_enter(){
    if(stage_count==100){//ゲームが始まって100カウントで登録
        enemy[0].cnt    =0;
        enemy[0].muki   =1;
        enemy[0].flag   =1;
        enemy[0].bltime =150;
        enemy[0].hp     =1000;
        enemy[0].hp_max =enemy[0].hp;
        enemy[0].pattern=0;
        enemy[0].x      =FIELD_MAX_X/2;
        enemy[0].y      =-20;
    }
}

//敵の行動制御
void enemy_act(){
    int i;
    for(i=0;i<ENEMY_MAX;i++){
        if(enemy[i].flag==1){//その敵のフラグがオンになってたら
            enemy_pattern0(i);
            enemy[i].cnt++;
            enemy[i].img=enemy[i].muki*3+(enemy[i].cnt%18)/6;
            //敵が画面から外れたら消す
            if(enemy[i].x<-50 || FIELD_MAX_X+50<enemy[i].x || enemy[i].y<-50 || FIELD_MAX_Y+50<enemy[i].y)
                enemy[i].flag=0;
        }
    }
}

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



描画処理では増えた敵データの表示を行っているだけです。

登録されている敵データを探し、そのデータを表示しています。




--graph.cppを変更--

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

void graph_enemy(){
        int i;
        for(i=0;i<ENEMY_MAX;i++){
                if(enemy[i].flag==1){
                        DrawRotaGraphF(enemy[i].x+FIELD_X,enemy[i].y+FIELD_Y,1.0f,0.0f,img_enemy[0][enemy[i].img],TRUE);
                }
        }
}

void graph_ch(){
        DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,img_ch[0][ch.img],TRUE);
}

void graph_board(){
        DrawGraph(  0,  0,img_board[10],FALSE);
        DrawGraph(  0, 16,img_board[11],FALSE);
        DrawGraph(  0,464,img_board[12],FALSE);
        DrawGraph(416,  0,img_board[20],FALSE);
}

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



メイン関数には赤の部分を変更しました。



--main.cppを変更--

#define GLOBAL_INSTANCE 
#include "../include/GV.h"

//ループで必ず行う3大処理
int ProcessLoop(){
    if(ProcessMessage()!=0)return -1;//プロセス処理がエラーなら-1を返す
    if(ClearDrawScreen()!=0)return -1;//画面クリア処理がエラーなら-1を返す
    GetHitKeyStateAll_2();//現在のキー入力処理を行う
    GetHitPadStateAll();  //現在のパッド入力処理を行う
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){
    ChangeWindowMode(TRUE);//ウィンドウモード
    if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;//初期化と裏画面化

    while(ProcessLoop()==0){//メインループ
        switch(func_state){
            case 0://初回のみ入る処理
                load();        //データロード
                first_ini();//初回の初期化
                func_state=99;
                break;
            case 99://STGを始める前に行う初期化
                ini();
                func_state=100;
                break;
            case 100://通常処理
                calc_ch();    //キャラクタ計算
                ch_move();    //キャラクタの移動制御
                enemy_main();//敵処理メイン
                graph_main();//描画メイン
                stage_count++;
                break;
            default:
                printfDx("不明なfunc_state\n");
                break;
        }
        if(CheckStateKey(KEY_INPUT_ESCAPE)==1)break;//エスケープが入力されたらブレイク
        ScreenFlip();//裏画面反映
    }
    DxLib_End();//DXライブラリ終了処理
    return 0;
}



今回追加した関数を登録しておきます。



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

GLOBAL void enemy_main();
GLOBAL void ini();


実行結果



しばらくすると、敵が降りてきて、少し経ったら上に戻っていけば成功です。

- Remical Soft -