第二十五章 为Boss加上碰撞判定吧

>>点此回到教程目录

现在我们为Boss加上碰撞判定,同时也为Boss的子弹加上碰撞判定吧。

同时在本章中我们也加上在前面一直忘记的角色的装饰。

接下来,前面我们已经用FILED_MAX_X来定义区域的宽度,不过写起来很麻烦(汗)

FIELD_MAX_X → FMX

FIELD_MAX_Y → FMY

FIELD_X → FX

FIELD_Y → FY

然后我们做了以上变更,在这里先事先说明一下。

接下来,我们说说碰撞判定的话题。

在前面的章节中,我们写了碰撞判定的函数。在那个时候我们说过我们后面来泛用化地写函数。

请和前面的函数对比起来看。

进行以下变更。

//射击的坐标:1 进行碰撞判定的物体:2
int out_judge(double x1, double y1, double x2, double y2,
                          double range1, double range2, double spd1,double angle1){
    int j;
    double x=x1-x2;//敌人和自机射击的子弹的距离
    double y=y1-y2;
    //敌人的碰撞判定和自机双色机的碰撞判定的合计范围
    double r=range1+range2;
    //如果有必要计算中间部分
    if(spd1>r){
        //保存1帧以前的位置
        double pre_x=x1+cos(angle1+PI)*spd1;
        double pre_y=y1+sin(angle1+PI)*spd1;
        double px,py;
        for(j=0;j<spd1/r;j++){//前进部分÷碰撞判定部分次循环
            px=pre_x-x2;
            py=pre_y-y2;
            if(px*px+py*py<r*r)
                return 1;
            pre_x+=cos(angle1)*r;
            pre_y+=sin(angle1)*r;
        }
    }
    if(x*x+y*y<r*r)//如果在碰撞判定内的话
        return 1;//碰撞
    return 0;
}

直到上个章节为止,我们要为每个变量都写一次函数,这次我们使这个函数能对任何变量都进行计算。

只需要变更要处理的参数的形式,算法和处理的内容完全没有变化。

这是射击的子弹和物体是否碰撞的判定函数,

x1,y1等有“1”的参数表示射击的信息 x2,y2等有“2”的参数表示物体的信息。 根据这个,我们在out文件中进行替换。

—- out.cpp 变更 —-

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

#define ENEMY_RANGE_MAX 4
#define CSHOT_RANGE_MAX 2
#define CRANGE 2.0
#define BRANGE 40.0

//敌人的碰撞判定范围
int enemy_range[ENEMY_RANGE_MAX]={16,30,16,50};
//自机设计的碰撞判定范围
int cshot_range[CSHOT_RANGE_MAX]={6,};

//碰撞判定
//射击的坐标:1 进行碰撞判定的物体:2
int out_judge(double x1, double y1, double x2, double y2,
              double range1, double range2, double spd1,double angle1){
    int j;
    double x=x1-x2;//敌人和自机射击的子弹的距离
    double y=y1-y2;
    //敌人的碰撞判定和自机设计的碰撞判定的合计范围
    double r=range1+range2;
//如果有必要计算中间的话
    if(spd1>r){
        //记录1帧之前的位置
        double pre_x=x1+cos(angle1+PI)*spd1;
        double pre_y=y1+sin(angle1+PI)*spd1;
        double px,py;
        for(j=0;j<spd1/r;j++){//前进部分÷碰撞判定部分次循环
            px=pre_x-x2;
            py=pre_y-y2;
            if(px*px+py*py<r*r)
                return 1;
            pre_x+=cos(angle1)*r;
            pre_y+=sin(angle1)*r;
        }
    }
    if(x*x+y*y<r*r)//如果在碰撞判定内
        return 1;//碰撞
    return 0;
}

