--- id: wiki-2026-0508-fixed-time-step-vs-variable-time title: Fixed Time Step vs Variable Time Step category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Game Loop, Timestep, Fixed Update, Variable Update] duplicate_of: none source_trust_level: A confidence_score: 0.95 verification_status: applied tags: [game-design, game-loop, simulation, physics, determinism] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: cpp framework: game-loop --- # Fixed Time Step vs Variable Time Step ## 매 한 줄 > **"매 simulation 의 fixed Δt 의 advance, render 의 variable Δt 의 interpolate"**. 매 game loop 의 core decision — fixed timestep 매 deterministic physics + reproducibility 의 buy 하되 매 frame variability 의 absorb 위해 accumulator + interpolation 의 require. 2026 매 Glenn Fiedler "Fix Your Timestep!" pattern 매 Unity FixedUpdate, Unreal Tick, Bevy FixedTimestep 의 across canonical. ## 매 핵심 ### 매 Fixed Timestep - 매 simulation 매 constant Δt (e.g. 16.67ms = 60Hz) 의 always advance. - **Pros**: Deterministic, lock-step multiplayer-friendly, stable physics. - **Cons**: 매 spiral-of-death (frame slow → catch-up → slower → ...). ### 매 Variable Timestep - 매 simulation Δt 매 wall-clock frame time 의 follow. - **Pros**: Simpler, no accumulator, no interpolation. - **Cons**: Non-deterministic, physics tunneling, replay-incompatible. ### 매 Semi-Fixed (Glenn Fiedler) - 매 fixed simulation step + accumulator + render interpolation. - 매 industry standard 의 2026 — Unity, Unreal, Bevy 의 default. ### 매 응용 1. Physics-heavy game (driving, fighting) — fixed mandatory. 2. Lock-step multiplayer (RTS, fighting games) — fixed + deterministic math. 3. Casual single-player — variable acceptable. ## 💻 패턴 ### Naive variable timestep ```cpp double previous = now(); while (running) { double current = now(); double dt = current - previous; previous = current; update(dt); // 매 dt 매 frame 의 따라 fluctuate render(); } ``` ### Fixed timestep with accumulator ```cpp constexpr double DT = 1.0 / 60.0; // 16.67ms double accumulator = 0.0; double previous = now(); State current_state, previous_state; while (running) { double current = now(); double frameTime = std::min(current - previous, 0.25); // 매 spiral 의 cap previous = current; accumulator += frameTime; while (accumulator >= DT) { previous_state = current_state; integrate(current_state, DT); accumulator -= DT; } double alpha = accumulator / DT; State render_state = lerp(previous_state, current_state, alpha); render(render_state); } ``` ### Determinism-safe integration (fixed-point) ```cpp // 매 lock-step multiplayer — float drift 의 avoid struct Fixed64 { int64_t raw; // 매 32.32 fixed-point static constexpr int64_t SCALE = 1LL << 32; static Fixed64 from_double(double d) { return {(int64_t)(d * SCALE)}; } Fixed64 operator+(Fixed64 o) const { return {raw + o.raw}; } Fixed64 operator*(Fixed64 o) const { return {(raw * o.raw) >> 32}; } }; ``` ### Physics tunneling guard (continuous collision) ```cpp // 매 variable / large Δt 매 fast object 매 thin wall 의 tunnel bool sweepAABB(AABB a, Vec2 vel, AABB b, double dt, double& tHit) { Vec2 inv = { vel.x ? 1.0 / vel.x : 1e30, vel.y ? 1.0 / vel.y : 1e30 }; double tEnter = std::max( (b.min.x - a.max.x) * inv.x, (b.min.y - a.max.y) * inv.y ); double tExit = std::min( (b.max.x - a.min.x) * inv.x, (b.max.y - a.min.y) * inv.y ); if (tEnter > tExit || tEnter > dt || tEnter < 0) return false; tHit = tEnter; return true; } ``` ### Unity-style separation ```csharp public class Player : MonoBehaviour { public float speed = 5f; void Update() { // 매 input + visuals 의 variable Δt float h = Input.GetAxis("Horizontal"); // 매 input buffer 의 only X 매 visual extrapolation 의 do } void FixedUpdate() { // 매 physics 의 fixed Δt — Time.fixedDeltaTime rigidbody.MovePosition(transform.position + dir * speed * Time.fixedDeltaTime); } } ``` ### Bevy fixed timestep ```rust use bevy::prelude::*; use bevy::time::common_conditions::on_timer; use std::time::Duration; fn main() { App::new() .add_plugins(DefaultPlugins) .insert_resource(Time::::from_hz(60.0)) .add_systems(FixedUpdate, physics_step) .add_systems(Update, render_step) .run(); } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Lock-step multiplayer (RTS, fighting) | 매 fixed + fixed-point math 의 mandatory | | Physics-heavy single-player | 매 fixed + accumulator + interpolation | | Casual / turn-based | 매 variable timestep 의 acceptable | | Replay / netcode rollback | 매 fixed + deterministic | | Educational / prototype | 매 variable 의 simplicity | **기본값**: 매 Glenn Fiedler 의 fixed-with-accumulator + interpolation pattern. ## 🔗 Graph - 부모: [[Game Loop]] - 변형: [[Rollback Netcode]] - 응용: [[Physics]] · [[Beat Saber]] ## 🤖 LLM 활용 **언제**: Loop architecture sketch, accumulator boilerplate, framework migration plan. **언제 X**: Hard-realtime kernel scheduling, low-level platform timer tuning. ## ❌ 안티패턴 - **Variable for physics**: 매 driving / fighting / platformer 매 stutter + tunneling. - **No spiral cap**: 매 GPU stall 매 simulation 의 catch-up loop 의 lock. - **Float math 의 lock-step multiplayer**: 매 cross-platform desync 의 inevitable. - **Render-state == sim-state**: 매 interpolation 의 absent 매 visual stutter. ## 🧪 검증 / 중복 - Verified (Glenn Fiedler "Fix Your Timestep!" 2004/2018, Unity Manual, Unreal Tick docs, Bevy book 2025). - 신뢰도 A+. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Fix-Your-Timestep canonical pattern, Unity/Bevy examples |