J2ME 2D小游戏入门之加入子弹群

 

J2ME 2D 小游戏入门之加入子弹群
 飞机类游戏中子弹是必不可少的,他们数量很多且充斥着整个屏幕,这些随机或者有着一定 AI 的小物体,实现起来不是总那么容易,有时候你不得不考虑很多和效能有关的问题。我们之前定义了 GameObject ,很大程度上就是为了方便的重用 Sprite ,因为我们有很多的子弹,不可能没增加一个子弹都是一个 Sprite ,我需要共享同一个 Sprite 。我们通过继承 GameObject 来实现。

  下面分析一下这个子弹类:

  它将继承自 GameObject

  记录子弹的个数;

   一个子弹的状态数组,记录各个子弹的类型 type, 位置 x,y ,速度 vx,vy ,是否存活 alive 等等。
   
  初始化子弹

  一个绘制方法,将子弹画到屏幕上。

  一个碰撞检测方法。

  好了先这样吧,以下是我们子弹类的定义,注意这种思想 —— 重用 Sprite ,这很重要。(这里参考了 tony 的很多设计)
public class Bullets extends GameObject {

  private int[][] bullets;// 子弹状态数组
  private int bulletstotal;// 数组的 length
  private Random rnd;// 随机数
  public static final int BULLET_TYPE_LEFT=0;// 子弹初始化的位置类型
  public static final int BULLET_TYPE_RIGHT=1;// 分为上下左右四种
  public static final int BULLET_TYPE_TOP=2;
  public static final int BULLET_TYPE_BOTTOM=3;
  private int width,height;// 屏幕的高和宽,用于随机子弹位置
  public Bullets(Image img,int picwidth,int picheight,int bulletstotal,int width,int height) {
   super(img,picwidth,picheight);
   this.bulletstotal=bulletstotal;
   bullets=new int[bulletstotal][6];
   rnd=new Random();
   this.width=width;
   this.height=height;
  }

  public void initBullets(){// 初始化子弹状态数组
   for (int i = 0; i < bullets.length; i++) {
    initBullet(i);
   }
  }

