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



原文地址:

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太失望了。回来一想,这是要我输个数字?
然后输入了个数字,竟然行了。。。




你妹的这验证码
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值