【四圣龙神录的编程教室】第11章、用Excel制作敌人的登场数据吧

原文地址:

http://dixq.net/rp/11.html


道中会有大量的敌人出现。

然后敌人身上带有大量的数据。至少也有下面这些数据吧。

————————————————————————————————————————————————————————————————————————————

--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 制作的敌人出现的数据文件。

在dat文件夹里下,建立一个 csv 文件夹,然后把文件放进去。内容大概是这样的。


如果是没有Excel的话,使用文本编辑工具,也可以打开看到里面的内容的。
首先,最左边是敌人出现的时间。在 60fps 运行的游戏里面,60 就等于1秒。
也就是说,这里的第一个敌人,在游戏开始后的1.6秒出现,这样的意思。
然后过了 10 个计数,也就是1/6 秒,下一个敌人就出现了。
敌人的数据,除了x坐标,其他都是一样的。在Excel 里要制作按同样变化增加的值是很方便的事情。
然后,在DxLib里,文件读取是要使用DxLib的函数的(为了使用存档文件)
如果DxLib 的文件读取函数的使用方法有不清楚的地方的话,去 它的主页学习一下吧。
基本上和C的标准函数 fopen(←这个稍微有点不同)和 fgetc 差不多的使用方法。
另外,虽然可能对csv文件没有准备,但只要用文本编辑器打开一看,我想应该就能看明白了。
只是用逗号来区分单元格,只要读取进来按照逗号来区分,就和普通的文本读取方式差不多。
那么我们就写一下这个读入的函数。
下面是把存了敌人出现的数据的 enemy_order 的excel 文件读取进来,存入变量的函数。
这个没有必要去特别的理解,只要知道这个是把数据存到变量里面的就行了。

————————————————————————————————————————————————————————————————————————————

--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);//取得一个字符
            if(inputc[i]=='/') 	//如果是斜线
            {
                while(FileRead_getc(fp)!='\n');//循环读取到行尾
                i=-1;//count 回到最开始
                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 组合来计算的,或者是 速度x分量vx,和速度y 分量vy来计算的。
这两种方式根据不同的情况,用途也不一样。
当然,这次的 enemy_pattern 所使用的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的元素
    {
        if(enemy[i].flag==0)
        {
            return i;//返回可以用的编号
        }
    }
    return -1;//全部都储存了的话,返回-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) //标记设置为ON
        {
            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();
}
————————————————————————————————————————————————————————————————————————————

运行结果:

连续的敌人掉下来,过一会上升的话,就对了。


(个人笔记:首先,load 里面,读取excel 表格里的内容,这记录着敌机登场的数据。然后在enemy_enter 里,到了点就读取对应的敌机数据,让敌机登场。
然后enemy_act 里面,首先调用敌机的行动模式函数,其中可以调整速度和方向等,然后对敌机的坐标,计数进行更新。 就是酱紫)


本人CSDN博客目录:

http://blog.csdn.net/tidus5


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值