Java ME (MIDP 2.0) 引入的 javax.microedition.lcdui.game 包,以僅五個類構建了輕量級二維遊戲開發框架,解決了早期 Canvas 多線程繪圖與輸入處理分離導致的狀態同步難題。本文系統講解核心 API、遊戲主循環重構、圖層架構、精靈動畫與碰撞檢測,並以官方 MuTank 範例為藍本,展示完整遊戲開發流程。
| 類 | 角色 | 關鍵能力 |
|---|---|---|
GameCanvas | 遊戲畫布基類 | 雙緩衝繪圖、鍵盤狀態輪詢、單線程遊戲循環 |
Layer | 圖層抽象基類 | 位置、尺寸、可見性、繪圖協議 |
TiledLayer | 瓦片地圖圖層 | 大場景高效構建、動畫瓦片、單元格編輯 |
Sprite | 動畫精靈 | 幀動畫、變換(鏡像/旋轉)、參考像素、碰撞檢測 |
LayerManager | 圖層管理器 | 圖層疊序管理、統一繪圖調度 |
run()(應用線程)調用 repaint() → 系統稍後回調 paint()(繪圖線程),時機不可控keyPressed()(事件線程)異步修改遊戲狀態,與 paint() 競態
public class MyGameCanvas extends GameCanvas implements Runnable {
public void run() {
Graphics g = getGraphics(); // 直接獲取離屏緩衝區 Graphics
while (running) {
// 1. 更新遊戲狀態
int keyState = getKeyStates(); // 輪詢鍵盤,即時獲取按鍵狀態
handleInput(keyState);
// 2. 繪圖(全部在離屏緩衝區)
render(g);
// 3. 刷新到屏幕(阻塞直到垂直同步)
flushGraphics();
// 4. 幀率控制
sleep(frameDelay);
}
}
}
核心優勢:單線程串行化「輸入→邏輯→繪圖→顯示」,消除競態;flushGraphics() 同步刷新,幀率可控;getKeyStates() 輪詢替代事件回調,狀態獲取確定性強。
x, y 位置、width, height 尺寸、visible 可見性paint(Graphics g) 子類實現具體繪圖核心概念:一張源圖像切分為等大小瓦片,通過二維網格索引組合大地圖。
// 載入源圖(如 64x48 像素,切 12 個 16x16 瓦片)
Image tileset = Image.createImage("/board.png");
TiledLayer map = new TiledLayer(10, 10, tileset, 16, 16); // 10x10 網格
// 填充單元格:setCell(col, row, tileIndex) ,tileIndex 從 1 開始,0 為空
int[] mapData = { 1,1,1,1,11,0,... }; // 一維數組按行優先
for (int i = 0; i < mapData.length; i++) {
int col = i % 10;
int row = i / 10;
map.setCell(col, row, mapData[i]);
}
// 繪製
map.paint(g); // 自動只繪製可視區域
瓦片編號規則:源圖左上角為 1,橫向優先遞增。支援動畫瓦片:連續幀瓦片循環播放。
// 源圖包含多幀,每幀 32x32
Image tankImg = Image.createImage("/tank.png");
Sprite tank = new Sprite(tankImg, 32, 32); // 幀寬高
// 幀序列控制
tank.setFrameSequence(new int[]{0, 1, 2, 1}); // 幀索引從 0 開始
tank.nextFrame(); // 播放下一幀
tank.prevFrame(); // 播放上一幀
// 變換:旋轉/鏡像(組合常量)
tank.setTransform(Sprite.TRANS_MIRROR_ROT90); // 垂直鏡像+順時針90度
// 參考像素:旋轉/縮放中心點,默認左上角(0,0),建議設為中心
tank.defineReferencePixel(16, 16);
// 碰撞檢測
boolean hit = tank.collidesWith(otherSprite, true); // true=像素級精確,false=矩形快速
LayerManager lm = new LayerManager();
lm.append(map); // 背景圖層最先加入(底層)
lm.append(tank); // 角色圖層疊加在上
// 遊戲循環中一行搞定所有繪圖
lm.paint(g, 0, 0); // 可指定視口偏移實現鏡頭跟隨
public void run() {
Graphics g = getGraphics();
while (running) {
long start = System.currentTimeMillis();
tick(); // 邏輯更新:碰撞、AI、道具倒計時
input(); // 輸入處理:getKeyStates() 輪詢方向鍵/射擊鍵
render(g); // 渲染:lm.paint(g)
flushGraphics();
long elapsed = System.currentTimeMillis() - start;
long sleep = FRAME_DELAY - elapsed; // 80ms 目標幀間隔
if (sleep > 0) Thread.sleep(sleep);
}
}
動態幀率控制:測量邏輯+渲染耗時,動態睡眠補齊固定幀間隔,比固定 Thread.sleep(80) 穩定得多。
private void tick() {
tank.forward(); // 嘗試移動
if (checkCollision()) {
tank.undo(); // 碰撞則回滾上一幀位置
}
}
雖 Java ME 已退出歷史舞台,但 Game API 的設計哲學在現代遊戲引擎中依然閃光:
Update()、Godot _process()、Cocos2d-x scheduleUpdate()掌握這套微型框架,有助於理解現代引擎底層運作原理。