您的位置:首页 > 其它

追逐算法之--牛鞭的子弹是怎样练成的(5)--牛鞭终养成

2013-04-10 01:41 495 查看
从3号开博到现在已经整整一个星期了,偶博客的访问量只有600多人,这得到什么时候才能达到100w啊。。。。我在这里郑重起誓,等我的博客访问量达到100w的时候,我就现场直播,剖腹,挖眼,割耳,掏心,想看杀人的朋友,走过路过千万不要错过,速度来顶吧!



看到没,这就是传说中的牛鞭子弹,我第一眼看到这张图就知道,要实现这个效果绝非易事,果然。在我仔细观察了3秒钟之后,我揭开了其中的秘密,现在请各位观众和我一起来到今天的《走进科学》,我将一一为大家揭开谜底。

别急,现在我们的敌机射出来的子弹不管任何时候都是垂直向下的,这样看起来非常的傻,敌机的子弹应该朝着我的飞机打来才对,因此我们首先来实现一下,敌机将子弹射向我们的效果。

原理很简单,我们先要在子弹类中加入targetV向量,代表子弹运动的目标方向,如图,我们将子弹的targetV设成(X2-X1,Y2-Y1)即可。



为此我们在TypeConst中加入一种新的子弹类型: //敌方跟踪子弹 int ENEMY_BULLET_TRACK_1 =4; 然后我们在Bullet中添加一个targetV属性

//目标向量

public PVector targetV = new PVector(0,0); 接着我们就可以实现这种子弹类型的逻辑函数和绘图函数了

case TypeConst.ENEMY_BULLET_TRACK_1:
this.setSpeed(3);
initV=targetV;
break;
case TypeConst.ENEMY_BULLET_TRACK_1:
g.setColor(Color.YELLOW);
g2d.rotate(this.rotateAngle,this.x, this.y);
g.fillOval((int)this.x,(int)this.y, rdiusW, rdiusH);
g2d.rotate(-this.rotateAngle,this.x, this.y);
break;

然后我们将enemy的fire函数做如下修改

public void fire(){
//产生一颗子弹,位置就在敌人飞机的正前方
if(this.fireTimer.act()) {
//这里的+20和+55用来调整子弹的初始位置,让它从飞机的正前方打出来
Bullet temp=new Bullet(this.x+20,this.y+55,true,TypeConst.ENEMY_BULLET_TRACK_1);
temp.setTargetV(new PVector(GameHandler.myPlane.getX()-temp.x,GameHandler.myPlane.getY()-temp.y).normalize());
}
}

这样我们再运行一下就可以看见,敌人的子弹是向着我们飞来了。运行一下试试吧!

下面我们就要来实现牛鞭子弹了,它的原理是怎样的呢,其实很简单,它的那条长长的鞭子其实是由一个一个的小方块组成的,就如同我们这里的小长方形,通过粒子特效,与图片效果,将子弹的射速加快之后,看起来就是一条长长的鞭子了,那么它是如何弯曲的呢?其实就是让这些小长方形每一帧都做上面追踪子弹追踪过程,这样看起来就如果一根长长的鞭子了,我们让每一颗小子弹用初始速度向量,加上目标向量,以达到逐渐改变方向的目的。


如图,根据向量的加法,子弹的initV向量加上子弹的targetV向量应该等于子弹的实际运动方向moveV,我们再对moveV归一化,然后将归一化后的向量的各自分量乘以子弹的速度speed,就可以得到实际移动的距离了,如果每一帧都做这样的移动,那么就会实现类似跟踪导弹的效果了,这在追逐算法里的专业术语叫做视线追逐。接下来我们就来尝试实现以下

首先我们为我方飞机加入一种新的子弹类型TypeConst.MY_BULLET_VECTOR_TRACK ,然后我们照例实现它的逻辑与绘图函数

case TypeConst.MY_BULLET_VECTOR_TRACK:
this.setSpeed(25);
//取得当前子弹指向敌机的向量 即目标向量
PVector tp = new PVector((targetV.x-this.x),(targetV.y-this.y));
//让初始向量的模向量,加上目标向量的模向量(此处我们同化初始向量和目标向量的比例差异)
initV=initV.normalize().add(tp.normalize());
break;
case TypeConst.MY_BULLET_VECTOR_TRACK:
g.setColor(Color.blue);
g2d.rotate(this.rotateAngle,this.x, this.y);
this.setRdius(3, 15);
g2d.drawRect((int)this.x,(int)this.y, rdiusW, rdiusH);
g2d.rotate(-this.rotateAngle,this.x, this.y);
break;

这时候我们产生了问题,如果屏幕上的敌机有很多架,那么我们的子弹追逐哪一架呢?这里我们寻找到离我们最近的敌机进行追逐,我们在Bullet类中加入一个寻找离自己最近敌机的函数

//得到离自己最近的敌机的坐标,并返回为向量
public PVector getNestEnemy(){
int result =0;
int index =-1;
//循环遍历敌人类,寻找y轴坐标最大的飞机
for (int i=0;i<GameHandler.enemyList.size();i++){
int yPos =(int) GameHandler.enemyList.get(i).y;
if (yPos>result){
result =yPos;
index = i;
}
}
//如果屏幕上没有飞机,就让子弹垂直往上射
if (index ==-1)return new PVector(this.x+20,-1);
//如果有最近的敌机,返回最近敌机的中间偏下的坐标
Enemy nestEnemy = GameHandler.enemyList.get(index);
return new PVector(nestEnemy.x+nestEnemy.ENEMY_WIDTH/2,nestEnemy.y+nestEnemy.ENEMY_HEIGHT);
}

有了上面的函数,我们就可以很轻松的找到离我们最近敌机的位置向量了。

我们将这个函数加入到TypeConst.MY_BULLET_VECTOR_TRACK 的逻辑更新函数中

case TypeConst.MY_BULLET_VECTOR_TRACK:
this.setSpeed(25);
//得到离自己最近的敌机的向量位置
this.targetV=this.getNestEnemy();
//取得当前子弹指向敌机的向量 即目标向量
PVector tp = new PVector((targetV.x-this.x),(targetV.y-this.y));
//让初始向量加上目标向量
initV=initV.add(tp).normalize();
break;

现在 ,我们在TypeConst中为我方飞机,加入一种新的射击方式

//发射多颗向量追踪子弹(牛鞭子弹)

int MY_SHOT_VECTOR_TRACK_1=3;

然后我们在MyPlanet类里的fire函数中,添加这个射击方式的功能如下,我们同时发出9颗子弹,并设置他们的初始方向向量为垂直向上,这里我们就不设置飞机的开火CD了,即为每帧发出下面这一批子弹。

//产生牛鞭子弹
case TypeConst.MY_SHOT_VECTOR_TRACK_1:
new Bullet(this.x+5,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(0,-1));
new Bullet(this.x+10,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(0,-1));
new Bullet(this.x+15,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(0,-1));
new Bullet(this.x+20,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(0,-1));
new Bullet(this.x+25,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(0,-1));
new Bullet(this.x+30,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(0,-1));
new Bullet(this.x+35,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(0,-1));
new Bullet(this.x+40,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(0,-1));
new Bullet(this.x+45,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(0,-1));
break;


好各位注意了,下面就是见证奇迹的时刻,如果你现在运行这个程序,发现子弹已经变成了牛鞭,我要的不多,每人十个回帖!

ok!!大功告成,只是还有些不爽的地方,就是子弹的打击感好像不咋的,我们给击中敌机的子弹按照Enemy的方式,做一个小的死亡爆炸效果,具体做法请参照前文,时间实在是太晚了,不能多写了!

public void draw(Graphics g){
//将Graphics 转成Graphics2D,这样就有旋转的功能了
g2d = (Graphics2D)g;
if(this.isExplode){
g.setColor(Color.pink);
//画一个圆圈表示爆炸效果
g.drawOval((int)x-10,(int)y-10, explodeRadius, explodeRadius);
//每一帧让圆的半径增大15
this.explodeRadius+=10;
if(this.explodeRadius>this.explodeMaxRadius)this.isExplodeDead=true;
return;
}else{
this.drawBulletType(g);
}
}

public void update(){
this.checkDead();
this.checkEnemyCollision();
this.updateBulletType();
//将x,y坐标,分别加上初始方向模向量的x,y分量*速度值
this.x = this.x+initV.x*speed;
this.y = this.y+initV.y*speed;
//区分左偏垂直线,还是右偏
if (initV.x<0)
this.rotateAngle=-new PVector(0,-1).checkVectorAngle(initV);
else
this.rotateAngle=new PVector(0,-1).checkVectorAngle(initV);
}




我们尝试修改一下跟踪子弹的初始方向向量,试试效果会如何。在MyPlanet中增加一个新的射击类型

//产生牛鞭子弹2号
case TypeConst.MY_SHOT_VECTOR_TRACK_2:
new Bullet(this.x+10,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(-5,1));
new Bullet(this.x+20,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(0,-1));
new Bullet(this.x+30,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(5,1));
break;




我们在这个基础上增加散射效果试试!

//散射加牛鞭1号
case TypeConst.MY_SHOT_VECTOR_PLUS_MULITI_TRACK_1:
new Bullet(this.x+10,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(-5,1));
new Bullet(this.x+20,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(0,-1));
new Bullet(this.x+30,this.y-5,false,TypeConst.MY_BULLET_VECTOR_TRACK).setInitV(new PVector(5,1));
if(this.fireCDTimer.act())
this.makeMulitiBullet(6, 10);
break;




源码地址 :http://download.csdn.net/detail/azhangzhengtong/5240817
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: