追逐算法之--牛鞭的子弹是怎样练成的(3)--简单碰撞检测

           对于雷电这样的游戏,偶是从来都不碰的,我曾经以每10秒一个币的速度,在当地所有的地痞,流氓,富二代的面前,将雷电拖到第5关,通常我投完币后,就会放一个大招,然后用我自己的身躯,堵住敌人所有的子弹,接着重复上面的操作,我的反应极为灵敏,就算屏幕上只有一颗远离我的子弹,我也会主动碰上去。每当我堆币的时候,身后的围观群众都会羡慕的说“这家伙真是个2b”。所以我还是比较喜欢打那些,就算没人操作也能存活两分钟以上的游戏,比如三国志什么的。。

 

      随着学习的深入,偶教程中的蒸馏水将会越来越少,货也会越来越干,请大家自备饮料解渴。

 

      上一篇教程中,我们已经实现了自己飞机的发射,和敌人飞机的追踪效果,但是我们的子弹打不中敌机,始终让人感觉不爽,今天我们来给子弹加上碰撞检测,让敌机不再得瑟!

           

            首先我们给敌机和子弹加上一个destroy()销毁函数,当他们超出屏幕范围的时候,就销毁他们释放资源,否则这些子弹会一直留在游戏中,随着时间的推移,游戏就会越来越卡,当然我们的飞机就不需要加上销毁函数了,开什么玩笑?我自己做的游戏还能被敌人击中?我的游戏我做主!

 

           首先在Bullet类中加入这两个函数

	//检测子弹是不是应该被销毁
	private void checkDead(){
		boolean outHeight= this.y>GameHandler.FRAME_HEIGHT||this.y<0;
		//如果高度越界就销毁
		if(outHeight)destroy();
	}
	
	//销毁自己,释放资源
	public void destroy(){
		//在集合类中移除自己,这样就没有任何对象有对此实例的引用了
		//jvm的gc会帮我们自动回收实例,并且释放资源
		GameHandler.bulletList.remove(this);
		//让图像到窗口外面去
		this.y =1000;
	}

         

   同理在Enemy类中也一样

 //检测是不是应该被销毁
 private void checkDead(){
  boolean outHeight= this.y>GameHandler.FRAME_HEIGHT;
  //如果高度越界就销毁
  if(outHeight)destroy();
 }
 
 //销毁自己,释放资源
 public void destroy(){
  //在集合类中移除自己,这样就没有任何对象有对此实例的引用了
  //jvm的gc会帮我们自动回收实例,并且释放资源
  GameHandler.enemyList.remove(this);
  //让图像到窗口外面去
  this.y =1000;
 }

    

然后我们分别在他们的update逻辑循环中加入checkDead()函数

	public void update(){
		//每帧追踪敌机
		this.trackMyPanel();
		this.checkDead();
		this.fire();
	}

    

好,下面我们开始搞碰撞检测,碰撞检测其实是游戏AI里面很重要的一块,后面我们会单独拿出一期,详细的讲,这里暂时只是先介绍一个最简单的碰撞检测,敌机和子弹的碰撞检测,我们可以抽象的看成是判断两个矩形有没有相交,因为之前我们画圆,也是用矩形的内切圆来画的,所以这里我们首先写一个判断两个矩形是否相交的函数,我们把这类需要计算的函数,单独放到一个Calculate类中,以放便各个类的调用,和以后的复用。

package planet;

public class Calculate {
	/**
	 *  判断两个矩形是否相交
	 *  参数为两个矩形的左上角坐标和其长宽
	 * @return 返回true表示碰撞成功了
	 */
	public static boolean isRectCollision(int x1,int y1,int width1,int height1,
			int x2,int y2,int width2,int height2){
		return x2 + width2 > x1 && x2 < x1 + width1
		&& y2 + height2 > y1 && y2 < y1 + height1;		
	}
}

     上面这个就是判断矩形是否相交的函数了,原理很简单,大家可以自己画个小图理解一下,不理解也没关系,你可以直接拿来用!

      下面我们需要在子弹类中加入与敌机的碰撞检测!由于需要用到,子弹的长和宽以及敌机的长和宽的数值,我们把他们提取出来写成这样的静态常量:  

       public static final int ENEMY_WIDTH =50,ENEMY_HEIGHT =50;

