第十章 来显示敌人吧

>>点此回到教程目录

既然是要做的是STG,那真想快点开始制作弹幕啊。

但是,在构造弹幕数据之前,我们必须先构造好敌人的数据,所以我们先来搞好敌人的构造吧。

首先我们先写好敌人数据的结构体。

–在struct.h中进行以下追加–

//和敌人相关的结构体
typedef struct{
    //flag、计数器、移动模式、方向、敌人的种类、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就是flag、cnt就是计数器、pattern就是敌人移动的模式,现在我们用自我表意的名字命名了它们,当我们想要知道这个变量是用来做什么的时候,就看着它们的名字就了解了。

因为敌人肯定拥有许多不同的信息,因此虽然变量一下子变得很多很多,但是这并不复杂。

事先定义好游戏中用于表示计数器的stage_count,还有用于保存敌人图像句柄的img_enemy。

在define里面我们把画面上同时显示的最大敌人数使用ENEMY_MAX定义为30.

–在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、游戏启动之后只进行一次的初始化;2、每次新游戏开始时进行的初始化。

根据下面的例子来说,config的出事设定在新游戏开始的时候没有必要进行。

–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)。

当敌人的数据被登录之后,就将处理交给控制函数,这个函数控制了敌人的行动,而这些敌人按照登录了的移动模式移动。

当敌人跑到画面外面的时候,就把登陆flag取消掉(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){//如果敌人的flag为有效
            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;
        }
    }
}

//敌人处理main
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函数中变更了红字部分。

–main.cpp变更–

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

//循环必须进行的三大处理
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();//敌人处理main
                /*** 修改请注意 ***/

                graph_main();//绘制main

                /*** 修改请注意 ***/
                stage_count++;
                /*** 修改请注意 ***/

                break;
            default:
                printfDx("错误的func_state\n");
                break;
        }
        if(CheckStateKey(KEY_INPUT_ESCAPE)==1)break;//如果按下ESC键跳出循环
        ScreenFlip();//里外画面翻转
    }
    DxLib_End();//DX Library终止处理
    return 0;
}

现在将本次追加的函数进行登录。

–在function.h 中进行以下处理–

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

运行结果


如果等了一阵子敌人从上面降落下来,过了一会儿又往上回去的话,那就说明成功了。

>>点此回到教程目录

results matching ""

    No results matching ""