概述
如果你正在開發一款賽車游戲,那么其中很重要的一個方面就是檢驗賽車什么時候沖出了跑道或是裝上了別的車。檢驗上述事件在使用精靈進行編程時是非常容易的事情。精靈提供了4個專門用于處理上述類似事件的方法:
boolean?collidesWith(Image?image,?int?x,?int?y,?boolean?pixelLevel)
boolean?collidesWith(Sprite?s,?boolean?pixelLevel)
boolean?collidesWith(TiledLayer?t,?boolean?pixelLevel)
void?defineCollisionRectangle(int?x,?int?y,?int?width,?int?height)
請注意你能檢驗的碰撞的類型有很大的靈活性。例如,你不但可以檢驗精靈之間的碰撞,還能檢驗精靈與圖像、平鋪的圖層之間的碰撞。
注意:平鋪圖層(Tiled Layer)是一個由一系列格子組成的可視化元素。這些格子能建立起一個大的卷軸背景而不需要一張大的圖畫。
碰撞檢驗
學習碰撞檢驗的最佳方法就是實際應用它。在這一章,你將創建你的第二個MIDlet。這個MIDlet將向你展示如何檢驗精靈之間的碰撞。
我們先實際看一看這個MIDlet。我們的目標是:在屏幕上移動小綠人而不碰到其他任何精靈,也就是說不能碰到蘋果、立方體、星星和中間的螺旋體。圖18展示了MIDlet運行時的3幅屏幕截圖,你可以看到小綠人在屏幕上移動但是卻沒碰到任何其他精靈。
(圖18)
圖19顯示了MIDlet如何相應碰撞。你會注意到,每次發生碰撞時,屏幕的背光會閃爍(屏幕邊緣出現淺綠色)
(圖19)
創建固定的精靈
第一步,我們先來創建固定不動的精靈(立方體、蘋果還有星星)。
在WTK中新建工程和精靈:
1、創建一個名為Collisions的工程;
2、把后面出現的所有代碼復制到文本編輯器中;
3、把這些源文件保存到WTK安裝目錄下的\apps\Collisions\src目錄下。
下面是代碼:
/**/
/*
--------------------------------------------------
?*?AppleSprite.java
?*-------------------------------------------------
*/
import
?javax.microedition.lcdui.game.
*
;
import
?javax.microedition.lcdui.
*
;

public
?
class
?AppleSprite?
extends
?Sprite?
{

????
public
?AppleSprite(Image?image)?
{
????????
//
?構造函數
????????
super
(image);
????????
//
?設置顯示在屏幕上的位置
????????setRefPixelPosition(
146
,?
35
);
????}
}
/**/
/*
--------------------------------------------------
?*?StarSprite.java
?*-------------------------------------------------
*/
import
?javax.microedition.lcdui.game.
*
;
import
?javax.microedition.lcdui.
*
;


public
?
class
?StarSprite?
extends
?Sprite?
{

????
public
?StarSprite(Image?image)?
{
????????
//
?構造函數
????????
super
(image);
????????
//
?設置顯示在屏幕上的位置
????????setRefPixelPosition(
5
,?
65
);
????}
}
/**/
/*
--------------------------------------------------
?*?CubeSprite.java
?*-------------------------------------------------
*/
import
?javax.microedition.lcdui.game.
*
;
import
?javax.microedition.lcdui.
*
;


public
?
class
?CubeSprite?
extends
?Sprite?
{

????
public
?CubeSprite(Image?image)?
{
????????
//
?構造函數
????????
super
(image);
????????
//
?設置顯示在屏幕上的位置
????????setRefPixelPosition(
120
,?
116
);
????}
}
注意精靈參考像素的位置決定了精靈在畫布上的位置。例如,立方體精靈被放置在橫坐標=120,縱坐標=
116處。

創建動畫精靈
上一章創建的動畫精靈會在這一章使用。
1
、把上一章的代碼復制到文本編輯器,
2
、將文件另存為AnimatedSprite.java,保存到WTK安裝目錄下的\apps\Collisions\src目錄
下面是代碼:
/**/
/*
--------------------------------------------------
?*?AnimatedSprite.java
?*-------------------------------------------------
*/
import
?javax.microedition.lcdui.game.
*
;
import
?javax.microedition.lcdui.
*
;


