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

J2ME 低層 UI 事件處理三大技術演進:keyPressed → gameAction → keyStates

     編輯:J2ME

事件處理演進脈絡

J2ME 遊戲開發核心在於「輸入 → 邏輯 → 渲染」迴圈。MIDP 1.0 到 2.0,按鍵處理經歷三代演進,各解決前一代痛點:

代數API核心機制適用版本關鍵限制
第一代keyPressed(int keyCode)事件回調、同步處理MIDP 1.0+阻塞 UI 執行緒、功能鍵 keyCode 跨裝置差異大
第二代getGameAction(keyCode)邏輯映射標準化MIDP 1.0+仍綁定 keyPressed 回調、無法脫離事件執行緒
第三代getKeyStates() (GameCanvas)輪詢式狀態機、非阻塞MIDP 2.0+僅支援遊戲鍵、需 GameCanvas 子類、構造器啟用

第一代:覆寫 keyPressed —— 基礎但脆弱

按鍵分類與 keyCode 痛點

  • 數字鍵 0-9、*、#:所有裝置 keyCode 固定 (48-57, 42, 35)
  • 功能鍵:左右軟鍵、導航鍵(上下左右確定)、接聽/掛斷 —— 每廠商/機型 keyCode 完全不同

兩種判斷寫法

// ❌ 硬編碼魔術數字,不可維護、不可移植
if (keyCode == 52) { /* ... */ }

// ✅ 使用 Canvas 定義常數(僅限數字鍵/標準游戲鍵)
if (keyCode == Canvas.KEY_NUM4) { /* ... */ }

致命缺陷:遊戲邏輯、渲染、輸入全部擠在系統事件執行緒,長時間運算導致幀率崩潰、按鍵遺漏、UI 凍結。

第二代:gameAction 映射 —— 邏輯標準化的關鍵一步

映射對照表(廠商實作)

Game Action 常數對應物理鍵 (典型)
Canvas.UP數字鍵 2、導航上
Canvas.DOWN數字鍵 8、導航下
Canvas.LEFT數字鍵 4、導航左
Canvas.RIGHT數字鍵 6、導航右
Canvas.FIRE數字鍵 5、導航確定
Canvas.GAME_A數字鍵 1 或 7
Canvas.GAME_B數字鍵 3 或 9
Canvas.GAME_C數字鍵 7 或 *
Canvas.GAME_D數字鍵 9 或 #

標準處理模式

protected void keyPressed(int keyCode) {
    int action = getGameAction(keyCode); // 映射為邏輯動作
    switch (action) {
        case Canvas.LEFT:  moveLeft();  break;
        case Canvas.RIGHT: moveRight(); break;
        case Canvas.UP:    moveUp();    break;
        case Canvas.DOWN:  moveDown();  break;
        case Canvas.FIRE:  fire();      break;
        case Canvas.GAME_A: specialA(); break;
        // ...
    }
}

優勢:同一套邏輯碼跑在 Nokia/Sony/Motorola 上行為一致。殘留問題:仍被迫在 keyPressed 回調中處理,無法脫離系統執行緒。

第三代:keyStates 輪詢 —— 真正的遊戲迴圈解脫

啟用條件

  • 類別繼承 GameCanvas (MIDP 2.0 Game API)
  • 構造器呼叫 super(true) 啟用按鍵狀態追蹤
  • 啟用後,keyPressed 不再接收遊戲鍵事件(功能鍵仍正常)

核心 API

// 在遊戲迴圈執行緒中任意位置調用
int states = getKeyStates(); // 取得並清空當前狀態位元遮罩

// 位元運算檢測
if ((states & GameCanvas.LEFT_PRESSED)  != 0) moveLeft();
if ((states & GameCanvas.RIGHT_PRESSED) != 0) moveRight();
if ((states & GameCanvas.UP_PRESSED)    != 0) moveUp();
if ((states & GameCanvas.DOWN_PRESSED)  != 0) moveDown();
if ((states & GameCanvas.FIRE_PRESSED)  != 0) fire();

革命性優勢

  1. 完全脫離事件執行緒:輸入檢測在遊戲主迴圈執行緒,與邏輯更新、渲染同步
  2. 狀態保持:即使迴圈因 GC/系統中斷卡住 200ms,按鍵狀態位元仍保留,下一幀不會遺漏
  3. 多鍵同時檢測:位元遮罩天然支援斜向移動+開火等組合
  4. 固定時間步長友好:配合 flushGraphics() 實現確定性遊戲迴圈

完整 GameCanvas 遊戲迴圈範本

public class GameLoopCanvas extends GameCanvas implements Runnable {
    private Thread gameThread;
    private volatile boolean running = true;

    public GameLoopCanvas() {
        super(true); // ★ 啟用 keyStates
        gameThread = new Thread(this);
        gameThread.start();
    }

    public void run() {
        while (running) {
            long frameStart = System.currentTimeMillis();

            // 1. 輸入輪詢
            int keys = getKeyStates();
            processInput(keys);

            // 2. 邏輯更新
            updateGameLogic();

            // 3. 渲染
            Graphics g = getGraphics();
            render(g);
            flushGraphics(); // 雙緩衝翻轉

            // 4. 幀率控制 (目標 20 FPS = 50ms)
            long elapsed = System.currentTimeMillis() - frameStart;
            long sleep = Math.max(0, 50 - elapsed);
            try { Thread.sleep(sleep); } catch (InterruptedException ignored) {}
        }
    }

    private void processInput(int keys) {
        if ((keys & LEFT_PRESSED)  != 0) player.moveLeft();
        if ((keys & RIGHT_PRESSED) != 0) player.moveRight();
        if ((keys & FIRE_PRESSED)  != 0) player.shoot();
    }
    // ... updateGameLogic, render 實作
}

實戰選型建議

專案類型推薦方案理由
MIDP 1.0 相容專案gameAction + keyPressed唯一選擇,善用 switch-case 結構
MIDP 2.0 純遊戲專案GameCanvas + keyStates效能最佳、架構最清晰、業界標準
混合 UI (表單+遊戲)Form/List 做選單,GameCanvas 做遊戲職責分離,各用其長
需處理功能鍵(軟鍵選單)GameCanvas 仍保留 keyPressed 處理非遊戲鍵雙軌並行,互不干擾

結語

從 keyPressed 到 gameAction 再到 keyStates,J2ME 事件處理演進史,是從「被動回調」走向「主動輪詢」、從「邏輯與渲染耦合」走向「確定性遊戲迴圈」的工程化歷程。掌握第三代技術,意味著你擁有了在 200KB 記憶體、100MHz CPU 上構建流暢遊戲的核心能力——這份底層功力,在今天 Unity/Unreal/Golang 遊戲服務端開發中,依然閃耀著工程美學的光芒。

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