第二十一章 为自机加上碰撞判定和无敌处理吧
之前我们已经为敌人增加了碰撞判定,现在我们也为自机加上碰撞判定吧。
首先,在进入具体的处理之前,我们进行一些预处理。
如果要对子弹进行判定处理,我们必须要让子弹自身保有判定范围之类的信息。
因此,我们像下面那样做。
—-在 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);
}
运行结果