您好,登錄后才能下訂單哦!
小編給大家分享一下如何使用Game API函數(shù)制作二維動作游戲,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
MIDP
2.0里面包括一個用來簡化編寫二維游戲的API函數(shù)。這個API函數(shù)是非常簡湊的,只包括javax.microedition.lcdui.game包里的五個類。這五個類主要提供了兩個重要的功能:
? 新的GameCanvas類使得在一個游戲循環(huán)體內(nèi)畫一個screen和響應(yīng)鍵盤輸入成為可能,而不需要調(diào)用系統(tǒng)的paint和input線程。
?
功能強(qiáng)大而復(fù)雜的圖層(layer)API函數(shù)可以輕松高效地建立復(fù)雜的場景。
screen.width-333)this.width=screen.width-333;">
muTank
Example
利用GameCanvas類創(chuàng)建一個游戲循環(huán)(game loop)
GameCanvas類是附加了功能的Canvas類,它提供了立即重畫和檢查設(shè)備按鍵狀態(tài)的方法。這些新的方法把一個游戲的所有函數(shù)(功能)封裝在一個循環(huán)體內(nèi),并由一個單線程進(jìn)行控制。為什么這樣做就非常吸引人阿?先讓我們考慮一下你是如何執(zhí)行一個使用了Canvas類的典型游戲的:
public void MicroTankCanvas
extends Canvas
implements Runnable {
public void run() {
while (true) {
// Update the game state.
repaint();
// Delay one time step.
}
}
public void
paint(Graphics g) {
// Painting code goes here.
}
protected void
keyPressed(int keyCode) {
// Respond to key presses here.
}
}
這不是一個美麗的畫面
。運(yùn)行在應(yīng)用程序線程中的run()方法,每一個時間段都會刷新游戲。典型的任務(wù)是刷新小球或飛行物的位置,繪制人物或飛行器動畫。每一次通過循環(huán)體,repaint()方法被用來刷新屏幕。系統(tǒng)把按鍵事件傳送給KeyPressed(),它能適當(dāng)?shù)厮⑿掠螒驙顟B(tài)。
問題是,每樣?xùn)|西都在不同的線程里,游戲代碼在以上三種不同方法里傳遞很容易混淆。當(dāng)run()方法里的主動畫循環(huán)體調(diào)用repaint()方法時,將沒有辦法確切知道系統(tǒng)什么時候調(diào)用paint()方法。當(dāng)系統(tǒng)調(diào)用KeyPressed()時,也沒有辦法知道程序的另一部分正在進(jìn)行什么。如果你KeyPressed()中的代碼將要刷新游戲的狀態(tài),而同一時刻paint()方法將表現(xiàn)屏幕,這時屏幕將會持續(xù)非常奇怪的狀態(tài)。如果表現(xiàn)屏幕所用時間超過一個單時間段,動畫會看起來顛簸不定或是很奇怪。
GameCanvas類允許你避開常用繪畫(painting)和按鍵消息(key-event)機(jī)制,所以所有的游戲邏輯都可以被包括在一個單循環(huán)中。首先,GameCanvas類允許你用getGraphics()方法直接訪問Graphics對象。對于所返回的Graphics對象的任何表現(xiàn)(rendering)都可以通過屏幕外緩沖區(qū)(offscreen
buffer)來實(shí)現(xiàn)。你可以用flushGraphics()復(fù)制緩沖區(qū)到屏幕上,直到屏幕被刷新才會返回。這種方式給你提供比調(diào)用repaint()方法更完善的控制。Repaint()方法會立即返回值,以至于你的應(yīng)用程序不能確定系統(tǒng)什么時候會調(diào)用paint()來刷新屏幕。
GameCanvas類也包含一個用來獲得設(shè)備按鍵當(dāng)前狀態(tài)的方法,即所謂得polling技術(shù)。你可以通過調(diào)用GameCanvas類的getKeyStates()方法,馬上確定哪一個按鍵被按下,從而取代了等待系統(tǒng)調(diào)用KeyPressed()方法。
下面是一個使用GameCanvas類的典型的游戲循環(huán)體:
public void MicroTankCanvas
extends
GameCanvas
implements Runnable {
public void run() {
Graphics g =
getGraphics();
while (true) {
// Update the game state.
int keyState
= getKeyStates();
// Respond to key presses here.
// Painting code goes
here.
flushGraphics();
// Delay one time step.
}
}
}
接下來的例子描述了一個基本的游戲循環(huán)體。它向你展現(xiàn)了一個旋轉(zhuǎn)的“X”,你可以用方向鍵在屏幕上移動它。這里的Run()方法特別的瘦小,這要多虧了GameCanvas。
import javax.microedition.lcdui.*;
import
javax.microedition.lcdui.game.*;
public class SimpleGameCanvas
extends GameCanvas
implements Runnable {
private boolean mTrucking;
private long mFrameDelay;
private int mX, mY;
private int
mState;
public SimpleGameCanvas() {
super(true);
mX = getWidth()
/ 2;
mY = getHeight() / 2;
mState = 0;
mFrameDelay = 20;
}
public void start() {
mTrucking = true;
Thread t = new
Thread(this);
t.start();
}
public void stop() { mTrucking =
false; }
public void run() {
Graphics g = getGraphics();
while (mTrucking == true) {
tick();
input();
render(g);
try { Thread.sleep(mFrameDelay); }
catch (InterruptedException ie) {}
}
}
private void tick() {
mState = (mState + 1) % 20;
}
private void input() {
int keyStates = getKeyStates();
if
((keyStates & LEFT_PRESSED) != 0)
mX = Math.max(0, mX - 1);
if
((keyStates & RIGHT_PRESSED) != 0)
mX = Math.min(getWidth(), mX + 1);
if ((keyStates & UP_PRESSED) != 0)
mY = Math.max(0, mY - 1);
if
((keyStates & DOWN_PRESSED) != 0)
mY = Math.min(getHeight(), mY + 1);
}
private void render(Graphics g) {
g.setColor(0xffffff);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0x0000ff);
g.drawLine(mX, mY, mX - 10 + mState, mY - 10);
g.drawLine(mX, mY, mX +
10, mY - 10 + mState);
g.drawLine(mX, mY, mX + 10 - mState, mY + 10);
g.drawLine(mX, mY, mX - 10, mY + 10 - mState);
flushGraphics();
}
}
本文所舉示例的代碼包括一個使用了這個canvas的MIDlet。你可以嘗試著運(yùn)行SimpleGameMIDlet這個小程序,看看它是怎樣工作的。你將會看到一個像正在做健身操的海星的東西(或許它正在尋找自己失掉的腿)。
screen.width-333)this.width=screen.width-333;">
SimpleGameMIDlet
Screen Shot
游戲場景就像是洋蔥(有層次)
典型的二維動作游戲常包含一個背景和若干動畫人物。盡管你可以自己來描繪出這種場景,不過Game
API函數(shù)使你能夠用圖層來建立場景。你可以做一個城市的背景圖層,另外再做一個含有一輛小汽車的圖層。將小汽車圖層放在背景上,你就創(chuàng)造出了一個完整的場景。把小汽車放在一個單獨(dú)的圖層中,可以很容易的熟練操控它,而不受背景和其他圖層的影響。
Game API函數(shù)使用以下四個類為圖層提供靈活的支持
?
Layer類是所有圖層類對象的抽象基類。它定義了一個圖層的基本屬性,包括位置,尺寸,和此圖層是否可見。Layer類的每個子類必須定義一個paint()方法,用來把這個圖層表現(xiàn)在一個圖象上,這個圖象將會被描畫到屏幕表面上。兩個確切的子類TiledLayer和Sprite應(yīng)該能滿足你的二維游戲的需要了。
? TiledLayer類用來建立背景圖像。你可以用一個小的源圖像貼的集合來高效的制作大的圖像。
?
Sprite類是一個動畫層。你提供源幀就可以對整個動畫進(jìn)行完全的控制。Sprite類也提供鏡像,并可對源幀作90度旋轉(zhuǎn)。
?
LayerManager類是一個非常有用的類,用來保存你的場景中的所有圖層的動作軌跡。LayerManager類
paint()方法的一個簡單調(diào)用就足以控制所包含的所有圖層。
使用TiledLayer類
盡管包含一些不是顯而易見的微妙不同,TiledLayer類還是很容易理解。這個類的基本思想就是,用一個源圖像提供一組圖像貼片,這些貼片可以組合成一幅大的場景。例如,下面的圖像是64*48像素的。
screen.width-333)this.width=screen.width-333;">
Source
Image
這個圖像被分成了12塊16*16的圖像貼片。TiledLayer類分配給每個圖像貼片編號,左上角的圖片規(guī)定為1,以此類推。上面源圖像的各個貼片如下編號:
screen.width-333)this.width=screen.width-333;">
Tile
Numbering
用代碼創(chuàng)建一個TiledLayer類是非常簡單的。你需要確定行數(shù)和列數(shù),源圖像以及這個源圖像里每個貼片的像素大小。下面的代碼片斷告訴你如何裝載圖像和創(chuàng)建TiledLayer類。
Image image = Image.createImage("/board.png");
TiledLayer tiledLayer =
new TiledLayer(10, 10, image, 16, 16);
在例子中,新的TiledLayer類有10行,10列。這些來自image的圖像貼片大小是16*16像素。
有趣的部分還是用這些圖像貼片來創(chuàng)建一幕場景。利用setCell()方法可以把一個圖像貼片分配到一個數(shù)組元胞里。你需要提供這個數(shù)組元胞所在行列數(shù)以及圖像貼片的編號。例如,你可以通過調(diào)用setCelll(2,1,5)方法把編號為5的圖像貼片分配到第2行中的第3個數(shù)組元胞里。如果你覺得這些參數(shù)看起來不對,請注意,圖像貼片編號是從1開始計(jì)數(shù),而行和列的編號是從0開始的。參數(shù)缺省情況下,新的TiledLayer類對象中的所有數(shù)組元胞的圖像貼片標(biāo)號為0,這就意味著它們是空的。
下面的代碼片斷向你說明一種使用整數(shù)數(shù)組來填充TiledLayer類對象。在實(shí)際圖像中,TiledLayer類可以從資源文件里定義,這就使得定義背景時可以有更多的靈活性,并能提供新的背景和級別來增強(qiáng)游戲的可玩性。
private TiledLayer createBoard() {
Image image = null;
try { image =
Image.createImage("/board.png"); }
catch (IOException ioe) { return null; }
TiledLayer tiledLayer = new TiledLayer(10, 10, image, 16, 16);
int[] map = {
1, 1, 1, 1, 11, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 9, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 7, 1, 0, 0, 0, 0, 0,
1, 1, 1,
1, 6, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 7, 11, 0,
0, 0, 0, 0, 0, 0, 7,
6, 0, 0,
0, 0, 0, 0, 0, 7, 6, 0, 0, 0
};
for (int i = 0; i <
map.length; i++) {
int column = i % 10;
int row = (i - column) / 10;
tiledLayer.setCell(column, row, map[i]);
}
return tiledLayer;
}
為了把這個TiledLayer類對象顯示在屏幕上,你需要調(diào)用一個Graphics對象的paint()方法。
TiledLayer類還支持動畫圖像帖子,這樣就使得通過一系列貼片來移動元胞集合很容易了。若想得到更詳細(xì)的說明,參看TiledLayer類相關(guān)的API文檔。
使用Sprite類實(shí)現(xiàn)人物動畫
Game
API函數(shù)里提供的另一個具體的Layer類是Sprite類。一方面,Sprite類是TileLayer類的概念化的逆轉(zhuǎn).TiledLayer類使用源圖像貼片的調(diào)色板來創(chuàng)建一幅大場景,而Sprite類則使用一系列源圖像幀來產(chǎn)生動畫。
你創(chuàng)建一個Sprite類所需要的只是源圖像和每個幀的尺寸。在TiledLayer類里,源圖像被分為相同大小的圖像貼片;在Sprite類里,子圖像被稱為幀。在下面的例子里,源圖像tank.png用來創(chuàng)建幀大小為32*32像素的Sprite類對象。
private MicroTankSprite createTank() {
Image image = null;
try {
image = Image.createImage("/tank.png"); }
catch (IOException ioe) { return
null; }
return new MicroTankSprite(image, 32, 32);
}
源圖像里面的每一幀都有一個編號,從0開始,以此累加。(在這里不要糊涂,記住圖像貼片的編號才是從1開始的)Sprite類有一個幀序列,它決定了幀顯示的順序。一個新Sprite類對象的缺省幀序列簡單地依照可用幀,從0開始累加。
使用Sprite類的nextFrame()方法和prevFrame()方法,可以把幀在幀序列中向前或向后移動。這些方法把幀序列的頭尾連接起來了。例如,如果Sprite類對象已經(jīng)把位于幀序列末尾的幀顯示出來了,若在調(diào)用nextFrame()方法將會顯示幀序列的頭幀。
調(diào)用setFrameSequence()方法,可以通過整型數(shù)組所指定的序列來確定不同于缺省時的幀序列。
你還可以調(diào)用setFrame()方法跳至當(dāng)前幀序列中的某一幀。你不能跳至特定的幀編號處,只能跳至幀序列的特定點(diǎn)。
利用從Layer類繼承下來的paint()方法時,只有在Sprite類在下一個時間段內(nèi)被表現(xiàn)的時候,幀的變化才真正實(shí)現(xiàn)。
Sprite類還可以變換源幀。可以把幀旋轉(zhuǎn)90度,或做鏡像變換,或兩者皆有。在Sprite類里的常數(shù)枚舉了這些可能性。Sprite類的當(dāng)前變換方式可以通過向setTransform()方法傳遞這些常數(shù)之一進(jìn)行設(shè)定。下面的例子是當(dāng)前幀繞垂直中心做鏡像變換,并旋轉(zhuǎn)90度:
// Sprite sprite = ...
sprite.setTransform(Sprite.TRANS_MIRROR_ROT90);
應(yīng)用了變換方式,從而使得Sprite類的參考像素并沒有移動。缺省下,Sprite類的參考像素位于Sprite類坐標(biāo)系里的(0,0)點(diǎn)處,即左上角。當(dāng)應(yīng)用了變換方式,參考像素的位置也變換了。Sprite類的位置被調(diào)整了,從而參考像素仍然在原位置上。
你可以通過調(diào)用defineReferencePixel()方法來改變參考像素點(diǎn)的位置。對于大多數(shù)類型的動畫,你可以把參考像素點(diǎn)定義在sprite的中心上。
最后,Sprite類提供幾個collidesWith()方法來檢測與其他Sprites,ItledLayers,或Images類對象的碰撞。你可以使用檢測矩形(快但粗糙)或者像素級別(慢但精確)來檢測碰撞。這些方法的微妙不同是難以描述的;若詳細(xì)資料,可參看API
文檔。
MuTank例子
MuTank例子向你說明TiledLayer,Sprite和LayerManager類的用法。
最重要的類是包含大部分代碼的MicroTankCanvas類和封裝了坦克行為的MicroTankSprite類。
MicroTankSprite類制作了大量的變換方式。它使用了一個只含3幀的源圖像來顯示指向16種方向的坦克。Turn()和forward()兩個公用方法使得坦克很容易控制。
MicroTankCanvas類是GameCanvas類的子類,它在run()方法里包含一個你應(yīng)該很熟悉的動畫循環(huán)體.Tick()方法用來檢測坦克是否碰到隔板上了。如果碰到了,就調(diào)用MicroTankCanvas類的undo()方法使它最近一次的運(yùn)動倒退。Input()方法簡單地檢測按鍵是否被按下,并同時調(diào)整坦克的方向或位置。Render()方法使用一個LayerManager類對象來對繪畫進(jìn)行處理。LayerManager類包含兩個圖層,坦克圖層和隔板圖層。
從游戲循環(huán)體中調(diào)用的Debug()方法,用來比較通過游戲循環(huán)體所用時間和期望的循環(huán)時間(80毫秒),并在屏幕上顯示時間的百分比。它僅僅是用作調(diào)試診斷目的的,在游戲被發(fā)送給用戶之前將會被刪除。
游戲循環(huán)體的計(jì)時比前面的SimpleGameCanvas類更加復(fù)雜。為了更精確的每80毫秒就重復(fù)一次游戲循環(huán)體,MicroTankCanvas類對tick(),input()和render()方法所花費(fèi)時間進(jìn)行測量。然后停下來花費(fèi)完80毫秒中的剩余時間,以使得通過每次循環(huán)所用的總共時間盡可能的接近于80毫秒。
以上是“如何使用Game API函數(shù)制作二維動作游戲”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。