首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> J2ME

J2ME 線程編程實戰:以俄羅斯方塊為例詳解 Runnable 模式與遊戲主循環

     編輯:J2ME

J2ME 線程模型特點

  • 繼承 J2SE java.lang.Thread + Runnable
  • 無 ThreadGroup、無 Daemon 線程
  • 單進程單 VM,線程調度由 VM 實現(通常分時輪轉)
  • 嚴禁在 UI 線程做耗時操作(網絡、複雜計算、sleep)

核心模式:Canvas 實現 Runnable

因 Canvas 已繼承 Displayable,**不能再繼承 Thread**,故採用組合:


public class TetrisCanvas extends Canvas implements Runnable {
    private Thread gameThread = null;  // 組合線程對象

    public TetrisCanvas() {
        gameThread = new Thread(this); // this = Runnable 實例
        gameThread.start();            // 啟動遊戲主循環
    }

    public void run() {                // 遊戲主循環入口
        while (gameThread != null) {   // 以 thread 引用為生命標誌
            // 1. 邏輯更新
            updateGameState();
            // 2. 觸發重繪
            repaint();
            // 3. 幀率控制
            try { Thread.sleep(frameDelay); } catch (InterruptedException e) {}
        }
    }

    // 停止遊戲(如 MIDlet.pauseApp 調用)
    public void stopGame() {
        gameThread = null;  // run() 循環下一次判斷退出
    }
}

完整俄羅斯方塊核心邏輯(節選)

1. 關鍵常量與狀態


private static final int BLOCK_SIZE = 7;
private static final int GRID_W = 12, GRID_H = 22;
private static final int OFF_X = 5, OFF_Y = 7;

// 19 種方塊定義:[x0,y0,x1,y1,x2,y2,x3,y3, color, 旋轉態數]
private static final int[][] BLOCKS = {
    {1,0, 1,1, 1,2, 1,3, 0xFF0000, 2},  // I 型
    {0,1, 1,1, 2,1, 3,1, 0xFF0000, 4},  // I 橫
    {0,0, 0,1, 1,1, 1,2, 0x0000FF, 2},  // Z
    // ... 其餘 16 種
};

private int[][] grid = new int[GRID_H][GRID_W];  // 只存顏色,0=空
private int curType, curX, curY;                 // 當前方塊
private int nextType;                            // 下一個
private int score, lines, level, speed;

2. 碰撞檢測


private boolean feasible() {
    for (int i = 0; i < 4; i++) {
        int bx = curX + BLOCKS[curType][2*i+1];
        int by = curY + BLOCKS[curType][2*i];
        if (by < 0 || by >= GRID_H || bx < 0 || bx >= GRID_W) return false;
        if (grid[by][bx] != 0) return false;
    }
    return true;
}

3. 消行邏輯


private void clearLines() {
    // 先將當前方塊固化到 grid
    for (int i = 0; i < 4; i++) {
        int bx = curX + BLOCKS[curType][2*i+1];
        int by = curY + BLOCKS[curType][2*i];
        grid[by][bx] = BLOCKS[curType][8];
    }
    // 自底向上掃描
    for (int y = GRID_H-2; y > 0; y--) {
        boolean full = true;
        for (int x = 1; x < GRID_W-1; x++) if (grid[y][x] == 0) { full = false; break; }
        if (full) {
            // 下落
            for (int yy = y; yy > 0; yy--)
                System.arraycopy(grid[yy-1], 0, grid[yy], 0, GRID_W);
            y++;  // 重新檢查當前行
            lines++; score += 200;
            if (score % 2000 < 200) speed++;  // 每 2000 分加速
        }
    }
}

4. 輸入處理(即時響應)


protected void keyPressed(int keyCode) {
    int action = getGameAction(keyCode);
    switch (action) {
        case LEFT:  curX--; if (!feasible()) curX++; break;
        case RIGHT: curX++; if (!feasible()) curX--; break;
        case DOWN:  curY++; if (!feasible()) curY--; speed = 9; break;  // 加速下落
        case FIRE:  // 旋轉
            int old = curType;
            curType = (curType + 1) % BLOCKS.length;  // 簡化:實際需按旋轉態數循環
            if (!feasible()) curType = old;
            break;
    }
    repaint();  // 即時反饋
}

5. 雙緩衝繪製


protected void paint(Graphics g) {
    // 背景(預載入 Image,不在 paint 中 createImage!)
    g.drawImage(bgImage, 0, 0, Graphics.TOP | Graphics.LEFT);

    // 疊落方塊
    for (int y = 0; y < GRID_H-1; y++)
        for (int x = 1; x < GRID_W-1; x++)
            if (grid[y][x] != 0)
                draw3DBlock(g, grid[y][x], OFF_X + x*BLOCK_SIZE, OFF_Y + y*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);

    // 當前方塊(未固化時)
    if (!landed) {
        for (int i = 0; i < 4; i++) {
            int bx = curX + BLOCKS[curType][2*i+1];
            int by = curY + BLOCKS[curType][2*i];
            draw3DBlock(g, BLOCKS[curType][8], OFF_X + bx*BLOCK_SIZE, OFF_Y + by*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
        }
    }

    // 側邊信息:下一個、分數、等級…
    drawSidePanel(g);
}

效能優化鐵律

  1. Image 預載入:構造器/ init() 中 Image.createImage(),**絕不在 paint() 中**
  2. 避免 new 物件:循環中復用 int[]、StringBuffer,減少 GC
  3. 定點數/整數運算:CLDC 1.0 無 float,CLDC 1.1 軟浮點慢
  4. 局部重繪:repaint(x,y,w,h) 或 setClip() 限制繪製區域
  5. 混淆壓縮:ProGuard 釋放體積、保護邏輯

常見錯誤與修正

錯誤後果修正
在 paint() 中 createImage嚴重卡頓、OOM移至 init() 預載入
UI 線程調用 Thread.sleep界面凍結、來電無響應所有 sleep 僅在遊戲線程
未停止線程就 MIDlet 銷毀VM 警告、資源洩漏pauseApp/destroyApp 中調用 stopGame()
keyPressed 邏輯過重輸入延遲、丟幀僅修改狀態變量,邏輯放線程

結語

J2ME 線程編程核心:**Canvas 實現 Runnable + 組合 Thread + 遊戲線程獨享邏輯/睡眠 + UI 線程純渲染/輸入轉發**。俄羅斯方塊展示了完整的「狀態機 + 碰撞檢測 + 消行 + 即時輸入 + 雙緩衝」最小閉環,掌握此模板可快速構建各類即時遊戲。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved