原文地址:
这次的内容是碰撞检测相关的方面。
做过STG的人的话,可能会说“用勾股定理就轻松搞定了啦”这样的观点。
但当子弹的射速比较快的时候,只用那个就可能不行了。
如果对和碰撞检测相关的用勾股定理判定的方法不太清楚的话,
到游戏编程馆里的第s11章去看一下先吧。 看这里
假设碰撞检测的有效范围是10,子弹的速度是100的话,会怎么样呢?
子弹每次会移动100的距离,但碰撞检测只能对那10 的范围进行检测,
也就是说,子弹在高速移动的话,会有检测不到的地方。
因此,有可能发生敌人直接穿过自机的子弹的情况。
为了避免这样的情况,要对自机子弹通过的地方,一点一点的进行检查,必须要保证没有遗漏的地方。
那么,啰嗦了这么多,我们就按如下步骤进行实现吧。
在out_judge_cshot 函数里,把传进来的 i 号的自机子弹, 和 s 号的敌机进行碰撞检测,
>if(cshot[i].cnt>0){
也就是说,一旦计算了子弹的轨道,要回到一帧以前的位置开始,
一点一点的让子弹飞向现在的位置,同时判断子弹和敌机是否发生碰撞,这样对每一部都进行检测才行。
实现过程如下。(睡觉先,2:39了)
————————————————————————————————————————————————————————————————————————
---- 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){
//保存下前一帧所在的位置
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){//敌机的血量小于0的话
enemy[s].flag=0;//让敌机消失
se_flag[1]=1;//敌机的 BIU 的音效
for(i=0;i<SHOT_MAX;i++){//遍历所有的弹幕
if(shot[i].flag!=0){//如果有注册了的弹幕数据
if(s==shot[i].num){//如果这个弹幕是这个敌机发出来的
shot[i].flag=2;//设置标记,让这个弹幕不再持续了
break;
}
}
}
}
}
//碰撞检测,主函数
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
enemy_death_judge(s);//判断敌机是否死亡
}
}
}
}
}
}
——————————————————————————————————————————————————————————————————————————
在out_main 函数里,对所有的自机子弹,和所有的敌机判断是否发生碰撞。
相应的,还要做以下的改动。
——————————————————————————————————————————————————————————————————————————
---- main.cpp 做如下改动 ----
case 100://通常处理
calc_ch(); //自机的处理
ch_move(); //自机的移动操作
cshot_main();//自机的射击主函数
enemy_main();//敌机的处理的主函数
shot_main(); //射击的主函数
out_main(); //碰撞检测
graph_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 文件作适当的变更。
执行结果
原文地址:
这次的内容是碰撞检测相关的方面。
做过STG的人的话,可能会说“用勾股定理就轻松搞定了啦”这样的观点。
但当子弹的射速比较快的时候,只用那个就可能不行了。
如果对和碰撞检测相关的用勾股定理判定的方法不太清楚的话,
到游戏编程馆里的第s11章去看一下先吧。 看这里
假设碰撞检测的有效范围是10,子弹的速度是100的话,会怎么样呢?
子弹每次会移动100的距离,但碰撞检测只能对那10 的范围进行检测,
也就是说,子弹在高速移动的话,会有检测不到的地方。
因此,有可能发生敌人直接穿过自机的子弹的情况。
为了避免这样的情况,要对自机子弹通过的地方,一点一点的进行检查,必须要保证没有遗漏的地方。
那么,啰嗦了这么多,我们就按如下步骤进行实现吧。
在out_judge_cshot 函数里,把传进来的 i 号的自机子弹, 和 s 号的敌机进行碰撞检测,
>if(cshot[i].cnt>0){
也就是说,一旦计算了子弹的轨道,要回到一帧以前的位置开始,
一点一点的让子弹飞向现在的位置,同时判断子弹和敌机是否发生碰撞,这样对每一部都进行检测才行。
实现过程如下。(睡觉先,2:39了)
————————————————————————————————————————————————————————————————————————
---- 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){
//保存下前一帧所在的位置
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){//敌机的血量小于0的话
enemy[s].flag=0;//让敌机消失
se_flag[1]=1;//敌机的 BIU 的音效
for(i=0;i<SHOT_MAX;i++){//遍历所有的弹幕
if(shot[i].flag!=0){//如果有注册了的弹幕数据
if(s==shot[i].num){//如果这个弹幕是这个敌机发出来的
shot[i].flag=2;//设置标记,让这个弹幕不再持续了
break;
}
}
}
}
}
//碰撞检测,主函数
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
enemy_death_judge(s);//判断敌机是否死亡
}
}
}
}
}
}
——————————————————————————————————————————————————————————————————————————
在out_main 函数里,对所有的自机子弹,和所有的敌机判断是否发生碰撞。
相应的,还要做以下的改动。
——————————————————————————————————————————————————————————————————————————
---- main.cpp 做如下改动 ----
case 100://通常处理
calc_ch(); //自机的处理
ch_move(); //自机的移动操作
cshot_main();//自机的射击主函数
enemy_main();//敌机的处理的主函数
shot_main(); //射击的主函数
out_main(); //碰撞检测
graph_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 文件作适当的变更。
执行结果