第五十三章 试着制作汉字弹幕吧

>>点此回到教程目录

接下来我们把在52章中制作好的汉字弹幕数据显示出来吧。

首先,我们做一些预先准备。并且,本次我们追加了2种新的子弹。

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

//用于文字弹幕的子弹最大数(53)
#define FONT_BULLET_MAX 1000
//用于文字弹幕的文字最大数(53)
#define FONT_NUM_MAX 10

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

GLOBAL BlPoint_t BlPoint[FONT_NUM_MAX];//汉字弹幕用变量 (53)

—在 func.h 中进行一下追加 —

extern void boss_shot_bulletH000();
extern void boss_shot_bulletH001();
(中间省略)
extern void boss_shot_bulletH011();

/***修改请注意***/
extern void boss_shot_bulletH012();//(53)
extern void boss_shot_bulletH013();//(53)
/***修改请注意***/

void (*boss_shot_bullet[DANMAKU_MAX])() =
{
        /***修改请注意***/
        boss_shot_bulletH012,//汉字弹幕(53)
        boss_shot_bulletH013,//汉字弹幕(53)
        /***修改请注意***/

//中路Boss
        boss_shot_bulletH000,//Normal
(中间省略)
};

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

/单个汉字子弹的信息(53)
typedef struct{
    int Knd;//子弹的种类
    int Col;//子弹的颜色
    float Angle;//子弹的角度
    float x,y;
}Bl_t;

//汉字子弹整体信息(53)
typedef struct{
    int Num;//已经登录的个数
    Bl_t Bl[ FONT_BULLET_MAX ];//登录的子弹信息
}BlPoint_t;

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

    //载入汉字弹幕的子弹坐标数据(53)
        GLOBAL int load_font_dat(char name[32], BlPoint_t *Bp);

—在 ini.cpp 中进行以下变更 —

        /***修改请注意***/
        boss.appear_count[0]=50;//中路Boss出现时刻(42)(47)(53)
        /***修改请注意***/

—在 load.cpp 的 load() 中进行以下追加 —

        LoadDivGraph( "../dat/img/bullet/b12.png", 10 , 10 , 1 , 12 ,  12 , ImgBullet[12]) ;
        LoadDivGraph( "../dat/img/bullet/b13.png", 10 , 10 , 1 , 22 ,  22 , ImgBullet[13]) ;

接下来,我们来读入弹幕数据,并显示出来吧。

首先就是读入。既然我们的数据是用fwrite把结构体的内容一口气写出去的,因此我们就可以用fread一口气把结构体的内容都读进来。

紧接着,汉字坐标数据的保存我们用BlPoint这个数组来进行。

我们试着来做出这样的函数,我们只需要将想要用来保存汉字弹幕数据的变量以及文件名作为参数传到这个函数中,那么就可以将汉字弹幕数据保存到那个变量中。

//文字弹幕的子弹坐标数据载入(53)
int load_font_dat(char name[64], BlPoint_t *Bp){
    int i;
    char fname[128];
    FILE *fp;
    sprintf(fname,"../dat/font/%s.dat",name);//将接收到的文件名放入路径中
    fp = fopen( fname , "rb" );//使用路径以及文件名来打开文件
    if( fp == NULL )
        return -1;
    fread( Bp, sizeof(BlPoint_t), 1, fp );
    fclose(fp);
    return 0;
}

汉字弹幕数据我们存到了“dat/font/龍.dat”中。

传入的name字符串比如有“龍”这个字,为了让它变为“data/font/龍.dat”这个样子,我们使用sprintf函数向上面那样子写就行了。

接下来,只要用fread函数读入那个路所指向的文件即可。如果读入出现错误返回-1。

那么接下来试着制作弹幕吧。

这次我们试着制作基本的2个弹幕。

首先,弹幕数据是这个样子的。

这次为了让颜色鲜明我们弄得五颜六色,嘛,到现在为止我想还没有人认为我的颜色使用是很没品的吧。(诶?)

如果你能够明白接下来我们能够像这样子,同时处理许多种颜色以及许多种类的子弹的话我就很欣慰了。

使用这个数据我们试着制作下面那样的2个弹幕吧。



第1个弹幕,我们让弹幕从中心扩散进行绘制。

这个弹幕的实现,首先我们要利用在-1~1范围内表示的坐标,用离开中心有多远来决定速度。

由于最开始子弹击中在像素-1~1之间,因此可以看见它们在中心位置重叠。

然后,用离开中心的距离来设定相应的速度,以这个速度从中心向外扩张。

一般而言,我们利用勾股定理来求得离开中心的距离,也即是√(xx+yy)这个公式。

至于子弹方向的计算,我们也利用坐标是用-1~1来表示的这一点,我们只需要把这个值传入计算角度的函数atan里面就行了。

另外,当然像下面那样,子弹的颜色和种类没有必要利用到弹幕数据中的内容。

这是因为在程序内变更就行了。

在这里,有一点需要注意,如果(0,0)有坐标数据,以及这种情况下调用atan2函数的时候会出现问题,由于我们是利用离开中心的距离来决定速度的,那么既然(0,0)有子弹,那么它会永远禁止在那里,这个时候我们应该将其作为错误情况。

因此制作扩散系的弹幕数据的时候,我们不要紧紧地靠近中心来制作。

另外,靠近中心的子弹如果不进行加速的话很难往外移动,因此我们有必要对其加速。

---在 boss_shotH.cpp 中进行以下追加 ---