我们每帧循环检测一颗子弹与所有敌机的是否碰撞,如果碰撞了就把自己这颗子弹和被碰撞的敌机都给销毁,既然是每一帧都要检测的函数,当然照例还是要放进update()里面的,这里我就不写了。

//遍历检测与敌机的碰撞
	private void checkEnemyCollision(){
		//检测这颗子弹与每一辆敌机是否碰撞
		for (int i =0;i<GameHandler.enemyList.size();i++){
			boolean result =this.judgeEnemyCollision(GameHandler.enemyList.get(i));
			//如果有碰撞则跳出循环
			if(result)break;
		}
	}
	//判断这颗子弹与某一敌机是否碰撞
	private boolean judgeEnemyCollision(Enemy enemy){
		if (Calculate.isRectCollision((int)this.x,(int)this.y, this.rdius, this.rdius,
				(int)enemy.x, (int)enemy.y, enemy.ENEMY_WIDTH, enemy.ENEMY_HEIGHT)){
			//如果碰撞的话就销毁自己和敌机
			this.destroy();
			enemy.destroy();
			return true;
		}
		return false;
	}

        好,现在大家可以运行一下程序测试一下,我们已经可以一枪一个的很轻松的戏谑敌机了! 只是,这敌机,只能傻乎乎的往我们的枪口上撞,似乎有欺负人之嫌,恩,说的对,我们也要让敌机可以打子弹,这样吧,我们让敌机每隔一秒钟朝我们打一颗子弹。

      首先我们要先在敌机类中加入一个1s的计时器

        //这里设置一秒钟产生1颗子弹
 private Timer fireTimer =new Timer(GameHandler.FPS);
 然后将MyPlanet中的fire函数复制过去,照例将fire()函数加入update()中

	public void fire(){
		//产生一颗子弹,位置就在敌人飞机的正前方
		if(this.fireTimer.act())
			//这里的+20和+55用来调整子弹的初始位置,让它从飞机的正前方打出来
			GameHandler.bulletList.add(new Bullet(this.x+20,this.y+55));
	}

之后运行,你就可以看到一个很搞笑的现象,就是敌机被自己打出来的子弹打死了,而且子弹是往上面飞的,这是由于我们的子弹现在还没有办法区分敌我,现在还是按之前,我们飞机打出子弹来设定的,因此我们这里给Bullet类的构造函数增加一个新的三个值的构造函数,增加一个isEnemy属性,用来判断是不是敌人飞机打出来的子弹

	//判断子弹是由谁打出来的,默认是主角打出来的
	public boolean isEnemy =false;
	//子弹的长和宽
	public Bullet(double x,double y){
		this.x =x;
		this.y = y;
		//将此类加入GameHandler的子弹集合
		GameHandler.bulletList.add(this);
	}
	
	public Bullet(double x,double y,boolean isEnemy){
		this(x, y);
		//判断子弹是谁发出来的
		this.isEnemy=isEnemy;
	}

然后我们在上面的fire()函数中调用新造的构造函数

//这里的+20和+55用来调整子弹的初始位置,让它从飞机的正前方打出来

GameHandler.bulletList.add(new Bullet(this.x+20,this.y+55,true));
然后我们要在Bullet类中的碰撞检测函数中,判断当前子弹是不是敌机打出来的,如果是敌机打的就不进行碰撞检测了

