程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> J2ME 2D小游戲入門之旅(四) 加入子彈群,實現碰撞運算

J2ME 2D小游戲入門之旅(四) 加入子彈群,實現碰撞運算

編輯:關於JAVA

飛機類游戲中子彈是必不可少的,他們數量很多且充斥著整個屏幕,這些隨機或者有著一定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]);
}
/* no use now
public void resetDeadBullet(){
for (int i = 0; i < bullets.length; i++) {
if(bullets[i][5]==0){//dead bullet
initBullet(i);
}
}
}
*/
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時,將不在是想象著子彈,飛機什麼的,你要思考一個系統。

總結一下子彈類的公共接口:

n Bullets(Image img,int picwidth,int picheight,int bulletstotal,int width,int height)構造函數

n public void initBullets()初始化子彈數組

n public void paint(Graphics g) paint子彈數組

n public void refreshBullets(Sprite planesprite, boolean needcollision)更新子彈數組狀態,碰撞檢測、處理等邏輯工作的綜合

n public void killbullets(Sprite planesprite,int range)//稍後解釋

到此為止,我們的游戲已經初具規模了,下一步是加入效果類,嘿嘿有點意思了…

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved