【四圣龙神录的编程教室】第21章、给自机加上碰撞检测和无敌效果吧

原文地址:

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


敌机的碰撞检测以及加上了,这次我们来做自机的碰撞检测吧。

首先,在做具体的处理之前,有些准备工作要做。

要做子弹的碰撞检测,我们需要知道比如子弹的碰撞判定范围这些信息。

那么,我们就像下面这样做。

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

---- 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像素,有5种颜色,碰撞范围是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_init 函数里有子弹的信息,我们要花点时间,根据这些信息来制定合适大小的碰撞范围。

如果有人觉得这个范围不行,那么就自己试试,调整一下吧。

这个函数里,包含了子弹大小,颜色的种类,和碰撞范围等信息。

那么,准备工作做好了,现在来看自机的处理吧。

话说回来,之前已经做好了一个碰撞检测的模块了,这次也是做同样的判定就行了,比较简单。

基本上复制过来就行。

虽然存在几个几乎一样的函数感觉不大好,但在这里把它改成通用的函数的话,会不大好理解。

函数的通用化以后再讲吧,这里先复制过来稍微改一下下就行了。

基本上敌机和自机的碰撞检测是一样的。

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

---- 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){//对子弹轨道的每一刻的状态都进行计算
        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 OVERFLOW");
        //敌机的碰撞检测范围和自机子弹的碰撞检测范围加起来
        double r=cshot_range[cshot[i].knd]+enemy_range[enemy[s].knd];
        //如果需要计算其中的过程
        if(cshot[i].spd>r){
            //储存一帧前的位置
            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){//对子弹轨道的每一刻的状态都进行计算
        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 OVERFLOW\n");
        //敌机子弹和自机的碰撞判定范围加起来
        double r=bullet_info[shot[s].bullet[n].knd].range+CRANGE;
        //如果需要计算其中的过程
        if(shot[s].bullet[n].spd>r){
            //保存上一帧的位置
            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;//敌机 BIU 的音效
        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;
                }
            }
        }
    }
}

//自机子弹和敌机的处理
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;//根据子弹的威力扣掉敌机相应的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;//清除这个子弹

                        /*决死的炸弹释放,在这里处理*/

                        if(ch.flag==0 && ch.mutekicnt==0){//如果在通常状态,没有处于无敌的情况下
                            ch.flag    =2;    //1:检查决死的炸弹 2:死亡动画浮上来
                            ch.cnt    =0;
                            se_flag[3]=1;//BIU 的音效
                            return;
                        }
                    }
                }
            }
        }
    }
}

//碰撞判定的主函数
void out_main(){
    cshot_and_enemy();
    enemyshot_and_ch();
}
______________________________________________________________________________________________________

enemyshot_and_ch 是敌机的子弹和自机的碰撞判定处理函数,这里出现了一个ch.flag=2的状态呢。

被击中后,玩家会进入两个阶段的状态。

首先,被击中的瞬间,会进入“可以使用炸弹决死”的状态。 这个状态, 用 ch.flag = 1 来表示。

一定时间内没有按下炸弹按键的话,接下来就进入从画面底端往上浮的这个状态。

这个状态,用 ch.flag = 2 表示。

在画面中上浮的这个状态中,如果有按键输入的话,就会进入最初的状态。

另外,角色死后的一段时间,是处于无敌状态的。

是否处于无敌状态,由另一个变量表示。

ch.mutekicnt不等于0的时候处于无敌状态。这个时候不进行被子弹打中的处理。

另外,在 graph函数里绘制时,每两帧只绘制一帧,

这样就实现一闪一闪的效果,感觉就像无敌了一样。

无敌状态经过一段时间就会消失。

那么就让我们来把这些处理和改动来实现一下吧。

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

---- 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;//角色向上移动
                //一秒钟以上,或者上移一定距离,或者有键按下的话
                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;//重置为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);
}
——————————————————————————————————————————————————————

运行结果:

http://dixq.net/rp/swf/21.swf


注: 由于制作时的帧率问题,以上的动画中没有很好的表现出角色的闪烁效果。

这个动画和实际运行的效果是不一样的。

请自己将代码编译运行,确认实际的运行效果。



(PS:上一篇文章翻译都是一年前的了,这半年多没更新了,一个字:懒。

要说工作忙也算不上,只是伤心了。项目忙了一段时间,然后测试了一段时间,然后,结果还是不理想,后面干脆砍掉重做。

唉,一直不抱希望,对这做出来效果烂的不行的项目。。新的一年,工资也没怎么涨,就这么混着耗下去。。。。唉。。。)



本人CSDN博客目录:

http://blog.csdn.net/tidus5


展开阅读全文
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值