while (running) {
update();
render();
}
缺陷:忽略時間,快機器跑得飛快,慢機器跑得慢,遊戲速度與硬體綁定。
const int SKIP_TICKS = 1000 / 60; // 60 UPS
int nextTick = GetTickCount();
while (running) {
update();
render();
int sleep = nextTick - GetTickCount();
if (sleep > 0) Sleep(sleep);
nextTick += SKIP_TICKS;
}
優點:邏輯固定 60 Hz,錄影回放簡單(固定幀間隔)。
缺點:複雜場景渲染慢 → `update` 被延遲 → 遊戲變慢(卡頓);簡單場景 → 遊戲加速再穩定;高速物體視覺殘影(強制 60 FPS)。
int last = GetTickCount();
while (running) {
int now = GetTickCount();
float dt = (now - last) / 1000.0f;
update(dt);
render();
last = now;
}
優點:渲染不阻塞邏輯,硬體性能全釋放。
缺點:複雜場景 → `dt` 大 → 物理穿透、數值不穩定;不同機器浮點誤差累積導致確定性失效(回放不同步)。
const int TICKS = 60; // 邏輯 60 Hz
const int SKIP = 1000 / TICKS;
const int MAX_FRAMESKIP = 5; // 最大追趕幀數
int nextTick = GetTickCount();
int loops;
float interpolation;
while (running) {
loops = 0;
while (GetTickCount() > nextTick && loops < MAX_FRAMESKIP) {
update();
nextTick += SKIP;
loops++;
}
interpolation = float(GetTickCount() + SKIP - nextTick) / SKIP;
render(interpolation);
}
優點:
- 邏輯**絕對固定 60 Hz**,物理穩定、確定性回放。
- 渲染**盡可能快**,高端機器享受高 FPS。
- **插值修正**:渲染時刻落在兩個邏輯幀之間,用 `interpolation` (0~1) 線性插值位置:
`view_pos = old_pos + (new_pos - old_pos) * interpolation`。
高速物體平滑移動,無殘影。
- 超過 `MAX_FRAMESKIP` 才真正降低邏輯頻率(遊戲變慢),保護低端機不死機。
邏輯幀: [10]----[11]----[12]----[13] (固定 16.67ms 間隔) 渲染時刻: ↑ t=10.3 插值因子: 0.3 渲染位置: pos10 + (pos11 - pos10) * 0.3
需在 `render(interpolation)` 內對所有可視物體應用插值預測位置。
| 場景 | 推薦模式 | 理由 |
|---|---|---|
| 手機/嵌入式/固定硬體 | 模式 2 (固定步長+Sleep) | 省電、實現簡單、行為確定 |
| PC/主機大型遊戲 | 模式 4 (固定邏輯+插值渲染) | 物理穩定、高幀率平滑、確定性回放 |
| 簡單 2D/原型/不需回放 | 模式 3 (變時間步長) | 代碼最少、硬體性能全用 |
| 教學/極簡 Demo | 模式 1 | 理解概念用,實戰禁用 |
void render(float interp) {
for (auto& obj : objects) {
// obj.oldPos 上一邏輯幀位置,obj.pos 當前邏輯幀位置
Vec2 viewPos = obj.oldPos + (obj.pos - obj.oldPos) * interp;
drawSprite(obj.sprite, viewPos);
}
}
// 邏輯幀更新時保存舊位置
void update() {
for (auto& obj : objects) {
obj.oldPos = obj.pos;
obj.pos += obj.vel * FIXED_DT; // 固定時間步長
}
}
**模式 4(固定邏輯步長 + 插值渲染)是工業級遊戲主循環的標準答案**。它同時滿足:物理確定性、高幀率視覺平滑、高低端硬體適應性、錄影回放確定性。唯一成本是需在渲染層實現插值預測,但收益遠大於成本。