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

遊戲虛擬時鐘實現:真實/系統/虛擬三時間域協同與暫停縮放機制

     編輯:J2ME

設計目標

為遊戲調度器提供**可控、可暫停、可縮放**的時間基線。區分三個時間域:

  • 硬件時間 (thisTime):System.currentTimeMillis() 原始值,單調遞增,不可控。
  • 系統時間 (systemTime):遊戲邏輯「牆鐘時間」,從 0 起算,與硬件時間同步,**暫停時凍結**。
  • 虛擬時間 (virtualTime):驅動遊戲邏輯(定時任務、動畫、物理)的時間,**可縮放、可暫停、可倒退**。

核心字段設計


class Clock {
    // 運行狀態
    private boolean running = false;
    
    // 硬件時間追蹤
    private int thisTime;      // 當前硬件時間
    private int lastTime;      // 上次更新硬件時間
    
    // 系統時間 (從 0 起算,暫停凍結)
    private int systemTime;    // 當前系統時間
    private int systemOffset;  // 硬件時間 → 系統時間 偏移量
    private int pauseAt;       // 暫停時刻的系統時間
    
    // 虛擬時間 (可縮放)
    private int virtualTime;   // 當前虛擬時間
    private int virtualOffset; // 暫停累積偏移
    private float timeScale = 1.0f; // 時間縮放因子
    
    // 幀邊界 (供調度器統計)
    private int frameStart;
    private int frameEnd;
    private int frameCount;
}

關鍵算法實現

1. 初始化/重置


public void reset() {
    running = false;
    thisTime = lastTime = (int) System.currentTimeMillis();
    systemTime = 0;
    systemOffset = thisTime;
    pauseAt = 0;
    virtualTime = 0;
    virtualOffset = 0;
    timeScale = 1.0f;
    frameStart = frameEnd = frameCount = 0;
}

2. 硬件時間同步 (每幀調用一次)


private void updateHardwareTime() {
    lastTime = thisTime;
    thisTime = (int) System.currentTimeMillis();
    if (running) {
        // 系統時間隨硬件時間推進
        systemTime += (thisTime - lastTime);
    }
}

3. 啟動/恢復


public void start() {
    if (!running) {
        running = true;
        updateHardwareTime();
        // 虛擬時間補償暫停期間流逝的系統時間
        virtualOffset += (systemTime - pauseAt);
        // 可選:重置幀計數器
        frameCount = 0;
    }
}

4. 暫停


public void stop() {
    if (running) {
        running = false;
        updateHardwareTime();
        pauseAt = systemTime; // 記錄暫停瞬間系統時間
    }
}

5. 幀邊界管理 (調度器調用)


// 幀開始
public void beginFrame() {
    updateHardwareTime();
    frameStart = systemTime;
    frameCount++;
}

// 幀結束:推進虛擬時間至幀預算末尾
public void advanceToEnd(int frameBudgetMs) {
    updateHardwareTime();
    frameEnd = systemTime;
    // 虛擬時間 = (系統時間 - 虛擬偏移) * 縮放因子
    virtualTime = (int) ((systemTime - virtualOffset) * timeScale);
}

6. 時間縮放 (慢動作/加速)


public void setTimeScale(float scale) {
    if (scale <= 0) scale = 0.01f; // 防止除零
    // 縮放前先同步當前虛擬時間
    updateHardwareTime();
    virtualTime = (int) ((systemTime - virtualOffset) * timeScale);
    // 應用新縮放,調整偏移保證虛擬時間連續
    timeScale = scale;
    virtualOffset = systemTime - (int) (virtualTime / timeScale);
}

7. 獲取器


public int getSystemTime() { 
    if (running) updateHardwareTime(); 
    return systemTime; 
}
public int getVirtualTime() { return virtualTime; }
public int getFrameStart() { return frameStart; }
public int getFrameEnd() { return frameEnd; }
public boolean isRunning() { return running; }

狀態機圖解

[RESET] → start() → [RUNNING] → stop() → [PAUSED] → start() → [RUNNING]
                ↑___________________________________________________|
  • RUNNING:三時間同步推進,virtualTime = (systemTime - virtualOffset) * scale
  • PAUSED:systemTime 凍結,virtualTime 凍結,硬件時間持續流逝
  • RESET:所有時間歸零,回到初始狀態

調度器集成示例


public class Scheduler {
    private Clock clock = new Clock();
    private final int FRAME_BUDGET = 20; // 50 FPS
    
    public void executeFrame() {
        clock.beginFrame();
        int startReal = clock.getSystemTime();
        
        // 執行任務...
        doTimeTasks(clock.getVirtualTime());
        doFrameTasks();
        doRenderTask();
        
        int endReal = clock.getSystemTime();
        clock.advanceToEnd(FRAME_BUDGET);
        
        // CPU 使用率 = 真實耗時 / 幀預算
        int cpu = (endReal - startReal) * 100 / FRAME_BUDGET;
        if (cpu > 100) enableFrameSkipping(); // 動態降級
    }
    
    public void waitUntil(int targetVirtualTime) {
        while (clock.getVirtualTime() < targetVirtualTime) {
            try { Thread.sleep(1); } catch (InterruptedException ignored) {}
        }
    }
}

MIDP 2.0 移植要點

  1. int 足夠:J2ME 無 long 性能優勢,32 位 int 可表示 ~24 天毫秒數,遊戲會話遠小於此。
  2. 無浮點:timeScale 用定點數 (Q16.16) 或分數 scaleNum/scaleDen 實現。
  3. sleep 精度:Thread.sleep(1) 實際精度 10-20ms,waitUntil 循環可接受。
  4. 序列化:暫存/恢復遊戲時持久化 systemOffset, virtualOffset, timeScale, pauseAt 即可完整恢復時間狀態。

總結

Clock 類將「時間」抽象為**可編程資源**:硬件時間提供物理基線,系統時間提供邏輯基線,虛擬時間提供遊戲基線。通過 virtualOffset 吸收暫停間隙,timeScale 實現時間縮放,beginFrame/advanceToEnd 界定幀邊界,構成調度器、網絡同步、回放系統、動畫引擎的統一時間基石。極簡 100 行代碼,承載遊戲時間的所有高級特性。

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