在虛擬時鐘 (Clock) 基礎上,**Scheduler** 負責:
waitUntil(virtualTime) 同步主循環與虛擬時間
public class Scheduler {
private final Clock clock = new Clock();
private final int FRAME_BUDGET_MS = 20; // 50 FPS
// 任務列表
private final List frameTasks = new ArrayList<>();
private final List timedTasks = new ArrayList<>(); // 按 triggerTime 有序
private Task renderTask;
// 動態質量控制
private boolean highQualityEnabled = true;
private static final int CPU_THROTTLE_THRESHOLD = 110; // %
private static final int CPU_BOOST_THRESHOLD = 70; // %
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 frameStartReal = clock.getSystemTime();
// 1. 定時任務 (按虛擬時間觸發)
processTimedTasks();
// 2. 幀任務 (按優先級)
processFrameTasks();
// 3. 渲染任務 (固定最後執行)
if (renderTask != null) renderTask.execute(clock.getVirtualTime());
// 4. 統計與自適應
int frameEndReal = clock.getSystemTime();
int realElapsed = frameEndReal - frameStartReal;
int virtualElapsed = clock.getFrameEnd() - clock.getFrameStart();
int cpuUsage = (realElapsed * 100) / Math.max(1, virtualElapsed);
System.out.println("elapsed: " + realElapsed + "ms, frame: " + virtualElapsed + "ms");
System.out.println("cpu usage: " + cpuUsage + "%");
// 動態質量調整
adaptQuality(cpuUsage);
clock.advanceToEnd(FRAME_BUDGET_MS);
cleanupExpiredTasks();
System.out.println("-- end execute frame --\n");
}
private void processTimedTasks() {
int vt = clock.getVirtualTime();
Iterator it = timedTasks.iterator();
while (it.hasNext()) {
TimedTask t = it.next();
if (t.triggerTime <= vt) {
if (t.execute(vt)) {
// 周期性任務:推進下次觸發時間
t.triggerTime += t.interval;
} else {
it.remove(); // 一次性任務移除
}
} else break; // 有序列表,後續未到期
}
}
private void processFrameTasks() {
// 優先級降序執行
for (Task t : frameTasks) {
t.execute(clock.getVirtualTime());
}
}
private void adaptQuality(int cpuUsage) {
if (cpuUsage > CPU_THROTTLE_THRESHOLD && highQualityEnabled) {
System.out.println("[THROTTLE] Disabling high-quality tasks");
disableHighQualityTasks();
highQualityEnabled = false;
} else if (cpuUsage < CPU_BOOST_THRESHOLD && !highQualityEnabled) {
System.out.println("[BOOST] Enabling high-quality tasks");
enableHighQualityTasks();
highQualityEnabled = true;
}
}
private void disableHighQualityTasks() {
// 示例:移除粒子系統、遠景更新等低優先級任務
frameTasks.removeIf(t -> t.getPriority() < Task.PRIORITY_NORMAL);
}
private void enableHighQualityTasks() {
// 重新註冊高質量任務 (需外部管理註冊表)
}
private void cleanupExpiredTasks() {
// 清理標記為完成的定時任務等
}
// 同步等待虛擬時間到達目標點 (主循環調用)
public void waitUntil(int targetVirtualTime) {
try {
while (clock.getVirtualTime() < targetVirtualTime) {
Thread.sleep(1); // MIDP 中精度 ~10-20ms
}
} catch (InterruptedException ignored) {}
}
// 測試主程序
public static void main(String[] args) {
Scheduler scheduler = new Scheduler();
scheduler.start();
// 模擬任務:定時任務 500ms, 幀任務 200ms
scheduler.addTimedTask(new TimedTask(0, 500, vt -> {
try { Thread.sleep(500); } catch (InterruptedException ignored) {}
return false; // 一次性
}));
scheduler.addFrameTask(vt -> {
try { Thread.sleep(200); } catch (InterruptedException ignored) {}
}, Task.PRIORITY_NORMAL);
int nextFrameTarget = 1000; // 每 1000ms 虛擬時間執行一幀
do {
scheduler.waitUntil(nextFrameTarget);
nextFrameTarget += 1000;
scheduler.executeFrame();
} while (scheduler.getSystemTime() < 10000);
}
}
// 基礎任務介面
interface Task {
int PRIORITY_LOW = 0, PRIORITY_NORMAL = 10, PRIORITY_HIGH = 20;
int getPriority();
boolean execute(int virtualTime); // true=保留, false=移除
}
// 定時任務
class TimedTask implements Task {
int triggerTime; // 虛擬時間觸發點
int interval; // 0=一次性, >0=周期間隔
IntConsumer action; // 函數式接口 (Java 8+), MIDP 用匿名類
TimedTask(int trigger, int interval, IntConsumer action) {
this.triggerTime = trigger;
this.interval = interval;
this.action = action;
}
public int getPriority() { return PRIORITY_HIGH; }
public boolean execute(int vt) { action.accept(vt); return interval > 0; }
public int getTriggerTime() { return triggerTime; }
}
-- start execute frame -- [beginFrame] virtual time = 0, systemTime = 1002 doing time tasks... (sleep 500ms) [advanceToEnd] virtual time = 992 doing frame tasks... (sleep 200ms) elapsed: 701, frame: 992 cpu usage: 70% -- end execute frame --
✅ 虛擬時間正常推進 (992ms ≈ 1000ms),遊戲速度正常。
-- start execute frame -- [beginFrame] virtual time = 0, systemTime = 1002 doing time tasks... (sleep 700ms) [advanceToEnd] virtual time = 992 doing frame tasks... (sleep 500ms) elapsed: 1201, frame: 992 cpu usage: 121% -- end execute frame -- -- start execute frame -- [beginFrame] virtual time = 992, systemTime = 2203 ... elapsed: 1202, frame: 1201 cpu usage: 100% -- end execute frame --
⚠️ 首幀超預算,虛擬時間推進受限 (992ms),第二幀起虛擬幀長自適應拉長至 1201ms,**遊戲整體變慢**,但邏輯幀順序不亂、無幀丟失。這正是「虛擬時間跟隨真實時間、超載時自動慢動作」的預期行為。
| 負載區間 | 策略 | 典型調整 |
|---|---|---|
| CPU < 70% | 質量提升 | 啟用粒子特效、高精度碰撞、動態陰影、遠景 LOD 0 |
| 70% ≤ CPU ≤ 110% | 穩定運行 | 維持當前質量等級 |
| CPU > 110% | 性能保護 | 禁用粒子、降低物理迭代、遠景 LOD 1/2、簡化 AI |
Vector 手動維護有序。System.nanoTime:全用 currentTimeMillis(),精度足夠 20ms 幀。TimedTask、Vector 節點預分配,避免幀間 GC。timeScale 用 int scaleNum, scaleDen 替代 float。
public void run() {
scheduler.start();
int target = scheduler.getVirtualTime() + FRAME_BUDGET_MS;
while (running) {
scheduler.waitUntil(target);
target += FRAME_BUDGET_MS;
scheduler.executeFrame();
// 處理系統事件 (來電、暫停) → scheduler.stop()/start()
}
}
Scheduler 將「幀」概念形式化為:**虛擬時間推進的原子單位**。通過 waitUntil → executeFrame → adaptQuality 閉環,實現了:
這套機制在資源受限的 J2ME 環境下經過實戰驗證,同樣適用於現代輕量級遊戲引擎核心調度層。