public
?
class
?AnimatedSprite?
extends
?Sprite?
{

????
public
?AnimatedSprite(Image?image,?
int
?frameWidth,?
int
?frameHeight)?
{
????????
//
?調用Sprite構造函數
????????
super
(image,?frameWidth,?frameHeight);
????}
}
創建移動精靈:代碼書寫
我們將控制移動一個綠色小人圖案的精靈。
1、將下面的代碼拷貝到文本編輯器。
2、將此源文件保存到WTK安裝目錄下的\apps\Collisions\src目錄
/**/
/*
--------------------------------------------------
?*?ManSprite.java
?*
?*?這個精靈可以通過調用moveLeft(),?moveRight()
?*?moveUp()?and?moveDown()方法在屏幕上移動
?*-------------------------------------------------
*/
import
?javax.microedition.lcdui.game.
*
;
import
?javax.microedition.lcdui.
*
;


public
?
class
?ManSprite?
extends
?Sprite?
{

????
private
?
int
?x?
=
?
0
,?y?
=
?
0
,?
//
?當前的x,y坐標
????????????previous_x,?previous_y;?
//
?是一個x,y坐標
????
private
?
static
?
final
?
int
?MAN_WIDTH?
=
?
25
;?
//
?精靈寬度
????
private
?
static
?
final
?
int
?MAN_HEIGHT?
=
?
25
;?
//
?精靈高度
????
public
?ManSprite(Image?image)?
{
????????
//
?調用Sprite的構造函數
????????
super
(image);
????}
????
public
?
void
?moveLeft()?
{
????????
//
?如果綠色的小人精靈沒有碰到左邊緣
????????
if
?(x?
>
?
0
)?
{
????????????saveXY();
????????????
//
?如果離左邊界小與3,則置0,
????????????
//
?否則從當前位置減3
????????????x?
=
?(x?
<
?
3
?
?
?
0
?:?x?
-
?
3
);
????????????setPosition(x,?y);
????????}
????}
????
public
?
void
?moveRight(
int
?w)?
{
????????
//
?如果綠色的小人精靈沒有碰到右邊緣
????????
if
?((x?
+
?MAN_WIDTH)?
<
?w)?
{
????????????saveXY();
????????????
//
?如果當前橫坐標加上精靈寬度超出了右邊界,
????????????
//
?將當前位置設為最右邊.?否則當前位置加3.
????????????x?
=
?((x?
+
?MAN_WIDTH?
>
?w)?
?
?(w?
-
?MAN_WIDTH)?:?x?
+
?
3
);
????????????setPosition(x,?y);
????????}
????}
????
public
?
void
?moveUp()?
{
????????
//
?如果綠色的小人精靈沒有碰到上邊緣
????????
if
?(y?
>
?
0
)?
{
????????????saveXY();
????????????
//
?如果離上邊界小于3,則置為0
????????????
//
?否則從當前位置減.
????????????y?
=
?(y?
<
?
3
?
?
?
0
?:?y?
-
?
3
);
????????????setPosition(x,?y);
????????}
????}
????
public
?
void
?moveDown(
int
?h)?
{
????????
//
?如果綠色的小人精靈沒有碰到下邊緣
????????
if
?((y?
+
?MAN_HEIGHT)?
<
?h)?
{
????????????saveXY();
????????????
//
?如果當前縱坐標加上精靈高度超出了下邊界,
????????????
//
?將當前位置設為最下邊.?否則當前位置加3.
????????????y?
=
?((y?
+
?MAN_WIDTH?
>
?h)?
?
?(h?
-
?MAN_WIDTH)?:?y?
+
?
3
);
????????????setPosition(x,?y);
????????}
????}
????
/**/
/*
--------------------------------------------------
?????*?保存x,y坐標用于碰撞檢驗
?????*-------------------------------------------------
*/
????
private
?
void
?saveXY()?
{
????????
//
?將當前坐標緩存到上一坐標
????????previous_x?
=
?x;
????????previous_y?
=
?y;
????}
????
/**/
/*
--------------------------------------------------
?????*?如果檢驗到碰撞發生,
?????*?則返回上一坐標
?????*-------------------------------------------------
*/
????
public
?
void
?restoreXY()?
{
????????x?
=
?previous_x;
????????y?
=
?previous_y;
????????setPosition(x,?y);
????}
}
讓我們回過頭快速回顧一下上一節代碼里的幾個關鍵點。
有4個方法控制精靈的運動。
傳遞給moveRight()?和 moveDown()方法的參數分別是畫布的寬度和高度。這兩個參數分別用來判斷小人精靈是否到達了屏幕的右邊界和下邊界。
1. moveLeft()
2. moveRight(int w)
3. moveUp()
4. moveDown(int h)
無論何時,你移動精靈,你首先要將精靈的當前位置保存起來(saveXY()),以便于精靈發生碰撞時使用。一旦碰撞真的發生,你就需要一種方法將精靈恢復到原來位置(restoreXY())
/**/
/*
--------------------------------------------------
?????*?保存x,y坐標用于碰撞檢驗
?????*-------------------------------------------------
*/
????
private
?
void
?saveXY()?
{
????????
//
?將當前坐標緩存到上一坐標
????????previous_x?
=
?x;
????????previous_y?
=
?y;
????}
/**/
/*
--------------------------------------------------
?????*?如果檢驗到碰撞發生,
?????*?則返回上一坐標
?????*-------------------------------------------------
*/
????
public
?
void
?restoreXY()?
{
????????x?
=
?previous_x;
????????y?
=
?previous_y;
????????setPosition(x,?y);
????}
上面兩個方法可以處理任何運動,不管什么方向,都遵循相同的邏輯。
根據精靈的當前位置檢驗邊緣碰撞。例如有一個向左移動的請求,確認當前坐標是否比0大,如果是,則對橫坐標進行相應的變換。下面是左移方法的代碼:
public
???
void
??moveLeft()???
{
?????????
//
??如果綠色的小人精靈沒有碰到左邊緣?
???????????
if
??(x??
>
???
0
?)???
{
????????????saveXY();
?????????????
//
??如果離左邊界小與3,則置0,
?????????????
//
??否則從當前位置減3?
?????????????x??
=
??(x??
<
???
3
???
?
???
0
??:?x??
-
???
3
?);
????????????setPosition(x,?y);
????????}
?
????}
?
注意:用3來更新橫坐標x值(或者縱坐標y值),這樣得到的用戶界面反應速度更快。如果每次只移動一個像素,那么在屏幕上移動將是一個漫長的過程。
將精靈添加到畫布:代碼書寫
畫布的作用就象是游戲的背景。所有的精靈連同畫布一起顯示到屏幕上。本節同時處理事件,包括當鍵(上,下,左,右)按下時屏幕的刷新。更著下面的步驟一步步來:
1、將下面的代碼拷貝到文本編輯器。
2、將此源文件保存到WTK安裝目錄下的\apps\Collisions\src目錄,并命名為CollisionCanvas.java。
一旦?所有代碼書寫完成,就可以返回此代碼檢驗幾個關鍵點。