//汉字弹幕(53)
void boss_shot_bulletH012(){
#define TM012 200
    int i,k,t=boss_shot.cnt%TM012,t2=boss_shot.cnt;
    static int num;
    if(t2==0){
        input_phy_pos(FMX/2,FMY/2, 50);//向正中间移动
        num=0;//次数初始化
        if(load_font_dat("龍", &BlPoint[0])!=0){
            printfDx("文字弹幕数据读入错误\n");
        }
    }
    if(t==50){
        for(i=0;i<BlPoint[0].Num;i++){//子弹个数次循环
            if((k=search_boss_shot())!=-1){
                float sx = BlPoint[0].Bl[i].x, sy = BlPoint[0].Bl[i].y;
                if(sx==0 && sy==0){
                    printfDx("汉字坐标数据中有(0,0)坐标数据\n");
                    return ;
                }
                int col=BlPoint[0].Bl[i].Col, knd=BlPoint[0].Bl[i].Knd;
                if(num==1){
                    col = 0;
                    knd = 4;
                }
                boss_shot.bullet[k].col   = col;//子弹的颜色
                boss_shot.bullet[k].x     = boss.x+BlPoint[0].Bl[i].x;//坐标
                boss_shot.bullet[k].y     = boss.y+BlPoint[0].Bl[i].y;
                boss_shot.bullet[k].knd   = knd;//子弹的种类
                boss_shot.bullet[k].angle = atan2(BlPoint[0].Bl[i].y, BlPoint[0].Bl[i].x);//角度
                boss_shot.bullet[k].flag  = 1;
                boss_shot.bullet[k].cnt   = 0;
                boss_shot.bullet[k].spd   = 
                    sqrt(BlPoint[0].Bl[i].x*BlPoint[0].Bl[i].x+BlPoint[0].Bl[i].y*BlPoint[0].Bl[i].y);//速度
                boss_shot.bullet[k].eff   = 0;
                boss_shot.bullet[k].state = 0;
            }
        }
    }
    for(i=0;i<BOSS_BULLET_MAX;i++){//加速
        if(boss_shot.bullet[i].flag>0){
            if(boss_shot.bullet[i].state==0){
                boss_shot.bullet[i].spd *= 1.01;
            }
        }
    }
    if(t==TM012-1){
        num = (num+1)%2;
    }
}

在前面我们好不容易在弹幕坐标数据中完成了的Angle并没有使用。

这是由于我们规定移动时候的Angle是前进方向。

因此,在弹幕坐标数据中完成的子弹角度,会在像上面的视频中的第2个弹幕那样子的情况下使用。

将这个展示如下。

—在 boss_shotH.cpp 中进行以下部分追加 —

//汉字弹幕(53)
void boss_shot_bulletH013(){
    int i,n,k,t=boss_shot.cnt,t2=boss_shot.cnt;
    if(t2==0){
        input_phy_pos(FMX/2,FMY/2, 50);//向正中间移动
        if(load_font_dat("龍", &BlPoint[0])!=0){
            printfDx("文字弹幕数据读入错误\n");
        }
    }
    if(t==50){
        for(i=0;i<BlPoint[0].Num;i++){
            if((k=search_boss_shot())!=-1){
                float sx = BlPoint[0].Bl[i].x, sy = BlPoint[0].Bl[i].y;
                if(sx==0 && sy==0){
                    printfDx("汉字坐标数据中有(0,0)坐标数据\n");
                    return ;
                }
                boss_shot.bullet[k].col   = BlPoint[0].Bl[i].Col;//子弹的颜色
                boss_shot.bullet[k].x     = boss.x+BlPoint[0].Bl[i].x*200;//坐标
                boss_shot.bullet[k].y     = boss.y+BlPoint[0].Bl[i].y*200;
                boss_shot.bullet[k].knd   = BlPoint[0].Bl[i].Knd;//子弹的种类
                boss_shot.bullet[k].angle = BlPoint[0].Bl[i].Angle;//角度
                boss_shot.bullet[k].flag  = 1;
                boss_shot.bullet[k].cnt   = 0;
                boss_shot.bullet[k].spd   = 0;//速度
                boss_shot.bullet[k].eff   = 0;
                boss_shot.bullet[k].state = 0;
            }
        }
    }
    for(i=0;i<BOSS_BULLET_MAX;i++){
        if(boss_shot.bullet[i].flag>0){
            if(boss_shot.bullet[i].state==0){
                int cnt = boss_shot.bullet[i].cnt;
                if(60<cnt && cnt<=120){
                    boss_shot.bullet[i].x += 1;
                }
                if(120<cnt && cnt<=240){
                    boss_shot.bullet[i].x -= 1;
                }
                if(240<cnt && cnt<=300){
                    boss_shot.bullet[i].x += 1;
                }
                if(350==cnt){
                    boss_shot.bullet[i].spd = 1;
                }
            }
        }
    }
}

我们可以使用把spd置为0,强制其不使用spd进行移动、或者在Angle的方向上使用spd进行移动的方法。

强制移动并不只是进行横向移动,再加以缩小扩大、旋转的处理不也很有趣么。

不过,由于spd为0的移动方法并不是设想中的移动,因此并没有轨道计算然后碰撞判定计算,所以请注意有子弹运动过快的情况(即有子弹擦过去的情况)。

另外,像第2个弹幕最后那样,如果制作按照决定好了的Angle方向飞过去的子弹的话,那么我们就可以让子弹在单纯地利用计算式没法描述的飞行模式下飞行了。

接下来就是您自行发挥的时候了☆。

>>点此回到教程目录

results matching ""

    No results matching ""