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

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

編輯:J2ME
飛機類游戲中子彈是必不可少的,他們數量很多且充斥著整個屏幕,這些隨機或者有著一定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