/**//*--------------------------------------------------
?*?CollisionCanvas.java
?*-------------------------------------------------*/
import?javax.microedition.lcdui.game.*;
import?javax.microedition.lcdui.*;


public?class?CollisionCanvas?extends?GameCanvas?implements?Runnable?
{
????private?AnimatedSprite?spSpiral;?//?動畫精靈

????private?static?final?int?FRAME_WIDTH?=?57;?//?一幀畫面的寬度

????private?static?final?int?FRAME_HEIGHT?=?53;?//?一幀畫面的高度

????private?int?canvas_width,?canvas_height;?//?保存畫布信息

????private?ManSprite?spMan;?//?小人?(可移動)

????private?AppleSprite?spApple;?//?蘋果?(不可移動)

????private?CubeSprite?spCube;?//?立方體?(不可移動)

????private?StarSprite?spStar;?//?星星?(不可移動)

????private?LayerManager?lmgr;?//?圖層管理器

????private?boolean?running?=?false;?//?線程是否運行?

????private?Collisions?midlet;?//?主MIDlet


????public?CollisionCanvas(Collisions?midlet)?
{
????????//?構造函數
????????super(true);
????????this.midlet?=?midlet;

????????try?
{
????????????//?非動畫精靈
????????????spMan?=?new?ManSprite(Image.createImage("/man.png"));
????????????spApple?=?new?AppleSprite(Image.createImage("/apple.png"));
????????????spCube?=?new?CubeSprite(Image.createImage("/cube.png"));
????????????spStar?=?new?StarSprite(Image.createImage("/star.png"));
????????????//?動畫精靈
????????????spSpiral?=?new?AnimatedSprite(Image.createImage("/spiral.png"),
????????????????????FRAME_WIDTH,?FRAME_HEIGHT);
????????????//?將參考像素改到精靈的中心
????????????spSpiral.defineReferencePixel(FRAME_WIDTH?/?2,?FRAME_HEIGHT?/?2);
????????????//?將精靈放置在畫布中心
????????????//?(精靈中心和畫布中心重合)
????????????spSpiral.setRefPixelPosition(getWidth()?/?2,?getHeight()?/?2);
????????????//?創建并添加到圖層管理器
????????????lmgr?=?new?LayerManager();
????????????lmgr.append(spSpiral);
????????????lmgr.append(spMan);
????????????lmgr.append(spApple);
????????????lmgr.append(spCube);
????????????lmgr.append(spStar);

????????}?catch?(Exception?e)?
{
????????????System.out.println("Unable?to?read?PNG?image");
????????}
????????//?保存畫布的寬度和高度
????????canvas_width?=?getWidth();
????????canvas_height?=?getHeight();
????}?
????

????/**//*--------------------------------------------------
?????*?啟動線程
?????*-------------------------------------------------*/

????public?void?start()?
{
????????running?=?true;
????????Thread?t?=?new?Thread(this);
????????t.start();
????}


????/**//*--------------------------------------------------
?????*?Main?game?loop
?????*-------------------------------------------------*/

????public?void?run()?
{
????????Graphics?g?=?getGraphics();

????????while?(running)?
{
????????????//?探測有沒有鍵被按下
????????????checkForKeys();

????????????if?(checkForCollision()?==?false)?
{
????????????????drawDisplay(g);

????????????}?else?
{
????????????????//?背光燈閃爍,設備震動
????????????????midlet.display.flashBacklight(500);
????????????????midlet.display.vibrate(500);
????????????}

????????????try?
{
????????????????Thread.sleep(125);

????????????}?catch?(InterruptedException?ie)?
{
????????????????System.out.println("Thread?exception");
????????????}
????????}
????}


????/**//*--------------------------------------------------
?????*?檢測有沒有發生碰撞.
?????*-------------------------------------------------*/

????private?boolean?checkForCollision()?
{
????????if?(spMan.collidesWith(spSpiral,?true)
????????????????||?spMan.collidesWith(spApple,?true)
????????????????||?spMan.collidesWith(spCube,?true)

????????????????||?spMan.collidesWith(spStar,?true))?
{
????????????//?發生碰撞,恢復到上一個位置
????????????spMan.restoreXY();
????????????return?true;
????????}?else
????????????return?false;
????}


????/**//*--------------------------------------------------
?????*?有鍵按下,移動精靈
?????*-------------------------------------------------*/

????private?void?checkForKeys()?
{
????????int?keyState?=?getKeyStates();

????????if?((keyState?&?LEFT_PRESSED)?!=?0)?
{
????????????spMan.moveLeft();

????????}?else?if?((keyState?&?RIGHT_PRESSED)?!=?0)?
{
????????????spMan.moveRight(canvas_width);

????????}?else?if?((keyState?&?UP_PRESSED)?!=?0)?
{
????????????spMan.moveUp();

????????}?else?if?((keyState?&?DOWN_PRESSED)?!=?0)?
{
????????????spMan.moveDown(canvas_height);
????????}
????}


????/**//*--------------------------------------------------
?????*?刷新屏幕
?????*-------------------------------------------------*/

????private?void?drawDisplay(Graphics?g)?
{
????????//?創建背景
????????g.setColor(0xffffff);
????????g.fillRect(0,?0,?getWidth(),?getHeight());
????????//?動畫精靈,按次序顯示下一幀
????????spSpiral.nextFrame();
????????//?繪制所有圖層
????????lmgr.paint(g,?0,?0);
????????//?將屏幕緩沖刷新到屏幕
????????flushGraphics();
????}


????/**//*--------------------------------------------------
?????*?停止線程
?????*-------------------------------------------------*/

????public?void?stop()?
{
????????running?=?false;
????}
}
將精靈添加到畫布:代碼回顧
盡管Canvas類中的許多代碼都沒變,但是你必須增加兩個關鍵方法。首先你要增加一個事件處理方法來響應按鍵事件。
private
?
void
?checkForKeys()?
{
????????
int
?keyState?
=
?getKeyStates();

????????
if
?((keyState?
&
?LEFT_PRESSED)?
!=
?
0
)?
{
????????????spMan.moveLeft();

????????}
?
else
?
if
?((keyState?
&
?RIGHT_PRESSED)?
!=
?
0
)?
{
????????????spMan.moveRight(canvas_width);

????????}
?
else
?
if
?((keyState?
&
?UP_PRESSED)?
!=
?
0
)?
{
????????????spMan.moveUp();

????????}
?
else
?
if
?((keyState?
&
?DOWN_PRESSED)?
!=
?
0
)?
{
????????????spMan.moveDown(canvas_height);
????????}
????}
當用戶選擇了一個方向,按下了相應的鍵時,調用Man類中的相應方法改變橫坐標x和縱坐標y的值,從而處理精靈移動。
第二個變化的地方是碰撞檢驗代碼:
private
?
boolean
?checkForCollision()?
{
????????
if
?(spMan.collidesWith(spSpiral,?
true
)
????????????????
||
?spMan.collidesWith(spApple,?
true
)
????????????????
||
?spMan.collidesWith(spCube,?
true
)

????????????????
||
?spMan.collidesWith(spStar,?
true
))?
{
????????????
//
?發生碰撞,恢復到上一個位置
????????????spMan.restoreXY();
????????????
return
?
true
;
????????}
?
else
????????????
return
?
false
;
????}
每一次有鍵按下,你必須通過調用Man類的collidesWith()方法與可能發生碰撞的精靈進行檢驗碰撞。一旦發生了碰撞,就將提前在ManSprite.java中的保存的x,y坐標值恢復回來。
主MIDlet代碼
下面的代碼主要負責啟動和關閉MIDlet,這跟上一章例子中的主MIDlet差不多。
1、將下面的代碼拷貝到文本編輯器。
2、將此源文件保存到WTK安裝目錄下的\apps\Collisions\src目錄,并命名為Collisions.java

