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

        从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




 

 

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 20
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值