第二十一章 为自机加上碰撞判定和无敌处理吧

>>点此回到教程目录

之前我们已经为敌人增加了碰撞判定,现在我们也为自机加上碰撞判定吧。

首先,在进入具体的处理之前,我们进行一些预处理。

如果要对子弹进行判定处理,我们必须要让子弹自身保有判定范围之类的信息。

因此,我们像下面那样做。

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

//子弹的信息
typedef struct{
        int size_x,size_y,col_num;
        double range;
}bullet_info_t;

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

GLOBAL bullet_info_t bullet_info[10];//子弹的信息

—-在 load.cpp load函数中进行以下追加 —-

sound_se[3]=LoadSoundMem("../dat/se/char_death.wav");//自机被击毁的声音
ChangeVolumeSoundMem( 80, sound_se[3] ) ;

—-在 ini.cpp 中进行以下追加 —-

//将传入的信息放入结构体的函数
void input_bullet_info(bullet_info_t *binfo,int size_x,int size_y,int col_num,double range){
        binfo->size_x =size_x;  binfo->size_y =size_y;
        binfo->col_num=col_num; binfo->range  =range;
}

—-在 ini.cpp first_ini()函数中进行以下追加 —-

    //比如:0号子弹,大小76x76像素,有五种颜色,碰撞范围为17像素
        input_bullet_info(&bullet_info[0],76, 76, 5,17.0);
        input_bullet_info(&bullet_info[1],22, 22, 6, 4.0);
        input_bullet_info(&bullet_info[2], 5,120,10, 2.5);
        input_bullet_info(&bullet_info[3],19, 34, 5, 2.0);
        input_bullet_info(&bullet_info[4],38, 38,10, 2.0);
        input_bullet_info(&bullet_info[5],14, 16, 3, 3.5);
        input_bullet_info(&bullet_info[6],14, 18, 3, 2.0);
        input_bullet_info(&bullet_info[7],16, 16, 9, 2.5);
        input_bullet_info(&bullet_info[8],12, 18,10, 1.5);
        input_bullet_info(&bullet_info[9],13, 19, 3, 2.0);

first_ini函数保存着子弹的信息,我为了把这个碰撞判定的范围调整到合适的大小费了不少时间……

这个范围我还是不能接受!……如果您还是这样觉得话,那就自己反复测试然后调整吧。

在这个函数中,保存着子弹的大小、颜色的种类数、碰撞判定范围。

接下来,准备已经做好了,现在开始进入正式的处理了。

……虽然这么说,在上一章节中,我们已经做好了判定的模块,因此这次我们也同样地进行判定就行了,相当简单。

全部复制粘贴过来就OK了。

虽然把几个看起来都差不多的函数一个个地写出来感觉很浪费时间,不过这里要是泛用化的话稍微有些不好确认,因此我们后面在讨论函数的泛用化问题,现在我们只需要一一复制然后稍微更改一下就行了。

基本上来看,敌人的射击和自机的射击的碰撞判定处理是一样的。

—- out.cpp 变更 —-

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

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

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

//判定敌人和自机射击的子弹是否相碰
int out_judge_cshot(int i,int s){
    int j;
    if(cshot[i].cnt>0){ //如果射击的子弹的轨道至少计算过1次
        double x=cshot[i].x-enemy[s].x; //敌人和自机射击的子弹的距离
        double y=cshot[i].y-enemy[s].y;
    //防止溢出
        if(cshot[i].knd>=CSHOT_RANGE_MAX || enemy[s].knd>=ENEMY_RANGE_MAX)
            printfDx("out_judge_cshot溢出");
    //敌人的碰撞判定和自机射击的子弹的碰撞判定的合计范围
        double r=cshot_range[cshot[i].knd]+enemy_range[enemy[s].knd];
    //如果有必要计算中间的话
        if(cshot[i].spd>r){
        //保存1帧之前的位置
            double pre_x=cshot[i].x+cos(cshot[i].angle+PI)*cshot[i].spd;
            double pre_y=cshot[i].y+sin(cshot[i].angle+PI)*cshot[i].spd;
            double px,py;
            for(j=0;j<cshot[i].spd/r;j++){//前进部分÷碰撞判定部分次循环
                px=pre_x-enemy[s].x;
                py=pre_y-enemy[s].y;
                if(px*px+py*py<r*r)
                    return 1;
                pre_x+=cos(cshot[i].angle)*r;
                pre_y+=sin(cshot[i].angle)*r;
            }
        }
        if(x*x+y*y<r*r)//如果在碰撞判定范围内
            return 1;//碰撞
    }
    return 0;
}