//判断敌人和自机射击是否碰上
int out_judge_cshot(int i,int s){
    if(cshot[i].cnt>0){ //射击的子弹的轨道如果至少计算过一次
        if(out_judge(cshot[i].x,cshot[i].y,enemy[s].x,enemy[s].y,
            cshot_range[cshot[i].knd],enemy_range[enemy[s].knd],
            cshot[i].spd,cshot[i].angle)){
                return 1;
        }
    }
    return 0;
}

//判定Boss是否和自机射击碰撞
int out_judge_cshot_boss(int i){
    if(cshot[i].cnt>0){//如果射击的子弹的轨道至少计算过一次
        if(out_judge(cshot[i].x,cshot[i].y,boss.x,boss.y,
            cshot_range[cshot[i].knd],BRANGE,cshot[i].spd,cshot[i].angle)){
                return 1;
        }
    }
    return 0;
}

//判定自机是否和敌人射击碰上
int out_judge_enemyshot(int s,int n){
    if(shot[s].bullet[n].cnt>0){//如果射击的子弹的轨道至少计算过一次
        if(out_judge(
            shot[s].bullet[n].x,shot[s].bullet[n].y,ch.x,ch.y,
            bullet_info[shot[s].bullet[n].knd].range,CRANGE,
            shot[s].bullet[n].spd,shot[s].bullet[n].angle
            )){
                return 1;
        }
    }
    return 0;
}

//是否自机是否和Boss射击碰上
int out_judge_bossshot(int n){
    if(boss_shot.bullet[n].cnt>0){//射击的子弹的轨道至少计算过一次的话
        if(out_judge(
            boss_shot.bullet[n].x,boss_shot.bullet[n].y,ch.x,ch.y,
            bullet_info[boss_shot.bullet[n].knd].range,CRANGE,
            boss_shot.bullet[n].spd,boss_shot.bullet[n].angle
            )){
                return 1;
        }
    }
    return 0;
}

extern void enter_del_effect(int);

//判定敌人是否击毁
void enemy_death_judge(int s){
    int i;
    se_flag[8]=1;//碰上敌人的声音
    if(enemy[s].hp<0){//敌人的HP小于0的话
        enemy[s].flag=0;//消灭敌人
        se_flag[1]=1;//敌人的击毁声
        enter_del_effect(s);
        for(i=0;i<SHOT_MAX;i++){//敌人总数次循环
            if(shot[i].flag!=0){//如果有登录了的弹幕数据
                if(s==shot[i].num){//如果敌人有已经登录的弹幕的话
                    shot[i].flag=2;//设置弹幕不再继续
                    break;
                }
            }
        }
    }
}

//让s号敌人承受power伤害
void hit_enemy(int s,int power){
    enemy[s].hp-=power;//减少子弹所持有的power量的HP
    enemy_death_judge(s);//决定敌人是否击毁
}

//让Boss承受power伤害
void hit_boss(int power){
    boss.hp-=power;//让HP减少子弹所持有的power的量的
}


//自机射击和敌人的处理
void cshot_and_enemy(){
    int i,s;
    for(i=0;i<CSHOT_MAX;i++){//自机射击的子弹总数
        if(cshot[i].flag>0){
            for(s=0;s<ENEMY_MAX;s++){//敌人总数
                if(enemy[s].flag>0){
                    if(out_judge_cshot(i,s)){//如果自机射击的子弹和敌人碰上
                        cshot[i].flag=0;//将自己子弹销毁
                        hit_enemy(s,cshot[i].power);
                        break;
                    }
                }
            }
            //Boss出现且其不描绘flag为无效且在射击中
            if(boss.flag==1 && boss.graph_flag==0 && boss.state==2){
                if(out_judge_cshot_boss(i)){
                    cshot[i].flag=0;
                    hit_boss(cshot[i].power);
                }
            }
        }
    }
}

//敌人射击和自机的处理
void enemyshot_and_ch(){
    int s,n;
    //杂兵的射击
    for(s=0;s<SHOT_MAX;s++){//敌人的射击的子弹总数
        if(shot[s].flag>0){//如果射击已经登录
            for(n=0;n<SHOT_BULLET_MAX;n++){//子弹总数
                if(shot[s].bullet[n].flag==1){//如果子弹已经登录的话
                    if(out_judge_enemyshot(s,n)==1){//且自机与子弹接触
                        shot[s].bullet[n].flag=0;//销毁子弹
                        if(ch.flag==0 && ch.mutekicnt==0){
                            ch.cnt=0;
                            ch.flag=1;
                            se_flag[3]=1;
                        }
                    }
                }
            }
        }
    }
//Boss射击
    if(boss_shot.flag>0){//如果射击已经登录
        for(n=0;n<BOSS_BULLET_MAX;n++){//子弹总数
            if(boss_shot.bullet[n].flag==1){//如果子弹已经登录
                if(out_judge_bossshot(n)==1){//如果子弹已经和自机接触
                    boss_shot.bullet[n].flag=0;//销毁子弹
                    if(ch.flag==0 && ch.mutekicnt==0){
                        ch.cnt=0;
                        ch.flag=1;
                        se_flag[3]=1;
                    }
                }
            }
        }
    }
}

//让敌人承受Boom的伤害
void cbom_and_enemy(){
    int s;
    if(bom.flag!=1)return;
    for(s=0;s<ENEMY_MAX;s++){//敌人总数
        if(enemy[s].flag>0)//如果敌人是存在的
            hit_enemy(s,ch.power/20);//给予伤害
    }
//Boss存在且其不绘制flag为无效且在射击中
    if(boss.flag==1 && boss.graph_flag==0 && boss.state==2)
        hit_boss(ch.power/20);//承受伤害
}

//碰撞判定main
void out_main(){
    cbom_and_enemy();//使敌人承受Boom的伤害
    cshot_and_enemy();//自机射击和敌人的处理
    enemyshot_and_ch();//敌人射击和自机的处理
}

虽然代码有点长,不过基本上来看,Boss的处理只是放在和杂兵一样的处理里面,因此在构造完全相同。

其次,在龙神录中SC(转载者注:SpellCard,即符卡,弹幕集)的弹幕的时候还会变化背景呢。

在什么弹幕的时候有什么样子的背景,为了设定这一点我们为Boss增加back_knd这个变量。

首先,我们在ini中把所有的SC都准备好吧。

—-在 ini.cpp ini()中进行变更、追加 —-

        boss.appear_count[0]=50;//中路Boss出现的时刻
        for(int i=0;i<DANMAKU_MAX;i++){//各自弹幕的HP
                boss.set_hp[i]=10000;
                boss.hp_max=10000;
        }
        for(int i=0;i<DANMAKU_MAX;i++)//弹幕的各自背景种类
                boss.back_knd[i]=1;

变量和结构体也事先定义。

—-在 struct.h 中追加 —-

//Boos的信息
typedef struct{
        int flag,cnt,knd,wtime,state,endtime,hagoromo,graph_flag;
        int hp,hp_max;
        int appear_count[2],set_hp[DANMAKU_MAX],back_knd[DANMAKU_MAX];
        double x,y,ang,spd;
        phy_t phy;
}boss_t;

—-在 define.h 中进行以下变更 —-

//区域的尺寸
#define FMX 384
#define FMY 448
//区域 左上角坐标
#define FX 32
#define FY 16

—-在 GV.h 中追加 —-

GLOBAL int img_chetc[10];       //和角色相关的其它图像
GLOBAL int img_etc[50];//其它图像

这样就在etc中定义了和角色的装饰、Boss的HP表示相关的图像。

我们将它们读取出来然后绘制。

—-在 load.cpp 的load函数中追加 —-

        img_chetc[0]    = LoadGraph( "../dat/img/char/atari.png" );//碰撞判定
        img_chetc[2]    = LoadGraph( "../dat/img/char/ball.png" );//球

        img_etc[1]      = LoadGraph( "../dat/img/enemy/hp.png" );
        img_etc[7]      = LoadGraph( "../dat/img/enemy/hp_boss.png" );//Boos 的HP

—-在 graph.cpp中追加、变更 —-

//绘制Boss
void graph_boss(){
        int i;
        if(boss.flag==0)return;
        DrawRotaGraphF(boss.x+FX+dn.x,boss.y+FY+dn.y,1.0f,0.0f,img_dot_riria[0],TRUE);
        if(boss.hp_max==0){printfDx("graph_boss的0%\n");return;}
        for(i=0;i<FMX*0.98*boss.hp/boss.hp_max;i++){
                if(boss.back_knd[boss.knd]==1)
                        DrawGraph(3+FX+i+dn.x,2+FY+dn.y,img_etc[7],FALSE);
                else
                        DrawGraph(3+FX+i+dn.x,2+FY+dn.y,img_etc[1],FALSE);
        }
}

//自机绘制
void graph_ch(){
        double sx,sy,ny=(sin(2.0*PI*(count%50)/50)*3),ang=2.0*PI*(count%120)/120;

        if(CheckStatePad(configpad.slow)>0)//如果低速移动中
                sx=15,sy=15+ny;//拉近
        else
                sx=30,sy=30+ny;//往普通的位置移动

        DrawRotaGraphF( ch.x-sx+FX, ch.y+sy+FY, 1.0f,  ang, img_chetc[2], TRUE );
        DrawRotaGraphF( ch.x+sx+FX, ch.y+sy+FY, 1.0f, -ang, img_chetc[2], TRUE );

        if(ch.mutekicnt%2==0){//无敌中的话亮灭
                //自机显示
                DrawRotaGraphF(ch.x+FX+dn.x,ch.y+FY+dn.y,1.0f,0.0f,img_ch[0][ch.img],TRUE);
                if(CheckStatePad(configpad.slow)>0)//如果低速移动中的话显示出碰撞判定
                        DrawRotaGraphF( ch.x+FX, ch.y+FY, 1.0f, 2.0*PI*(count%120)/120, img_chetc[0], TRUE );
        }
}

现在来绘制SC用的背景。

boos.knd的指示编号boos.back_knd在1的时候表示SC,这一章中我们全部置为1.

然后我们背景什么都不绘制,让它一片漆黑。

—- graph_back.cpp 变更 —-

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

void graph_back00(){//通常背景
        SetDrawArea( 32 , 16 , 416 , 464 ) ;//设定绘制可能的区域
        DrawGraph(FX,count%700+FY-700,img_back[0],FALSE);
        DrawGraph(FX,count%700+FY    ,img_back[0],FALSE);
        SetDrawArea( 0, 0, 640, 480);//还原区域
}

void graph_back01(){//SC用背景
        SetDrawArea( 32 , 16 , 416 , 464 ) ;//设置绘制可能的区域

        //在这里绘制SC用的背景

        SetDrawArea( 0, 0, 640, 480);//还原区域
}

void graph_back_main(){
        //Boos为有效且在放SC
        if(boss.flag==1 && boss.back_knd[boss.knd]==1)
                graph_back01();
        else//除此之外
                graph_back00();
}

—- func.h 变更 —-

extern void boss_shot_bulletH000();

void (*boss_shot_bullet[DANMAKU_MAX])() =
{
        boss_shot_bulletH000,
        boss_shot_bulletH000,
        boss_shot_bulletH000,
        boss_shot_bulletH000,
        boss_shot_bulletH000,
        boss_shot_bulletH000,
        boss_shot_bulletH000,
};

在func.h中写入弹幕情报。

现在我们只做了H000这个弹幕,后面在增加弹幕的时候,这个也会增加。

因此在这里加入的函数的顺序就是弹幕的顺序。

现在,在弹幕结束的时候从0号弹幕开始。

7次弹幕结束了的话,会发生指针错误请注意一下。

运行结果



>>点此回到教程目录

results matching ""

    No results matching ""