您的位置:首页 > 编程语言

【四圣龙神录的编程教室】第18章、给自机的子弹加上碰撞检测吧

2014-03-15 14:17 316 查看
原文地址:

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

这次的内容是碰撞检测相关的方面。

做过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 文件作适当的变更。

执行结果

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

原文地址:

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

这次的内容是碰撞检测相关的方面。

做过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 文件作适当的变更。

执行结果

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

本人CSDN博客目录:
http://blog.csdn.net/tidus5

擦,你妹的CSDN,要我输验证码,我输半天,半个字都没错,就是不让我过。就让我输一些很2 的算式,
你这验证码系统有问题是吧!!!



打了一局游戏,本来想发到其他坛子,对CSDN太失望了。回来一想,这是要我输个数字?
然后输入了个数字,竟然行了。。。



你妹的这验证码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: