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