//遍历检测与敌机的碰撞
	private void checkEnemyCollision(){
		//如果这颗子弹是敌机打出来的,那么不进行与敌机的碰撞检测
		if(this.isEnemy)return;
		//检测这颗子弹与每一辆敌机是否碰撞
		for (int i =0;i<GameHandler.enemyList.size();i++){
			boolean result =this.judgeEnemyCollision(GameHandler.enemyList.get(i));
			//如果有碰撞则跳出循环
			if(result)break;
		}
	}

 好,这时运行,敌机已经可以向我们打子弹了!shit,敌人的子弹竟然和我的一样快,这怎么可以!给他们的子弹减速!

	public void update(){
		this.checkDead();
		this.checkEnemyCollision();
		if (!this.isEnemy)
			//如果这颗子弹是我打出来的就让它,按正常速度往上飞
			this.y-=speed;
		else
			//如果是敌机打的,就让它半速往下降
			this.y+=speed/2;
	}

恩,现在运行起来,感觉好多了,不但能戏谑敌机,还不怕别人说闲话了!有的码农朋友要问了,我们是不是也要同理,把敌人子弹和我方飞机的碰撞检测也做一下啊?不然像现在这样,不管敌人的子弹有多少始终都打不中我们。。。

 对于提出这样疑问的码农朋友,我只能语重心长的说一句,让你二大爷放学回家的时候小心一点!。。。
    

      ok今天的主要功能已经完成了,但是还是有一点小小的瑕疵,敌人一碰到子弹就挂了,打击感比较欠缺,而且直接消失也比较突兀,而且敌我都一样,子弹也一样,不好区分,下面我们一一解决这些问题,首先我们给敌机加上生命值,只有碰撞的子弹达到一定数量的时候我们才让敌机消失。

         首先我们给Enemy类加入一个生命值属性 public int life=3;

         然后我们在Enemy中加入一个  hited函数

	public void hited(){
		//如果没有血了,就开始爆炸动画
		if(--this.life<0)this.destroy();
	}

然后我们修改Bullet类中的碰撞检测函数,让他碰撞的时候,改为调用hited函数。

 这样我们再运行一下,这时候我们就需要打三下,才能消灭一架敌机了,接着我们为敌机用画圆的方式,模拟一下被击中爆炸的效果

 我们为Enemy加入几个状态属性,用来模拟一个从小到大的圆

 //是否爆炸状态,是否爆炸结束状态
 public boolean  isExplode=false,isExplodeDead =false;
 //爆炸半径,最大爆炸半径
 private int explodeRadius=0,explodeMaxRadius=125;
       在draw函数中我们这样写

public void draw(Graphics g){
		g.setColor(Color.ORANGE);
		//如果爆炸状态开启,就开始画爆炸的效果
		if(this.isExplode){
			//画一个圆圈表示爆炸效果
			g.drawOval((int)x-40,(int)y-40, explodeRadius, explodeRadius);
			//每一帧让圆的半径增大25			                                   
                            this.explodeRadius+=25;
			if(this.explodeRadius>this.explodeMaxRadius)this.isExplodeDead=true;
			return;
		}
		//画一个矩形表示敌机
		g.fillRect((int)this.x, (int)this.y, ENEMY_WIDTH, ENEMY_HEIGHT);
	}

       

 相应修改hited函数和destroy()函数,让他们在爆炸效果播放完之后再销毁

 

	//检测是不是应该被销毁
	private void checkDead(){
		boolean outHeight= this.y>GameHandler.FRAME_HEIGHT;
		//如果高度越界就销毁
		if(outHeight)destroy();
		//如果爆炸效果结束了,那么也销毁
		if(this.isExplodeDead)this.destroy();
	}
	
	public void hited(){
		//如果没有血了,就开始爆炸动画
		if(--this.life<0)this.isExplode=true;
	}

      

  ok最后我们修改一下双方敌机和子弹的颜色,修改颜色的语法是这样的 g.setColor(Color.ORANGE);

         好了现在我们的游戏看起来已经像模像样了!

         

      游戏源码:http://download.csdn.net/detail/azhangzhengtong/5223888

 

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值