第二十五章 为Boss加上碰撞判定吧
现在我们为Boss加上碰撞判定,同时也为Boss的子弹加上碰撞判定吧。
同时在本章中我们也加上在前面一直忘记的角色的装饰。
接下来,前面我们已经用FILED_MAX_X来定义区域的宽度,不过写起来很麻烦(汗)
FIELD_MAX_X → FMX
FIELD_MAX_Y → FMY
FIELD_X → FX
FIELD_Y → FY
然后我们做了以上变更,在这里先事先说明一下。
接下来,我们说说碰撞判定的话题。
在前面的章节中,我们写了碰撞判定的函数。在那个时候我们说过我们后面来泛用化地写函数。
请和前面的函数对比起来看。
进行以下变更。
//射击的坐标:1 进行碰撞判定的物体:2
int out_judge(double x1, double y1, double x2, double y2,
double range1, double range2, double spd1,double angle1){
int j;
double x=x1-x2;//敌人和自机射击的子弹的距离
double y=y1-y2;
//敌人的碰撞判定和自机双色机的碰撞判定的合计范围
double r=range1+range2;
//如果有必要计算中间部分
if(spd1>r){
//保存1帧以前的位置
double pre_x=x1+cos(angle1+PI)*spd1;
double pre_y=y1+sin(angle1+PI)*spd1;
double px,py;
for(j=0;j<spd1/r;j++){//前进部分÷碰撞判定部分次循环
px=pre_x-x2;
py=pre_y-y2;
if(px*px+py*py<r*r)
return 1;
pre_x+=cos(angle1)*r;
pre_y+=sin(angle1)*r;
}
}
if(x*x+y*y<r*r)//如果在碰撞判定内的话
return 1;//碰撞
return 0;
}
直到上个章节为止,我们要为每个变量都写一次函数,这次我们使这个函数能对任何变量都进行计算。
只需要变更要处理的参数的形式,算法和处理的内容完全没有变化。
这是射击的子弹和物体是否碰撞的判定函数,
x1,y1等有“1”的参数表示射击的信息 x2,y2等有“2”的参数表示物体的信息。 根据这个,我们在out文件中进行替换。
—- out.cpp 变更 —-
#include "../include/GV.h"
#define ENEMY_RANGE_MAX 4
#define CSHOT_RANGE_MAX 2
#define CRANGE 2.0
#define BRANGE 40.0
//敌人的碰撞判定范围
int enemy_range[ENEMY_RANGE_MAX]={16,30,16,50};
//自机设计的碰撞判定范围
int cshot_range[CSHOT_RANGE_MAX]={6,};
//碰撞判定
//射击的坐标:1 进行碰撞判定的物体:2
int out_judge(double x1, double y1, double x2, double y2,
double range1, double range2, double spd1,double angle1){
int j;
double x=x1-x2;//敌人和自机射击的子弹的距离
double y=y1-y2;
//敌人的碰撞判定和自机设计的碰撞判定的合计范围
double r=range1+range2;
//如果有必要计算中间的话
if(spd1>r){
//记录1帧之前的位置
double pre_x=x1+cos(angle1+PI)*spd1;
double pre_y=y1+sin(angle1+PI)*spd1;
double px,py;
for(j=0;j<spd1/r;j++){//前进部分÷碰撞判定部分次循环
px=pre_x-x2;
py=pre_y-y2;
if(px*px+py*py<r*r)
return 1;
pre_x+=cos(angle1)*r;
pre_y+=sin(angle1)*r;
}
}
if(x*x+y*y<r*r)//如果在碰撞判定内
return 1;//碰撞
return 0;
}
//判断敌人和自机射击是否碰上
int out_judge_cshot(int i,int s){
if(cshot[i].cnt>0){ //射击的子弹的轨道如果至少计算过一次
if(out_judge(cshot[i].x,cshot[i].y,enemy[s].x,enemy[s].y,
cshot_range[cshot[i].knd],enemy_range[enemy[s].knd],
cshot[i].spd,cshot[i].angle)){
return 1;
}
}
return 0;
}
//判定Boss是否和自机射击碰撞
int out_judge_cshot_boss(int i){
if(cshot[i].cnt>0){//如果射击的子弹的轨道至少计算过一次
if(out_judge(cshot[i].x,cshot[i].y,boss.x,boss.y,
cshot_range[cshot[i].knd],BRANGE,cshot[i].spd,cshot[i].angle)){
return 1;
}
}
return 0;
}
//判定自机是否和敌人射击碰上
int out_judge_enemyshot(int s,int n){
if(shot[s].bullet[n].cnt>0){//如果射击的子弹的轨道至少计算过一次
if(out_judge(
shot[s].bullet[n].x,shot[s].bullet[n].y,ch.x,ch.y,
bullet_info[shot[s].bullet[n].knd].range,CRANGE,
shot[s].bullet[n].spd,shot[s].bullet[n].angle
)){
return 1;
}
}
return 0;
}
//是否自机是否和Boss射击碰上
int out_judge_bossshot(int n){
if(boss_shot.bullet[n].cnt>0){//射击的子弹的轨道至少计算过一次的话
if(out_judge(
boss_shot.bullet[n].x,boss_shot.bullet[n].y,ch.x,ch.y,
bullet_info[boss_shot.bullet[n].knd].range,CRANGE,
boss_shot.bullet[n].spd,boss_shot.bullet[n].angle
)){
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;//设置弹幕不再继续
break;
}
}
}
}
}
//让s号敌人承受power伤害
void hit_enemy(int s,int power){
enemy[s].hp-=power;//减少子弹所持有的power量的HP
enemy_death_judge(s);//决定敌人是否击毁
}
//让Boss承受power伤害
void hit_boss(int power){
boss.hp-=power;//让HP减少子弹所持有的power的量的
}
//自机射击和敌人的处理
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;//将自己子弹销毁
hit_enemy(s,cshot[i].power);
break;
}
}
}
//Boss出现且其不描绘flag为无效且在射击中
if(boss.flag==1 && boss.graph_flag==0 && boss.state==2){
if(out_judge_cshot_boss(i)){
cshot[i].flag=0;
hit_boss(cshot[i].power);
}
}
}
}
}
//敌人射击和自机的处理
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)==1){//且自机与子弹接触
shot[s].bullet[n].flag=0;//销毁子弹
if(ch.flag==0 && ch.mutekicnt==0){
ch.cnt=0;
ch.flag=1;
se_flag[3]=1;
}
}
}
}
}
}
//Boss射击
if(boss_shot.flag>0){//如果射击已经登录
for(n=0;n<BOSS_BULLET_MAX;n++){//子弹总数
if(boss_shot.bullet[n].flag==1){//如果子弹已经登录
if(out_judge_bossshot(n)==1){//如果子弹已经和自机接触
boss_shot.bullet[n].flag=0;//销毁子弹
if(ch.flag==0 && ch.mutekicnt==0){
ch.cnt=0;
ch.flag=1;
se_flag[3]=1;
}
}
}
}
}
}
//让敌人承受Boom的伤害
void cbom_and_enemy(){
int s;
if(bom.flag!=1)return;
for(s=0;s<ENEMY_MAX;s++){//敌人总数
if(enemy[s].flag>0)//如果敌人是存在的
hit_enemy(s,ch.power/20);//给予伤害
}
//Boss存在且其不绘制flag为无效且在射击中
if(boss.flag==1 && boss.graph_flag==0 && boss.state==2)
hit_boss(ch.power/20);//承受伤害
}
//碰撞判定main
void out_main(){
cbom_and_enemy();//使敌人承受Boom的伤害
cshot_and_enemy();//自机射击和敌人的处理
enemyshot_and_ch();//敌人射击和自机的处理
}
虽然代码有点长,不过基本上来看,Boss的处理只是放在和杂兵一样的处理里面,因此在构造完全相同。
其次,在龙神录中SC(转载者注:SpellCard,即符卡,弹幕集)的弹幕的时候还会变化背景呢。
在什么弹幕的时候有什么样子的背景,为了设定这一点我们为Boss增加back_knd这个变量。
首先,我们在ini中把所有的SC都准备好吧。
—-在 ini.cpp ini()中进行变更、追加 —-
boss.appear_count[0]=50;//中路Boss出现的时刻
for(int i=0;i<DANMAKU_MAX;i++){//各自弹幕的HP
boss.set_hp[i]=10000;
boss.hp_max=10000;
}
for(int i=0;i<DANMAKU_MAX;i++)//弹幕的各自背景种类
boss.back_knd[i]=1;
变量和结构体也事先定义。
—-在 struct.h 中追加 —-
//Boos的信息
typedef struct{
int flag,cnt,knd,wtime,state,endtime,hagoromo,graph_flag;
int hp,hp_max;
int appear_count[2],set_hp[DANMAKU_MAX],back_knd[DANMAKU_MAX];
double x,y,ang,spd;
phy_t phy;
}boss_t;
—-在 define.h 中进行以下变更 —-
//区域的尺寸
#define FMX 384
#define FMY 448
//区域 左上角坐标
#define FX 32
#define FY 16
—-在 GV.h 中追加 —-
GLOBAL int img_chetc[10]; //和角色相关的其它图像
GLOBAL int img_etc[50];//其它图像
这样就在etc中定义了和角色的装饰、Boss的HP表示相关的图像。
我们将它们读取出来然后绘制。
—-在 load.cpp 的load函数中追加 —-
img_chetc[0] = LoadGraph( "../dat/img/char/atari.png" );//碰撞判定
img_chetc[2] = LoadGraph( "../dat/img/char/ball.png" );//球
img_etc[1] = LoadGraph( "../dat/img/enemy/hp.png" );
img_etc[7] = LoadGraph( "../dat/img/enemy/hp_boss.png" );//Boos 的HP
—-在 graph.cpp中追加、变更 —-
//绘制Boss
void graph_boss(){
int i;
if(boss.flag==0)return;
DrawRotaGraphF(boss.x+FX+dn.x,boss.y+FY+dn.y,1.0f,0.0f,img_dot_riria[0],TRUE);
if(boss.hp_max==0){printfDx("graph_boss的0%\n");return;}
for(i=0;i<FMX*0.98*boss.hp/boss.hp_max;i++){
if(boss.back_knd[boss.knd]==1)
DrawGraph(3+FX+i+dn.x,2+FY+dn.y,img_etc[7],FALSE);
else
DrawGraph(3+FX+i+dn.x,2+FY+dn.y,img_etc[1],FALSE);
}
}
//自机绘制
void graph_ch(){
double sx,sy,ny=(sin(2.0*PI*(count%50)/50)*3),ang=2.0*PI*(count%120)/120;
if(CheckStatePad(configpad.slow)>0)//如果低速移动中
sx=15,sy=15+ny;//拉近
else
sx=30,sy=30+ny;//往普通的位置移动
DrawRotaGraphF( ch.x-sx+FX, ch.y+sy+FY, 1.0f, ang, img_chetc[2], TRUE );
DrawRotaGraphF( ch.x+sx+FX, ch.y+sy+FY, 1.0f, -ang, img_chetc[2], TRUE );
if(ch.mutekicnt%2==0){//无敌中的话亮灭
//自机显示
DrawRotaGraphF(ch.x+FX+dn.x,ch.y+FY+dn.y,1.0f,0.0f,img_ch[0][ch.img],TRUE);
if(CheckStatePad(configpad.slow)>0)//如果低速移动中的话显示出碰撞判定
DrawRotaGraphF( ch.x+FX, ch.y+FY, 1.0f, 2.0*PI*(count%120)/120, img_chetc[0], TRUE );
}
}
现在来绘制SC用的背景。
boos.knd的指示编号boos.back_knd在1的时候表示SC,这一章中我们全部置为1.
然后我们背景什么都不绘制,让它一片漆黑。
—- graph_back.cpp 变更 —-
#include "../include/GV.h"
void graph_back00(){//通常背景
SetDrawArea( 32 , 16 , 416 , 464 ) ;//设定绘制可能的区域
DrawGraph(FX,count%700+FY-700,img_back[0],FALSE);
DrawGraph(FX,count%700+FY ,img_back[0],FALSE);
SetDrawArea( 0, 0, 640, 480);//还原区域
}
void graph_back01(){//SC用背景
SetDrawArea( 32 , 16 , 416 , 464 ) ;//设置绘制可能的区域
//在这里绘制SC用的背景
SetDrawArea( 0, 0, 640, 480);//还原区域
}
void graph_back_main(){
//Boos为有效且在放SC
if(boss.flag==1 && boss.back_knd[boss.knd]==1)
graph_back01();
else//除此之外
graph_back00();
}
—- func.h 变更 —-
extern void boss_shot_bulletH000();
void (*boss_shot_bullet[DANMAKU_MAX])() =
{
boss_shot_bulletH000,
boss_shot_bulletH000,
boss_shot_bulletH000,
boss_shot_bulletH000,
boss_shot_bulletH000,
boss_shot_bulletH000,
boss_shot_bulletH000,
};
在func.h中写入弹幕情报。
现在我们只做了H000这个弹幕,后面在增加弹幕的时候,这个也会增加。
因此在这里加入的函数的顺序就是弹幕的顺序。
现在,在弹幕结束的时候从0号弹幕开始。
7次弹幕结束了的话,会发生指针错误请注意一下。
运行结果