第十一章 使用Excel来设置敌人出现的数据吧

>>点此回到教程目录

在道中会出来很多敌人。

同时敌人拥有的信息有很多。至少也得有下面这些吧:

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

typedef struct{
        //计数器、移动模式、敌人的种类
        int cnt,pattern,knd;
        //初始化坐标和移动速度
        double x,y,sp;
        //弹幕开始时间、弹幕种类、颜色、体力、子弹的种类、停止时间、物品(6个种类)
        int bltime,blknd,col,hp,blknd2,wait,item_n[6];
}enemy_order_t;

这是和敌人出现信息有关的结构体。

何时、何处、什么样子的敌人、射过来什么样子的子弹、要停止多久、是否回去……之类的信息。

我们必须要指定许多东西。但是,在计数器达到112时x坐标在这里、y坐标在那里、移动模式是这样、计数器达到187的时候又要这样又要那样……

要是将这些数据全部手动输入然后代入到数据中的话会很麻烦的。

因此我们试试使用Excel来制作敌人的出现的数据吧。

首先,从这里下载已经用Excel写好了的出现信息数据。

Excel数据在这里下载

在dat文件夹里面增加一个“csv”的文件夹,然后把Excel文件放进去。内部应该是这样子的。

如果没有Excel的话,文本处理器也能够打开,如下:

首先最左边的是敌人出现的时刻。因为这是60fps的游戏,60=1秒。

也就是说,这表示第一个敌人在游戏开始之后1.6秒左右出现。

然后置10个计数,也就是说每隔1/6秒就出现一个敌人。

敌人的信息除了x坐标以外其它都是一样的。使用Excel能够简单地作成有同样变化的值,非常方便。

接下来,使用DX Library将文件读入的时候,使用Dx Library的函数(为了使用archive)。

如果不知道DX Library的文件读入函数的使用方法,请到其参考主页上学习。

使用起来基本上和C语言的标准函数fopen(←只有一点区别)、fgetc之类的函数的方法相同。

其次,我认为就算是csv文件,即使没有使用过的经验,将这个文件用文本编辑器打开应该很快就能够明白了。由于这只是简单地将单元用逗号隔开用于隔开的数据,因此只要用逗号隔开然后仔细阅读的话,一般而言就能够掌握和阅读一般文本数据一样的要领了。那么,现在我们来写这个读入函数。

下面我们把从Excel数据读入用于存储敌人出现信息的变量enemy_order中,虽然用了一个代入函数,但是这没有有必要理解它,只要知道这个函数是用来把数据带入这个变量的就行了。

–在load.cpp中进行以下追加 –

//将敌人出现的信息从Excel读入并保存的函数
void load_story(){
        int n,num,i,fp;
        char fname[32]={"../dat/csv/storyH0.csv"};
        int input[64];
        char inputc[64];

        fp = FileRead_open(fname);//读入文件
        if(fp == NULL){
                printfDx("read error\n");
                return;
        }
        for(i=0;i<2;i++)//丢掉最开始的两行
                while(FileRead_getc(fp)!='\n');

        n=0 , num=0;
        while(1){
                for(i=0;i<64;i++){
                        inputc[i]=input[i]=FileRead_getc(fp);//读入1个字符 
                        if(inputc[i]=='/'){//如果是斜杠
                                while(FileRead_getc(fp)!='\n');//循环一直到换行
                                i=-1;//重置计数器
                                continue;
                        }
                        if(input[i]==',' || input[i]=='\n'){//如果是逗号或者换行
                                inputc[i]='\0';//将到此的所有文字作为字符串
                                break;
                        }
                        if(input[i]==EOF){//如果到了文件尾
                                goto EXFILE;//终止
                        }
                }
                switch(num){
                        case 0: enemy_order[n].cnt      =atoi(inputc);break;
                        case 1: enemy_order[n].pattern  =atoi(inputc);break;
                        case 2: enemy_order[n].knd      =atoi(inputc);break;
                        case 3: enemy_order[n].x        =atof(inputc);break;
                        case 4: enemy_order[n].y        =atof(inputc);break;
                        case 5: enemy_order[n].sp       =atof(inputc);break;
                        case 6: enemy_order[n].bltime   =atoi(inputc);break;
                        case 7: enemy_order[n].blknd    =atoi(inputc);break;
                        case 8: enemy_order[n].col      =atoi(inputc);break;
                        case 9: enemy_order[n].hp       =atoi(inputc);break;
                        case 10:enemy_order[n].blknd2   =atoi(inputc);break;
                        case 11:enemy_order[n].wait     =atoi(inputc);break;
                        case 12:enemy_order[n].item_n[0]=atoi(inputc);break;
                        case 13:enemy_order[n].item_n[1]=atoi(inputc);break;
                        case 14:enemy_order[n].item_n[2]=atoi(inputc);break;
                        case 15:enemy_order[n].item_n[3]=atoi(inputc);break;
                        case 16:enemy_order[n].item_n[4]=atoi(inputc);break;
                        case 17:enemy_order[n].item_n[5]=atoi(inputc);break;
                }
                num++;
                if(num==18){
                        num=0;
                        n++;
                }
        }
EXFILE:
        FileRead_close(fp);
}

想完全理解的话太阳都要落山了,那么我们就先把它放到这里继续往下走吧。

在进入具体的处理之前,我先说明几个细微的增加点。

在ini.cpp中,在初始化函数中初始化现在我们增加了的数据。 在define.h中,设定了允许登录的最大数量。 在Gv.h中我们定义了使用到的变量。 –在ini.cpp的ini函数中进行以下追加-

memset(enemy_order,0,sizeof(enemy_order_t)*ENEMY_ORDER_MAX);

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

//敌人出现信息的最大数量
#define ENEMY_ORDER_MAX 500

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

GLOBAL enemy_order_t enemy_order[ENEMY_ORDER_MAX];//敌人的出现信息

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

GLOBAL void load_story();
–main.cpp中的main函数的switch语句中的以下部分变更–

            case 99://STG开始前的初始化
                ini();

                /*** 修改注意 ***/
                load_story();
                /*** 修改注意 ***/

                func_state=100;
                break;

这样一来,我们就把这次敌人出现信息相关的数据放到enemy_order里面了。

然后我们把保存了的数据在下面进行登录,而这实际上就是在游戏内登录敌人。

使用enemy_enter进行敌人数据的登录。

因为enemy_order[n].cnt中已经存入了敌人应该合适出现的数据,那么便在与当前的游戏计数器一致的时候,进行敌人数据的登录。

进行敌人数据登录的时候,为了找到哪个编号是空的,我们实现一个叫做enemy_num_search的函数。

放入敌人数据的enemy数组下标是0~ENEMY_MAX-1,这个函数将告诉我们哪个编号可以使用。如果全部都不是空的话那么返回-1,这个时候就不再进行登录。

另外,前文已经说过enemy[n].wait表示停止时间,像下面的红字那样使用。

这样一来就可以控制敌人的停止时间了。

再者,之所以敌人移动的计算,既有利用ang和sp组合的计算,也有利用vx、vy组合的计算,这是因为根据情况的不同,用途会不同。

当然,这次在enemy_parttern中使用到的vy=2也可以和ang=3.14/2、sp=2进行置换,前者更容易理解就是了。

–enemy.cpp变更–

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

//使用敌人的移动模式0进行移动控制
void enemy_pattern0(int i){
    int t=enemy[i].cnt;
    if(t==0)
        enemy[i].vy=2;//下降
    if(t==60)
        enemy[i].vy=0;//静止

    /*** 修改注意 ***/
    if(t==60+enemy[i].wait)//在登录的时间区间内停止
    /*** 修改注意 ***/

        enemy[i].vy=-2;//上升
}

//检测空闲的敌人编号
int enemy_num_search(){
        for(int i=0;i<ENEMY_MAX;i++){//搜寻没有立上flag的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;//flag
                                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){//如果敌人的flag为有效
            enemy_pattern0(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;
        }
    }
}

//敌人处理main
void enemy_main(){
    enemy_enter();
    enemy_act();
}

运行结果


敌人连续地落下,等了一会儿又往回跑的话,就说明成功了。

>>点此回到教程目录

results matching ""

    No results matching ""