J2ME 小遊戲即在消費類電子設備上運行的遊戲,例如在蜂窩電話、可視電話、數字機頂盒、汽車導航系統、個人數字助理(PDA)和移動手持設備(MID)上運行的小遊戲大多為J2ME小遊戲。J2ME是一種高度最佳化的Java運行環境,是Java的組成部分,它主要針對消費類電子設備的,在此基礎上設計出的遊戲統稱為J2ME 小遊戲。
基本介紹
- 中文名:J2ME 小遊戲
- 平台:消費類電子設備
- 基礎:蜂窩電話、可視電話、數字機頂盒
- 種類:Sprite,TiledLayer
- 聲音:簡單重複
- 控制:ToneControl
簡介,遊戲特色,Gaming API分類,Sprite,TiledLayer,LayerManager,GameCanvas,操作指南,檢測方法,存在的問題,
簡介
一個J2ME的2D遊戲技術DEMO源碼
計畫了很久了,準備作為J2ME的Game APIs的例子貼出來,無奈一直不得空,直到最近才簡單地整理了一下,把它共享出來。
遊戲特色
嚴格地講,這個不能算作一個完整的遊戲,沒有自己的創意,只是簡單地模仿了一個Flash的小遊戲,當時是為了自己練習用的,Game Play也很簡單,但包含了J2ME的Game包中所有的東西,作為一個Demo來講,內容還是很充實的。
Gaming API分類
在介紹之前,先簡單地提一下Gaming API中的幾個類,包括Sprite,TiledLayer,LayerManager,Media.Player等。
Sprite
偶爾看到過有些人把它翻譯成精靈,我偷懶一下,就不翻譯了,這個類用來表達遊戲中的一個活動的角色,包括玩家控制的Player角色和非玩家控制的角色(NPC)。
TiledLayer
這個是用來表達背景的類,其實從繪圖的角度來看,Sprite和TiledLayer沒有本質差別,只是將要畫在螢幕上的一幅圖像而已,因此在Game包中它們都是Layer類的子類。並且都能夠從一幅圖像方便地構造。
LayerManager
這個是用來管理所有圖像對象的類,通過把Sprite和TiledLayer加入其中,J2ME設備就知道如何來繪製它們了。
GameCanvas
最後要提到的是畫布,這是所有可視對象最終要表演的舞台。其實跟以前的Canvas沒有本質的不同,同樣是提供了一個Graphics接口來供把一些內容畫上去而已,但增加了對玩家輸入的處理,能夠通過按鍵狀態來直接讀入玩家按鍵操作,相對更加簡便了。
還利用到了Media包中的Player和ToneControl來播放音樂,沒有音樂和聲音的遊戲是不可能出現的,呵呵。
操作指南
先簡單地介紹一下這個遊戲,玩家只能控制角色(一個端著網兜的小人)水平地移動,接住自然下落的小球就得分,積分到一定程度後,小球下落速度將加快,直到最高速為止;如果沒接到小球,也有相應的懲罰,最終遊戲會Game Over。
為了避免過於單調,玩家角色不是簡單地平移,而是利用了Sprite的簡單幀動畫來讓角色看上去有些動作。其實很方便的,只是在移動位置時更換一下圖像就行了,Sprite提供有幾個方法NextFrame(),PrevFrame()用來切換。
聲音部分就更簡單了,只是重複地播放一段預先寫進去的音樂,來自Sun的WTK中的一個例子。
不過既然提到了它,就還是先簡單地說一下吧,免得後面介紹其他部分時有些疑問。
J2ME中的聲音部分非常簡單,當然效果也不太好,所需的基本元素只有如下幾個,一個內容部分的Byte系列;一個是播放器Player;還有一個是控制部分的ToneControl。代碼示例如下:
tonePlayer = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);
tonePlayer.setLoopCount(-1);
tonePlayer.realize();
ToneControl tc = (ToneControl)tonePlayer.getControl("javax.microedition.media.control.ToneControl");
tc.setSequence(mySequence);
當然還需要些初始化工作和異常處理,然後就可以通過tonePlayer.start()/close()方法來控制聲音的播放和停止了。
具體請參見源碼中CanvasGetBall.java中的createTonePlayer()方法。
鑼鼓響了半天,主角也該出場了。這個遊戲裡的主角只有一個接球小人,不過角色還有一個跟它演對手戲的NPC,就是那隻從天而降的小球了。這兩個類都是Sprite的子類,小球因為有些自己的動作,同時實現了Runnable接口,能自主活動。不過也很簡單,NPC嘛,一般來說都是相對弱智些,不然也沒法玩了,誰的反應速度跟得上機器啊,再說了,NPC知道的信息也要多些:)。
先來看看主角吧,BallPlayer類就是我們的主角,其實非常簡單,在所有的6個類中,除了記分用的Score外就數它最小了。提供一幅圖像把它實例化後,就只能通過左右移動來控制了,額外的幾個方法都是跟記分有關係的,先略過不提。
先來看看它的構造方法:
public BallPlayer(Image img, int fw, int fh) {
super(img, fw, fh);
this.step = fw / 2;
}
主要工作都由它的父類Sprite做了,給出一幅圖像,這個圖像是用PNG格式提供的,大家可能留意到不是一幅單一的圖像,而是有點象幀動畫中的幾個關鍵幀,不錯,的確如此,構造方法中的後兩個參數就是告訴Sprite如何分割這幅圖像的。這裡整個Player共有6個關鍵幀,比較粗糙,呵呵,自己動手截屏做的:)
Field step是用來控制主角的移動步伐的,為了快一點,取了它身寬的一半。
接下來我們看看如何移動它,就是通過這樣兩個方法來左移和右移。
public void left() {
prevFrame();
if( (getX() - step) >= -12 ) {
move( -1 * step, 0);
}
}
public void right () {
nextFrame();
if((getX() + step) < canvas.getWidth()) {
move(step, 0);
}
}
留意一下,這裡只管相對位移,主角的開始位置通過setPosition來設定,在運動過程中最好就不要直接設定位置了,增大計算量,要不就看起來動作不自然了。
接下來簡單說一下配角--球。球的構造跟主角類似,只是為了節省構造銷毀對象帶來的開銷,這個對象是一直存在的,也就是讓它掉下去了又自己起來,並根據記分來確定下落速度,簡單地用執行緒實現的,沒怎么仔細設計,大家看看代碼就清楚了。
再來看看CanvasGetBall這個類,它從GameCanvas繼承,並實現了CommandListener和Runnable兩個接口,是整個遊戲中最複雜的一個類了,主要工作有如下幾個部分,實例化主角,配角對象,還有背景對象,音樂等,並在適當的時候畫出這些對象,在頂部畫出些狀態信息,並根據玩家操作開始和暫停遊戲,並顯示相應畫面。並通過進行碰撞檢測來判定玩家是否接到了小球。
檢測方法
檢測方法如下:
private boolean notMiss( ) {
// return player.collidesWith(ball,false);
int ballCX = ball.getX() + ball.getWidth()/2;
int ballCY = ball.getY() + ball.getHeight()/2;
int playerCX = player.getX() + player.getWidth()/2;
int playerCY = player.getY() + player.getHeight()/2;
return ((Math.abs(playerCX - ballCX)< ball.getWidth()/2) &&
(Math.abs(ballCY - playerCY) < 5));
}
被注釋掉的一行是直接用Sprite的碰撞檢測,下面的部分是自己計算兩幅圖像有沒有重疊,效果差不多。其中collidesWith()的第二個參數是告訴內部方法是否要用像素級別的檢測,通常答案是千萬不要,這很慢的,而且沒有必要這么精確。
為了說明整個遊戲的控制邏輯,我們先來看看MIDletGetBall這個類,跟通常的MIDlet略有不同,因為我把主執行緒放在了CanvasGetBall中,MIDletGetBall只是簡單地控制主執行緒就行了。
public void startMainThread() {
Display.getDisplay(this).setCurrent(displayable);
if(mainThread != null) {
mainThread = null;
Runtime.getRuntime().gc();
}
mainThread = new Thread(displayable);
mainThread.start();
}
其中第一行就是設定當前顯示頁面;也就是顯示CanvasGetBall。
回到CanvasGetBall,整個遊戲分幾個階段,相應有不同的畫面和命令接口,詳細說明如下:
1. 等待開始,對應在方法ready():
public void ready() {
cover.setTitle(TIPS[2]);
cover.addCommand(playCommand);
Display.getDisplay(MIDletGetBall.instance).setCurrent(cover);
}
為了繪製方便,這裡單獨用了個GameCanvas來繪製提示信息和回響命令,並根據玩家操作在CanvasCover和CanvasGetBall兩個畫面之間來回切換。
2. 遊戲畫面,包括啟動和結束兩個方法:
public void start() {
if(!playing) {
strTip = TIPS[0];
playing = true;
MIDletGetBall.instance.startMainThread();
removeCommand(playCommand);
removeCommand(resumeCommand);
addCommand(pauseCommand);
ball.start();
try {
if(tonePlayer != null) {
tonePlayer.start();
}
}
catch (MediaException ex) {
tonePlayer.close();
tonePlayer = null;
}
}
}
public void stop () {
if(playing) {
ball.stop();
strTip = TIPS[1];
try {
Thread.sleep(300);
}
catch (InterruptedException ex) {
}
playing = false;
removeCommand(pauseCommand);
addCommand(resumeCommand);
try {
tonePlayer.stop();
}
catch (MediaException ex1) {
tonePlayer.close();
tonePlayer = null;
}
}
}
並對應設定相應的命令來讓玩家能夠繼續下去,構成一個簡單的封閉控制環路。
3.遊戲結束,對應方法gameover()
public void gameover() {
this.stop();
cover.setTitle(TIPS[3]);
cover.removeCommand(playCommand);
cover.addCommand(restartCommand);
Display.getDisplay(MIDletGetBall.instance).setCurrent(cover);
}
說到這裡,基本上也就把它講完了,具體內容請詳細研究源碼,其實沒必要看太多書,深入地研究一個問題並根據自己的理解來改進或者是修正它,實踐才是最好的老師,希望大家能夠有所收穫。
存在的問題
總結一下,這個遊戲存在的問題有如下幾個:
1. 沒有好的Game Play,畫面很差;
2. 可玩性不強,控制比較單調;
3. 遊戲聲音過於單調;
4. 運行速度有些慢。
但作為一個技術Demo,它涵蓋了Game包中的所有內容,並提供了一個利用執行緒方式實現簡單遊戲的方法,很簡單,但不適合真實的遊戲,比較費時。
背景處理很差,可以通過一個Map來分割組合處理背景小片,能讓遊戲場景變得生動些,可以實現類似於捲軸遊戲的效果,自己試試吧!
附:原始碼和工程,在JBuilderX下編譯,同時需要WTK2.0或以上版本。好久沒有用Jbuilder了,買不起正版:),現在主要開發工具是Eclipse和EclipseME,感覺非常爽,免費的也有好貨。