經典三層 J2ME 遊戲架構:
Canvas 實現 Runnable,負責渲染管線、輸入事件、遊戲主循環測試環境:WTK 2.5.1 運行通過。
public class TerrisMIDlet extends MIDlet {
public TerrisMIDlet() {
Display.getDisplay(this).setCurrent(new GameCanvas());
}
protected void destroyApp(boolean unconditional) {}
protected void pauseApp() {}
protected void startApp() {}
}
public class GameCanvas extends Canvas implements Runnable {
private GameEngine engine = new GameEngine();
private int width, height;
private boolean gameOver = false;
private boolean isPaused = false;
private final String pauseStr = "暫停", continueStr = "繼續";
public GameCanvas() {
setFullScreenMode(true); // 全屏模式
width = getWidth(); height = getHeight();
new Thread(this).start(); // 啟動遊戲循環線程
}
// 渲染管線
protected void paint(Graphics g) {
clearScreen(g); // 1. 清屏
paintBoard(g); // 2. 邊框
engine.paintMap(g); // 3. 地圖已固化方塊
engine.paintBrick(g); // 4. 當前下落方塊
engine.paintNextBrick(g); // 5. 下一個方塊預覽
engine.paintLevelAndScore(g); // 6. 分數/等級
if (gameOver)
g.drawString("Game Over", 30, 85, Graphics.TOP | Graphics.LEFT);
// 暫停/繼續提示 (右下角)
g.drawString(isPaused ? pauseStr : continueStr,
width, height, Graphics.RIGHT | Graphics.BOTTOM);
}
// 輸入處理
public void keyPressed(int keyCode) {
if (keyCode == -7) { // 右軟鍵:暫停/繼續切換
isPaused = !isPaused; return;
}
if (isPaused) return; // 暫停狀態忽略遊戲按鍵
int action = getGameAction(keyCode); // 映射為標準遊戲鍵
switch (action) {
case UP: case FIRE: engine.changeShape(); break; // 旋轉
case LEFT: engine.moveToLeft(); break;
case RIGHT: engine.moveToRight(); break;
case DOWN: engine.highSpeed(); break; // 加速下落
}
repaint();
}
public void keyReleased(int keyCode) {
int action = getGameAction(keyCode);
if (action == DOWN) engine.normalSpeed(); // 釋放下鍵恢復正常速度
}
// 輔助繪製
private void paintBoard(Graphics g) {
g.drawRect(0, 0,
engine.MAXCOL * engine.CELLWIDTH,
engine.MAXROW * engine.CELLWIDTH);
}
private void clearScreen(Graphics g) {
g.setColor(0xffffff); g.fillRect(0, 0, width, height);
g.setColor(0);
}
// 遊戲主循環
public void run() {
try {
while (true) {
Thread.sleep(engine.speed / engine.times); // 速度/加速因子
if (!isPaused) {
engine.brickDown(); // 邏輯更新
if (engine.isGameOver()) { // 結束判定
gameOver = true;
repaint();
break; // 退出循環
}
}
repaint(); // 幀渲染
}
} catch (Exception e) { e.printStackTrace(); }
}
}
public class GameEngine {
// 7種方塊 × 旋轉態數 × 4×4 矩陣 (0=空, 1=實心)
// I, J, L, O, S, T, Z 型完整定義
int[][][][] brick = { /* 見源碼完整數據 */ };
// 遊戲區域常量
final int MAXROW = 19; // 行數
final int MAXCOL = 10; // 列數
public static final int CELLWIDTH = 10; // 像素大小
// 地圖狀態
int[][] map = new int[MAXROW][MAXCOL];
// 遊戲狀態變量
int nextBrickType = 0; // 下一個方塊類型
int brickType, index; // 當前方塊類型、旋轉態
int cRow, cCol; // 當前方塊左上角在地圖中的位置
int speed = 500; // 基礎下落間隔 (ms)
int times = 1; // 加速倍數
int level = 0; // 關卡
int score = 0; // 分數 (每行10分)
Random r = new Random();
public GameEngine() { gernerateBrick(); }
// 單格繪製 (3層漸變效果)
private void paintCell(Graphics g, int x, int y) {
g.setColor(0); // 黑色邊框
g.drawRect(x, y, CELLWIDTH, CELLWIDTH);
g.setColor(0xffffff); // 白色內框
g.fillRect(x+1, y+1, CELLWIDTH-2, CELLWIDTH-2);
g.setColor(0); // 黑色核心
g.fillRect(x+2, y+2, CELLWIDTH-4, CELLWIDTH-4);
}
// 地圖渲染
public void paintMap(Graphics g) {
for (int row = 0; row < MAXROW; row++)
for (int col = 0; col < MAXCOL; col++)
if (map[row][col] == 1)
paintCell(g, col * CELLWIDTH, row * CELLWIDTH);
}
// ... 其餘邏輯方法見下篇
}
| 層面 | 設計決策 | 優勢 |
|---|---|---|
| 分層 | MIDlet/Canvas/Engine 三層分離 | 邏輯可單測、UI 可替換、符合 MVC |
| 主循環 | 獨立線程 + Thread.sleep 固定幀率 | 簡單可靠、J2ME 標準模式 |
| 渲染管線 | 清屏→邊框→地圖→當前方塊→預覽→UI 疊加 | 層次分明、繪製順序正確 |
| 輸入映射 | getGameAction() 標準化鍵盤 | 跨設備兼容 (鍵盤/觸屏/手柄) |
| 暫停機制 | 右軟鍵切換 isPaused,邏輯/渲染雙重守護 | 即時響應、無幀丟失 |
| 加速下落 | 按下/釋放下鍵切換 times 倍率 | 手感自然、實現簡單 |
| 方塊數據 | 4維數組 [type][rotation][row][col] | 查詢 O(1)、內存緊湊、易擴展新方塊 |
| 單格繪製 | 三層矩形漸變 (邊框/高光/核心) | 偽 3D 效果、視覺層次豐富 |
brick[7][max_rotations][4][4] 完整定義了 7 種標準方塊 (I/J/L/O/S/T/Z) 的所有旋轉態:
總計 19 個旋轉態,每態 4×4 矩陣,1 表示實心格子。這種預計算查表法避免了運行時旋轉變換運算,極大降低 CPU 佔用,適合 J2ME 受限環境。
MIDlet.startApp()
→ new GameCanvas()
→ setFullScreenMode()
→ new Thread(run).start()
→ loop:
sleep(speed/times)
if !paused:
engine.brickDown() // 邏輯幀
if engine.isGameOver(): break
repaint() // 渲染幀
→ paint()
→ clearScreen()
→ paintBoard()
→ engine.paintMap()
→ engine.paintBrick()
→ engine.paintNextBrick()
→ engine.paintLevelAndScore()
→ draw GameOver/Pause overlay
這個實現展示了 J2ME 遊戲開發的標準最佳實踐:
下篇將深度解析 GameEngine 中的運動算法、碰撞檢測、消行邏輯及遊戲結束判定。