/**//*--------------------------------------------------
?*?Collisions.java
?*?使用精靈的演示MIDlet
?*?包括碰撞檢驗.
?*-------------------------------------------------*/
import?javax.microedition.midlet.*;
import?javax.microedition.lcdui.*;


public?class?Collisions?extends?MIDlet?implements?CommandListener?
{
????protected?Display?display;?//?Reference?to?display

????private?CollisionCanvas?canvas;?//?游戲畫布

????private?Command?cmExit;?//?退出按鈕


????public?Collisions()?
{
????????display?=?Display.getDisplay(this);
????????//?創建游戲畫布和退出按鈕

????????if?((canvas?=?new?CollisionCanvas(this))?!=?null)?
{
????????????cmExit?=?new?Command("Exit",?Command.EXIT,?1);
????????????canvas.addCommand(cmExit);
????????????canvas.setCommandListener(this);
????????}
????}


????public?void?startApp()?
{

????????if?(canvas?!=?null)?
{
????????????display.setCurrent(canvas);
????????????canvas.start();
????????}
????}


????public?void?pauseApp()?
{
????}


????public?void?destroyApp(boolean?unconditional)?
{
????????canvas.stop();
????}


????public?void?commandAction(Command?c,?Displayable?s)?
{

????????if?(c?==?cmExit)?
{
????????????destroyApp(true);
????????????notifyDestroyed();
????????}
????}
}
編譯、預校驗、運行
在WTK中單擊Build按鈕編譯、預校驗并打包MIDlet。選擇Run啟動應用管理器,點擊Launch運行MIDlet。
(圖20)
像素級碰撞檢驗:第一部分
讓我們回到代碼來確認一下兩個精靈間是否發生了碰撞:
private
?
boolean
?checkForCollision()?
{
????????
if
?(spMan.collidesWith(spSpiral,?
true
)
????????????????
||
?spMan.collidesWith(spApple,?
true
)
????????????????
||
?spMan.collidesWith(spCube,?
true
)

????????????????
||
?spMan.collidesWith(spStar,?
true
))?
{
????????????
//
?發生碰撞,恢復到上一個位置
????????????spMan.restoreXY();
????????????
return
?
true
;
????????}
?
else
????????????
return
?
false
;
????}
上一節中關于這個方法的討論,我們并沒有澄清布爾參數表示的含義。當傳入true時,說明要求像素級碰撞。在像素級碰撞的情況下,兩個精靈的不透明像素碰撞時碰撞才發生。下一節你將學到更多這方面的知識。
像素級碰撞:第二部分
請注意圖21中,當碰撞發生時兩個精靈間的接近情況。
(圖21)
讓我們將修改以下代碼,關掉像素級碰撞,看看會有什么情況發生。
private
?
boolean
?checkForCollision()?
{
????????
if
?(spMan.collidesWith(spSpiral,??false
)
????????????????
||
?spMan.collidesWith(spApple,?
false
)
????????????????
||
?spMan.collidesWith(spCube,?
false
)

????????????????
||
?spMan.collidesWith(spStar,?
false
))?
{
?????????????... ...
????}
像素級碰撞:第三部分
圖22中,請大家注意碰撞被檢測到時的不同。我畫出了精靈的外圍邊界來突出他的碰撞范圍。
(像素級碰撞關閉狀態,當碰撞范圍有交差時,碰撞就被檢測到)
(像素級碰撞打開狀態,精靈間能更接近)
使用像素級碰撞時,精靈的透明部分可以交疊。提升了碰撞檢驗的精確度。
posted on 2006-05-03 12:16
學二的貓 閱讀(3222)
評論(2) 編輯 收藏 所屬分類:
J2ME