Scheduler 職責
- 以固定頻率驅動每一幀更新與渲染。
- 封裝
Clock 計時器,提供系統時間與虛擬時間。
- 統計每幀耗時、CPU 佔用率,支援動態負載調整。
核心程式碼 (參考《Game Programming Gems 3》C++ 實作)
package game.engine.core;
public class Scheduler {
private Clock clock = new Clock();
public void start() { clock.start(); }
public void stop() { clock.stop(); }
public int getSystemTime() { return clock.getSystemTime(); }
public int getVirtualTime() { return clock.getVirtualTime(); }
// 執行完整一幀
public void executeFrame() {
System.out.println("-- start execute frame --");
clock.beginFrame();
int started = clock.getSystemTime();
// 1. 時間驅動任務 (物理、AI、邏輯更新)
System.out.println("doing time tasks...");
try { Thread.sleep(500); } catch (InterruptedException ignored) {}
clock.advanceToEnd(); // 推進虛擬時間到幀末
// 2. 幀任務 (渲染準備、動畫幀推進)
System.out.println("doing frame tasks...");
try { Thread.sleep(200); } catch (InterruptedException ignored) {}
// 3. 渲染任務與統計
int end = clock.getSystemTime();
int elapsed = end - started; // 實際耗時 (ms)
int frameLength = clock.getFrameEnd() - clock.getFrameStart(); // 目標幀長
System.out.println("elapsed: " + elapsed + ", frame: " + frameLength);
System.out.println("cpu usage: " + (elapsed * 100 / frameLength) + "%");
System.out.println("-- end execute frame --");
}
}
Clock 輔助類別關鍵介面
public class Clock {
private long frameStart, frameEnd, virtualTime;
private boolean running;
public void start() { running = true; virtualTime = 0; }
public void stop() { running = false; }
public void beginFrame() { frameStart = System.currentTimeMillis(); }
public void advanceToEnd() { frameEnd = System.currentTimeMillis(); virtualTime += (frameEnd - frameStart); }
public int getSystemTime() { return (int) System.currentTimeMillis(); }
public int getVirtualTime() { return (int) virtualTime; }
public int getFrameStart() { return (int) frameStart; }
public int getFrameEnd() { return (int) frameEnd; }
public int getFrameLength() { return (int) (frameEnd - frameStart); }
}
設計要點與擴充
- 固定時間步長:
frameLength 通常設 16ms (60 FPS) 或 33ms (30 FPS)。若 elapsed > frameLength 需決定:丟幀、追趕、或降級畫質。
- 虛擬時間:遊戲邏輯使用
virtualTime,暫停/加速/慢動作只需調整虛擬時間增量,不影響真實時間。
- CPU 使用率監控:
elapsed * 100 / frameLength 超過 80% 可觸發降級(減少粒子、簡化 AI)。
- 執行緒模型:MIDP 2.0
GameCanvas 通常在單獨執行緒 run() 中迴圈呼叫 scheduler.executeFrame(),再 flushGraphics()。
整合到 GameCanvas 範例
public class GameLoop extends GameCanvas implements Runnable {
private Scheduler scheduler = new Scheduler();
public void run() {
scheduler.start();
while (running) {
scheduler.executeFrame();
// 實際渲染程式碼...
flushGraphics();
// 若提前完成可 sleep 微調
}
}
}