diff --git a/ShapeEngine/Core/GameDef/Game.cs b/ShapeEngine/Core/GameDef/Game.cs index b1b8b802..c5515583 100644 --- a/ShapeEngine/Core/GameDef/Game.cs +++ b/ShapeEngine/Core/GameDef/Game.cs @@ -111,7 +111,7 @@ public partial class Game /// This value is calculated as 1.0 / FixedPhysicsFramerate and represents /// the duration of each physics step in seconds. /// - public float FixedPhysicsTimestep { get; private set; } + public double FixedPhysicsTimestep { get; private set; } /// /// Gets the game time information for the variable update loop. @@ -348,7 +348,7 @@ private bool IsIdleFrameRateLimitActive() private readonly List shapeFlashes = []; private readonly List deferred = []; - private float physicsAccumulator; + private double physicsAccumulator; private List? customScreenTextures; #endregion @@ -540,7 +540,7 @@ public Game(GameSettings gameSettings, WindowSettings windowSettings, InputSetti { if (fixedFramerate < 30) fixedFramerate = 30; FixedPhysicsFramerate = fixedFramerate; - FixedPhysicsTimestep = 1f / FixedPhysicsFramerate; + FixedPhysicsTimestep = 1.0 / FixedPhysicsFramerate; FixedPhysicsEnabled = true; } diff --git a/ShapeEngine/Core/GameDef/GameGameloop.cs b/ShapeEngine/Core/GameDef/GameGameloop.cs index 5708a5b3..a309d49a 100644 --- a/ShapeEngine/Core/GameDef/GameGameloop.cs +++ b/ShapeEngine/Core/GameDef/GameGameloop.cs @@ -164,7 +164,8 @@ private void RunGameloop() if (FixedPhysicsEnabled) { ResolveUpdate(true); - AdvanceFixedUpdate(dt); + // Use double-precision frameDelta for more accurate fixed-step physics timing + AdvanceFixedUpdate(frameDelta); } else ResolveUpdate(false); @@ -341,25 +342,27 @@ private void GameTextureOnTextureResized(int w, int h) ResolveOnGameTextureResized(w, h); } - private void AdvanceFixedUpdate(float dt) + private void AdvanceFixedUpdate(double dt) { - const float maxFrameTime = 1f / 30f; - float frameTime = dt; - // var t = 0.0f; + const double maxFrameTime = 1.0 / 30.0; + double frameTime = dt; if (frameTime > maxFrameTime) frameTime = maxFrameTime; physicsAccumulator += frameTime; while (physicsAccumulator >= FixedPhysicsTimestep) { - FixedTime = FixedTime.TickF(FixedPhysicsFramerate); + FixedTime = FixedTime.Tick(FixedPhysicsTimestep); ResolveFixedUpdate(); // t += FixedPhysicsTimestep; physicsAccumulator -= FixedPhysicsTimestep; } - float alpha = physicsAccumulator / FixedPhysicsTimestep; - ResolveInterpolateFixedUpdate(alpha); + double alpha = physicsAccumulator / FixedPhysicsTimestep; + // alpha is computed in double precision for timing accuracy, but interpolation + // uses float because ResolveInterpolateFixedUpdate (e.g., via IUpdateable) has + // a float-based public API. This precision downgrade is intentional. + ResolveInterpolateFixedUpdate((float)alpha); } } \ No newline at end of file diff --git a/ShapeEngine/Core/Structs/GameTime.cs b/ShapeEngine/Core/Structs/GameTime.cs index bbb511f8..51213274 100644 --- a/ShapeEngine/Core/Structs/GameTime.cs +++ b/ShapeEngine/Core/Structs/GameTime.cs @@ -107,6 +107,15 @@ public GameTime(double totalSeconds, int totalFrames, double elapsedSeconds) /// public readonly float Delta => (float)ElapsedSeconds; + /// + /// Gets the elapsed time since the last frame as a double-precision value. + /// + /// + /// Use when double precision is required instead of the single-precision . + /// This property simply returns the underlying value. + /// + public readonly double DeltaDouble => ElapsedSeconds; + /// /// Gets the current frames per second (FPS) based on the elapsed time between frames. ///