MIDP 2.0 (JSR 118) 引入 javax.microedition.lcdui.game 包,提供 GameCanvas、Sprite、Layer、LayerManager 等專用 API,將原本需手工實現的雙緩衝、精靈動畫、圖層管理、碰撞檢測等核心功能標準化。本文以經典「坦克大戰」為目標,從零構建 GameCanvas 遊戲主循環,講解輸入處理、幀率控制、圖形渲染管線,並給出可直接運行的最小化示例。
lib/midpapi20.jar 与 lib/cldcapi11.jar;運行配置指定仿真器皮膚 (如 DefaultColorPhone)。| 特性 | Canvas (MIDP 1.0) | GameCanvas (MIDP 2.0) |
|---|---|---|
| 雙緩衝 | 需手動 Image.createImage + Graphics 管理 | 內建 getGraphics() 返回離屏緩衝,flushGraphics() 一鍵翻屏 |
| 輸入處理 | 事件驅動 keyPressed/Released/Repeated | 輪詢式 getKeyStates() 返回位掩碼,適合遊戲主循環 |
| 幀率控制 | 靠 Thread.sleep 粗略控制 | 配合 flushGraphics() 同步垂直回掃,畫面無撕裂 |
| 生命週期 | showNotify/hideNotify 通用 | 繼承並增強,建議在 showNotify 啟動線程,hideNotify 停止 |
public class MyGameCanvas extends GameCanvas implements Runnable {
// 狀態變量
private boolean isRunning = false;
private long frameDelay = 20L; // 目標幀間隔 ms (~50 FPS)
private int x = 0, y = 0; // 主角坐標
private int scrW, scrH; // 屏幕尺寸
public MyGameCanvas() {
super(true); // true = 抑制系統按鍵事件,改用輪詢
scrW = getWidth();
scrH = getHeight();
x = scrW / 2;
y = scrH / 2;
}
public void startGame() {
if (!isRunning) {
isRunning = true;
new Thread(this).start();
}
}
public void stopGame() { isRunning = false; }
// 主循環
public void run() {
Graphics g = getGraphics(); // 獲取離屏緩衝 Graphics
while (isRunning) {
handleInput(); // 1. 處理輸入
renderFrame(g); // 2. 繪制幀
flushGraphics(); // 3. 翻屏顯示 (阻塞至 VSync)
// 4. 幀率微調 (補償渲染耗時)
long elapsed = System.currentTimeMillis() - frameStart;
long sleep = frameDelay - elapsed;
if (sleep > 0) {
try { Thread.sleep(sleep); } catch (InterruptedException ignored) {}
}
}
}
private long frameStart;
private void handleInput() {
int keys = getKeyStates();
if ((keys & LEFT_PRESSED) != 0) x = Math.max(0, x - 2);
if ((keys & RIGHT_PRESSED) != 0) x = Math.min(scrW - 10, x + 2);
if ((keys & UP_PRESSED) != 0) y = Math.max(0, y - 2);
if ((keys & DOWN_PRESSED) != 0) y = Math.min(scrH - 10, y + 2);
}
private void renderFrame(Graphics g) {
frameStart = System.currentTimeMillis();
// 清屏
g.setColor(0xFFFFFF);
g.fillRect(0, 0, scrW, scrH);
// 繪制主角 (簡單用字符代替 Sprite)
g.setColor(0x0000FF);
g.drawString("X", x, y, Graphics.TOP | Graphics.LEFT);
// 可在此繪製 HUD、背景、精靈等
}
}
public class GameMIDlet extends MIDlet {
private Display display;
private MyGameCanvas canvas;
public void startApp() {
if (display == null) {
display = Display.getDisplay(this);
canvas = new MyGameCanvas();
canvas.startGame();
display.setCurrent(canvas);
}
}
public void pauseApp() { canvas.stopGame(); }
public void destroyApp(boolean unconditional) {
canvas.stopGame();
notifyDestroyed();
}
}
super(true) 啟用內建離屏緩衝 (尺寸 = 屏幕分辨率)。getGraphics() 返回離屏 Graphics,所有繪製操作寫入記憶體。flushGraphics() 將離屏內容原子複製到顯存,**阻塞等待垂直回掃**,消除撕裂,隱含幀率同步作用。getKeyStates() 返回 32 位位掩碼,按位與 (&) 判斷鍵狀態,適合「持續按住移動」等遊戲邏輯。LEFT_PRESSED, RIGHT_PRESSED, UP_PRESSED, DOWN_PRESSED, FIRE_PRESSED 等。super(true)) 可防止按鍵干擾系統菜單、輸入法等。上述示例使用固定像素位移,實際專案應引入 **時間步長 (delta time)**:
long now = System.currentTimeMillis();
long dt = now - lastFrameTime; // ms
lastFrameTime = now;
// 速度單位:像素/秒
x += (int)(velocityX * dt / 1000.0);
保證不同設備上移動速度一致。
GameCanvas 將 J2ME 遊戲開發門檻大幅降低:內建雙緩衝、輪詢輸入、VSync 同步三大核心機制,讓開發者聚焦於遊戲邏輯而非底層圖形管線。掌握「主循環 → 輸入 → 邏輯更新 → 離屏繪製 → 翻屏」這一標準幀流程,並結合時間步長實現幀率無關移動,是構建流暢 J2ME 遊戲的基石。