  private void initBullet(int i) {// 初始化 index 号子弹
   bullets[i][0] = (rnd.nextInt() & 0x7fffffff) % 4; //type
   bullets[i][5] = 1; //alive 1 表示存活, 0 表示死去
   switch (bullets[i][0]) {
    case BULLET_TYPE_LEFT:
     bullets[i][1] = -5;
     bullets[i][2] = (rnd.nextInt() & 0x7fffffff) % height;
     bullets[i][3] = (rnd.nextInt() & 0x7fffffff) % 3 + 1; //vx
     bullets[i][4] = (rnd.nextInt()) % 3; //vy
     break;
    case BULLET_TYPE_RIGHT:
     bullets[i][1] = width + 5;
     bullets[i][2] = (rnd.nextInt() & 0x7fffffff) % height;
     bullets[i][3] = ( (rnd.nextInt() & 0x7fffffff) % 3 + 1) * -1; //vx
     bullets[i][4] = (rnd.nextInt()) % 3; //vy
     break;
    case BULLET_TYPE_TOP:
     bullets[i][1] = (rnd.nextInt() & 0x7fffffff) % width;
     bullets[i][2] = -5;
     bullets[i][3] = (rnd.nextInt()) % 3; //vx
     bullets[i][4] = (rnd.nextInt() & 0x7fffffff) % 3 + 1; //vy
     break;
    case BULLET_TYPE_BOTTOM:
     bullets[i][1] = (rnd.nextInt() & 0x7fffffff) % width;
     bullets[i][2] = height + 5;
     bullets[i][3] = (rnd.nextInt()) % 3; //vx
     bullets[i][4] = ( (rnd.nextInt() & 0x7fffffff) % 3 + 1) * -1; //vy
     break;
    }
   }
   public void updata(int i){// 根据速度更新 i 子弹下一桢的位置,碰壁反弹
    bullets[i][1]+=bullets[i][3];
    bullets[i][2]+=bullets[i][4];
    if(bullets[i][1]<-5 || bullets[i][1]>width+5){
     bullets[i][3]*=-1;
    }
    if(bullets[i][2]<-5 || bullets[i][2]>height+5){
     bullets[i][4]*=-1;
    }
   }
   private void paint(Graphics g,int i){// 绘画出第 i 个子弹
    updataspritepos(i);// 更新位置
    sprite.paint(g);// 绘画 Sprtie
   }
   public void paint(Graphics g) {// 绘画整个子弹组
    for (int i = 0; i < bullets.length; i++) {
     if(bullets[i][5]==0){// 死去的子弹不绘画
      continue;
     }
    sprite.setPosition(bullets[i][1],bullets[i][2]); // 更新位置
    sprite.paint(g);
   }
  }
  public void refreshBullets(Sprite planesprite, boolean needcollision){// 刷新字典数组的状态,并作碰撞处理
   for (int i = 0; i < bullets.length; i++) {
    if(bullets[i][5]==0){ // 死去的子弹不更新
     continue;
   }
   if(needcollision){// 如果需要碰撞检测
    if (isCollision(planesprite, i, 10)) {// 如果碰撞,进行处理
     //System.out.println("collision ");
     Navigate.mc.gameover = true;
     Navigate.mc.explosion.sprite.setPosition(bullets[i][1] - 16,bullets[i][2] - 16);
     bullets[i][5] = 0;// 杀死碰撞的子弹
     continue;
    }
   }
   updata(i);// 更新状态
  }
}

private boolean isCollision(Sprite sprite,int i,int range){
  // 判断是否碰撞
  //updataspritepos(i);
  //return sprite.collidesWith(this.sprite,true);
  boolean result=false;
  int planeXCenter=sprite.getX()+12;
  int planeYCenter=sprite.getY()+12;
  int bulletXCenter=bullets[i][1]+3;
  int bulletYCenter=bullets[i][2]+3;
  if(Math.abs(planeXCenter-bulletXCenter) < range){
   if (Math.abs(planeYCenter - bulletYCenter )< range) {
    result = true;
   }
  }
  return result;
}

private void updataspritepos(int i){//
sprite 更新到 i 字弹的位置

sprite.setPosition(bullets[i][1],bullets[i][2]);

}

public void killbullets(Sprite planesprite,int range){
杀死一定区域内的子弹

  for (int i = 0; i < bullets.length; i++) {
   if(bullets[i][5]!=0){//alive bullets
    if(isCollision(planesprite, i, range)){
     bullets[i][5]=0;
     initBullet(i);
    }
   }
  }
}

}

  子弹如何表示?

  首先我们用一个二维数组来记录子弹的信息:

   bullets[i][0] 表示子弹的类型,有上、下、左、右四种,分别表示子弹飞入屏幕前的四种位置;

   bullets[i][1] 表示子弹的 x 坐标;

   bullets[i][2] 表示子弹的 y 坐标

   bullets[i][3] 表示子弹的 x 方向速度;

   bullets[i][4] 表示子弹的 y 方向速度;

   bullets[i][5] 表示子弹的存活状态;

  子弹如何初始化?

  我们首先写了一个初始化单个子弹的方法,然后便利数组调用 initBullet (i) ;来更新整个状态数组。

  子弹如何绘制?

  我们首先写了一个绘制单个子弹的方法,然后便利数组调用 paint g,i );来绘制整个状态数组。

  子弹如何碰撞?

  有很多种方法,其中 sprite 本身就提供了边框碰撞检测和基于像素的碰撞检测。前者不太适合我们的游戏,我们的飞机是不规则物体,且飞行游戏对碰撞比较敏感;而后者的效率又得不到我们的信赖,所以我们是用一种半径检测,把飞机近似的看成圆,选取恰当的半径, Math.abs(planeXCenter-bulletXCenter) < range 则表明碰撞。

  碰撞看似简单,其实是很复杂的问题,值得庆幸的是,二维碰撞相比三维碰撞简单得多。一个小技巧是,宁可让膨胀检测半径变小也不要他变得大 —— 漏掉检测,总比误检测要好得多。

  子弹更新?

  我们利用 refreshBullets 进行更新,这是主要逻辑部分。这个方法负责便利数组检测碰撞,如果碰撞就将处于碰撞位置的子弹杀死,并作相应的处理,这里是结束游戏并爆炸飞机;否则更新子弹的位置。

  我们只是线性的遍历整个的数组,进行碰撞检测,之后是更新位置;但是这样做有一个前提,就是碰撞检测简单而且处理部分也很简单:在这个游戏中,碰撞检测只是子弹群和飞机的检测,碰撞检测在游戏结束后就不执行了(通过控制 boolean needcollision );而处理更是简单了一些 —— 直接结束了游戏。如果不是如此,比如处理后并不是简单的结束游戏,我们就不得不设计的复杂一些。可能就不是将碰撞简单的以飞机为中心了。我们需要设计好游戏事件,设计好碰撞系统。

  如果碰撞本身比较复杂,或者子弹数量,种类增加时,我们线性的遍历数组就不能总是对所有的子弹都检测,可能屏幕需要分区,不处于一个区域的单位不检测。

  总之当你想想你的 1934 时,将不在是想象着子弹,飞机什么的,你要思考一个系统。

  总结一下子弹类的公共接口:

   Bullets(Image img,int picwidth,int picheight,int bulletstotal,int width,int height) 构造函数

   public void initBullets() 初始化子弹数组

   public void paint(Graphics g) paint 子弹数组

   public void refreshBullets(Sprite planesprite, boolean needcollision) 更新子弹数组状态,碰撞检测、处理等逻辑工作的综合

   public void killbullets(Sprite planesprite,int range)// 稍后解释

  到此为止,我们的游戏已经初具规模了,下一步是加入效果类,嘿嘿有点意思了

(转载自天极网)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值