第十八章 为自机的射击添上碰撞判定吧
这一章中将要介绍碰撞判定。
如果是做过STG的人的话,大概马上就会说“啊啊,使用勾股定理的话不是很容易吗”,不过在高速射击的的情况下单单这样是不够的。
如果您还不明白碰撞判定中利用勾股定理进行判断的方法的话,请在游戏编程馆s11章中进行确认。 链接。
假如碰撞判定的有效判定为10,那么如果子弹的速度是100的话会怎么样?
子弹100、100地前进,而由于碰撞判定是10、10地进行,子弹高速移动的情况下,就会出现没有被检查到的区域。
因此,有可能会出现敌人擦着自机子弹过去的情况。
为了不出现这种情况,必须一点一点地检查自机射击的子弹所经过的地方,不能漏掉一个地方。
因此,虽然有点麻烦,我们还是像下面那样实现吧。
在out_judge_cshot函数中对传入的第i号自机的射击数据和第s号敌人数据进行碰撞判定,
if(cshot[i].cnt>0){
也即是说,如果子弹至少计算过一次轨道的话,那么回到1帧之前的位置1次,然后反复以子弹和敌人的碰撞判定之合计那么多的量向当前的位置接近,这样,每次都检查一下是否已经碰撞。
下面是上述的实现。
—- out.cpp 变更 —-
#include "../include/GV.h"
#define ENEMY_RANGE_MAX 4
#define CSHOT_RANGE_MAX 2
//敌人的碰撞判定范围
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中溢出");
//敌人的碰撞判定和自机射击的子弹的碰撞判定的合计范围
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;
}
//决定敌人是否死掉
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;//敌人击毁的声音(原文是敵のピチュり音,至于这个梗……直接谷歌“ピチュる とは”就清楚了。)
for(i=0;i<SHOT_MAX;i++){//敌人总数次循环
if(shot[i].flag!=0){//如果有没有登录的弹幕
if(s==shot[i].num){//且敌人有已经登录了的弹幕
shot[i].flag=2;//改变flag以表示弹幕不再继续
break;
}
}
}
}
}
//碰撞判定main
void out_main(){
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减少子弹的power的量那么多
enemy_death_judge(s);//决定敌人是否死掉
}
}
}
}
}
}
在out_main中,进行所有的自机射击的子弹和所有敌人的总判定。 因此与此同时,我们和之前一样进行以下变更。
—-在 main.cpp 中进行以下变更—-
case 100://通常处理
calc_ch(); //角色计算
ch_move(); //控制角色移动
cshot_main();//自机射击main
enemy_main();//敌人处理main
shot_main(); //射击main
out_main(); //碰撞计算
graph_main();//绘制main
stage_count++;
break;
—-在 function.h 中进行以下追加 —-
//out.cpp
GLOBAL void out_main();
—-在 load.cpp 的 load函数中进行以下追加 —-
sound_se[1]=LoadSoundMem("dat/se/enemy_death.wav");
sound_se[2]=LoadSoundMem("dat/se/cshot.wav");
sound_se[8]=LoadSoundMem("dat/se/hit.wav");
ChangeVolumeSoundMem( 50, sound_se[0] ) ;//设定各素材的重播音量
ChangeVolumeSoundMem(128, sound_se[1] ) ;
ChangeVolumeSoundMem(128, sound_se[2] ) ;
ChangeVolumeSoundMem( 80, sound_se[8] ) ;
—-在 music.cpp 的 music_play函数进行以下变更 —-
void music_play(){
int i;
for(i=0;i<SE_MAX;i++){
if(se_flag[i]==1){
if(CheckSoundMem(sound_se[i])!=0){
if(i==8)continue;
StopSoundMem(sound_se[i]);
}
PlaySoundMem(sound_se[i],DX_PLAYTYPE_BACK);
}
}
}
至于为何要在最后追加的music_play函数中写上
if(i==8)continue;
那是因为第8号效果音是击中敌人时候的声音,如果它处于播放中,让它停止然后再重播的话,就会听到断断续续不好听的声音。
也就是说,必须要在它重播结束之后在重播才行。
现在的情况下有这种必要的只有第8号,因此就先这样了。
Excel数据也要适当地变更。
运行结果