J2ME 遊戲通常包含兩個類:MIDlet(生命週期管理)與 Displayable 子類(繪製、事件、主循環)。Displayable 基於事件驅動,三大回調:paint(Graphics)、keyPressed(int)、keyReleased(int)。
GameMIDlet (生命週期)
└─ GameDisplayable extends FullCanvas implements Runnable
├─ Thread MainThread
├─ run() 循環:計時 → Game_Process() → sleep(30)
├─ paint() 繪製幀
└─ keyPressed/Released 處理輸入
public class GameDisplayable extends FullCanvas implements Runnable {
Thread MainThread = null;
public static long timeinterval = 20; // 目標幀間隔 ms
public static long timeold = 0, timenow = 0;
public static boolean running = true;
public GameDisplayable() {
MainThread = new Thread(this);
MainThread.start(); // 啟動主循環線程
}
public void run() {
while (running) {
timenow = System.currentTimeMillis();
long interval = timenow - timeold;
if (interval >= timeinterval) { // 固定時間步長
timeold = timenow;
Game_Process(); // 邏輯更新
// FPS 統計略
}
lib.sleep(30); // 關鍵:釋放 CPU,保證任務切換
}
}
// paint、keyPressed 等回調...
}
sleep(30) 必須性sleep(),遊戲將無法切回後台/前台;任何內部緊循環也需加 sleep(30)。30ms 經測試安全,更小值未驗證。sleep(30)。結論:採用「獨立線程 + sleep(30)」可同時滿足兩大主流平台,兼容性最佳。
public class GameMIDlet extends MIDlet {
public static long timeinterval = 0;
public static boolean running = false;
public void startApp() {
running = true;
Display.getDisplay(this).setCurrent(displayable);
while (running) {
timenow = System.currentTimeMillis();
long interval = timenow - timeold;
if (interval >= timeinterval) {
timeold = timenow;
displayable.Game_Process(); // 直接調用邏輯
// FPS 統計略
}
// 無 sleep,依賴平台調度
}
if (exitApp) { destroyApp(true); notifyDestroyed(); }
}
// pauseApp/destroyApp 置 running=false
}
timeinterval 設定目標幀間隔(如 20ms ≈ 50FPS),邏輯更新與渲染解耦。sleep(30) 非精確休眠,實際喚醒時間可能超過 30ms;採用「積累時間差」法更穩健:accumulator += delta; while (accumulator >= FIXED_DT) { update(FIXED_DT); accumulator -= FIXED_DT; }frames_per_second 便於調優。J2ME 遊戲主循環的核心挑戰在於平台差異導致的任務調度行為不一致。採用「Runnable 實現類 + 獨立線程 + 保守 sleep(30)」模式,可在主流機型(Nokia S40/S60、Motorola)上穩定運行。對於內存極其受限的 S40 低端機型,可退化為無線程模式,但需嚴格測試目標設備。