//判定自机和敌人射击的子弹是否碰上
int out_judge_enemyshot(int s,int n){
    int j;
    if(shot[s].bullet[n].cnt>0) //如果射击的子弹的轨道至少计算过1次
        double x=shot[s].bullet[n].x-ch.x //敌人和自机射击的子弹的距离
        double y=shot[s].bullet[n].y-ch.y;
    //防止溢出
        if(shot[s].bullet[n].knd>=10)
            printfDx("out_judge_enemyshot溢出\n");
    //敌人射击的子弹和自机碰撞判定的合计范围
        double r=bullet_info[shot[s].bullet[n].knd].range+CRANGE;
    //如果有必要计算中间的话
        if(shot[s].bullet[n].spd>r){
        //保存1帧前的位置
            double pre_x=shot[s].bullet[n].x+cos(shot[s].bullet[n].angle+PI)*shot[s].bullet[n].spd;
            double pre_y=shot[s].bullet[n].y+sin(shot[s].bullet[n].angle+PI)*shot[s].bullet[n].spd;
            double px,py;
            for(j=0;j<shot[s].bullet[n].spd/r;j++){//前进部分÷碰撞判定部分次循环
                px=pre_x-ch.x;
                py=pre_y-ch.y;
                if(px*px+py*py<r*r)
                    return 1;
                pre_x+=cos(shot[s].bullet[n].angle)*r;
                pre_y+=sin(shot[s].bullet[n].angle)*r;
            }
        }
        if(x*x+y*y<r*r)//如果在碰撞判定内的话
            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;//将flag设置为不再继续
                    break;
                }
            }
        }
    }
}

//自机射击和敌人的处理
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;//将自机销毁
                        enemy[s].hp-=cshot[i].power;//敌人减少子弹所持有的power的量那么多的HP
                        enemy_death_judge(s);//决定敌人是否消灭
                    }
                }
            }
        }
    }
}

//敌人射击和自机的处理
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)){//且自己与这个子弹接触了
                        shot[s].bullet[n].flag=0;//销毁子弹

            /*在这里进行决死处理(译者注:决个毛蛋死,咱就喜欢抱B撞233333~)*/

                        if(ch.flag==0 && ch.mutekicnt==0){状态为一般状态,且不是无敌状态下
                            ch.flag    =2; //1:正在进行决死处理 2:已经被击毁且新的自机正在从下浮起来
                            ch.cnt    =0;
                            se_flag[3]=1;//击毁声
                            return;
                        }
                    }
                }
            }
        }
    }
}

//碰撞判定main
void out_main(){
    cshot_and_enemy();
    enemyshot_and_ch();
}

enemyshot_and_ch是进行敌人射击和自机的碰撞判定处理的函数,

不过,击中之后为什么要ch.flag=2?

事实上中子弹之后角色会进入两个阶段的状态。

首先,中子弹的瞬间会有“正在进行决死处理”。将这个状态设置为ch.flag=1。

如果在规定时间内没有按下Boom键的话接下来就会进入从画面下方将新的自机浮上来的状态。

将这个状态设置为ch.flag=2。

浮上来进入画面内的时候,如果进行了按键输入的话,就将状态还原。

其次在角色死掉的顺江让角色进入无敌状态。

而是否是无敌状态的flag是另外一个标记。

ch.mutekicnt如果不为0表示是无敌的。这个时候不进行中子弹处理。

其次,在graph函数内绘制角色的时候,每2帧绘制1次。

这样一来自机会一闪一闪地,给人的无敌的感觉。

无敌状态在适当的时间之后消失。

现在我们就进行上述处理的变更/追加处理吧。

—-在 char.cpp 中变更以下函数 —-

void calc_ch(){
        if(ch.cnt==0 && ch.flag==2){//如果当前瞬间死掉的话
                ch.x=FIELD_MAX_X/2;//设置坐标
                ch.y=FIELD_MAX_Y+30;
                ch.mutekicnt++;//进入无敌状态
        }
        if(ch.flag==2){//如果已经死掉正在上浮的话
                unsigned int push=CheckStatePad(configpad.left)+CheckStatePad(configpad.right)
                        +CheckStatePad(configpad.up)+CheckStatePad(configpad.down);
                ch.y-=1.5;//将角色往上移动
        //1秒以上,或者角色已经到达某个的位置且按下了某个键
                if(ch.cnt>60 || (ch.y<FIELD_MAX_Y-20 && push)){
                        ch.cnt=0;
                        ch.flag=0;//将角色的状态还原
                }
        }
        if(ch.mutekicnt>0){//如果无敌状态不为0的话
                ch.mutekicnt++;
                if(ch.mutekicnt>120)//如果已经2秒以上的话
                        ch.mutekicnt=0;//还原
        }

        ch.cnt++;//角色计数器自增
        ch.img=(ch.cnt%24)/6;//决定当前的图像
}

—-在 graph.cpp 的以下函数中变更 —-

//自机绘制
void graph_ch(){
        if(ch.mutekicnt%2==0)
                DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,img_ch[0][ch.img],TRUE);
}

运行结果



注:由于录制的FPS的原因,在上面的动画中并没有很好地展示角色闪烁的效果。这个动画和实际的效果不太一样。

请自己编译后运行确认结果。

>>点此回到教程目录

results matching ""

    No results matching ""