From 3b1a1fcb311c2c5ad4e558b7f41913b62f3cab8d Mon Sep 17 00:00:00 2001
From: Nik <ighosta9@gmail.com>
Date: Fri, 13 Oct 2023 19:18:13 +0300
Subject: [PATCH] feat: fixed time step

---
 .../Components/ComponentsCompositionRoot.cs   |  2 +-
 .../CompositionRoots/GameCompositionRoot.cs   |  5 ++++-
 src/Apps/GameDesktop/Game.cs                  | 21 +++++++++++++++----
 src/Libs/Systems/CollisionSystem.cs           |  2 ++
 src/Libs/Systems/InputSystem.cs               |  8 ++++---
 src/Libs/Systems/MovementSystem.cs            |  5 ++---
 6 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs
index 0ebef32..e1cc3a0 100644
--- a/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs
+++ b/src/Apps/GameDesktop/CompositionRoots/Components/ComponentsCompositionRoot.cs
@@ -58,7 +58,7 @@ private static void RegisterTransformComponent(IServiceRegistry serviceRegistry)
     {
         serviceRegistry.RegisterSingleton(_ =>
         {
-            return new TransformComponent { Position = new(16, 20) };
+            return new TransformComponent { Position = new(16, 6) };
         }, "Player");
 
         serviceRegistry.RegisterSingleton(_ =>
diff --git a/src/Apps/GameDesktop/CompositionRoots/GameCompositionRoot.cs b/src/Apps/GameDesktop/CompositionRoots/GameCompositionRoot.cs
index 2d2d1fa..53584e4 100644
--- a/src/Apps/GameDesktop/CompositionRoots/GameCompositionRoot.cs
+++ b/src/Apps/GameDesktop/CompositionRoots/GameCompositionRoot.cs
@@ -7,13 +7,16 @@ namespace GameDesktop.CompositionRoots;
 
 internal class GameCompositionRoot : ICompositionRoot
 {
+    private const float TargetFramesPerSecond = 120.0f;
     private const bool IsMouseVisible = true;
 
     public void Compose(IServiceRegistry serviceRegistry)
     {
         serviceRegistry.Register(factory =>
         {
-            Game game = new(factory.GetInstance<ILogger>(), factory.GetInstance<IServiceContainer>())
+            Game game = new(factory.GetInstance<ILogger>(),
+                factory.GetInstance<IServiceContainer>(),
+                TargetFramesPerSecond)
             {
                 IsMouseVisible = IsMouseVisible, Content = { RootDirectory = AppVariable.ContentRootDirectory, }
             };
diff --git a/src/Apps/GameDesktop/Game.cs b/src/Apps/GameDesktop/Game.cs
index fbe5e3a..c1b48b7 100644
--- a/src/Apps/GameDesktop/Game.cs
+++ b/src/Apps/GameDesktop/Game.cs
@@ -1,4 +1,5 @@
-using Features;
+using System;
+using Features;
 using GameDesktop.CompositionRoots.Features;
 using LightInject;
 using Microsoft.Xna.Framework;
@@ -9,17 +10,21 @@ namespace GameDesktop;
 
 public class Game : Microsoft.Xna.Framework.Game
 {
+    private readonly float _targetTimeStep;
+
     private readonly ILogger _logger;
     private readonly IServiceContainer _container;
 
     private Entitas.Extended.Feature _rootFeature;
     private SpriteBatch _spriteBatch;
+    private float _accumulatedTime;
 
 
-    public Game(ILogger logger, IServiceContainer container)
+    public Game(ILogger logger, IServiceContainer container, float targetFramesPerSecond)
     {
         _logger = logger;
         _container = container;
+        _targetTimeStep = 1 / targetFramesPerSecond;
 
         _logger.ForContext<Game>().Verbose("ctor");
     }
@@ -75,11 +80,19 @@ protected override void EndRun()
         _logger.ForContext<Game>().Verbose("Ended");
     }
 
-    private void FixedUpdate(GameTime fixedGameTime) => _rootFeature.FixedExecute(fixedGameTime);
+    // maybe fixed update is incorrect. needs review in the future
+    private void FixedUpdate(GameTime gameTime) => _rootFeature.FixedExecute(gameTime);
 
     protected override void Update(GameTime gameTime)
     {
-        FixedUpdate(gameTime);
+        _accumulatedTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+        while (_accumulatedTime >= _targetTimeStep)
+        {
+            FixedUpdate(gameTime);
+
+            _accumulatedTime -= _targetTimeStep;
+        }
 
         base.Update(gameTime);
         _rootFeature.Execute(gameTime);
diff --git a/src/Libs/Systems/CollisionSystem.cs b/src/Libs/Systems/CollisionSystem.cs
index 01c3235..310c08a 100644
--- a/src/Libs/Systems/CollisionSystem.cs
+++ b/src/Libs/Systems/CollisionSystem.cs
@@ -5,6 +5,8 @@
 
 namespace Systems;
 
+// Input & Collision systems both have to be fixed execute systems,
+// otherwise it'll lead to the desynchronized behaviour.
 public class CollisionSystem : IFixedExecuteSystem
 {
     private readonly IGroup<GameEntity> _group;
diff --git a/src/Libs/Systems/InputSystem.cs b/src/Libs/Systems/InputSystem.cs
index 8e4e755..d6f5289 100644
--- a/src/Libs/Systems/InputSystem.cs
+++ b/src/Libs/Systems/InputSystem.cs
@@ -1,13 +1,15 @@
 using Entitas;
+using Entitas.Extended;
 using Microsoft.Xna.Framework;
 using Serilog;
 using Services.Input;
-using IExecuteSystem = Entitas.Extended.IExecuteSystem;
 
 namespace Systems;
 
+// Input & Collision systems both have to be fixed execute systems,
+// otherwise it'll lead to the desynchronized behaviour.
 [Input]
-public class InputSystem : IExecuteSystem
+public class InputSystem : IFixedExecuteSystem
 {
     private readonly IInputScanner _inputScanner;
     private readonly IGroup<GameEntity> _group;
@@ -20,7 +22,7 @@ public InputSystem(IInputScanner inputScanner, IGroup<GameEntity> group, ILogger
         _logger = logger;
     }
 
-    public void Execute(GameTime gameTime)
+    public void FixedExecute(GameTime gameTime)
     {
         try
         {
diff --git a/src/Libs/Systems/MovementSystem.cs b/src/Libs/Systems/MovementSystem.cs
index e27e4e7..30ed296 100644
--- a/src/Libs/Systems/MovementSystem.cs
+++ b/src/Libs/Systems/MovementSystem.cs
@@ -7,8 +7,7 @@
 
 namespace Systems;
 
-// TODO: correct fixed execution in the game loop
-public class MovementSystem : IFixedExecuteSystem
+public class MovementSystem : IExecuteSystem
 {
     private readonly IGroup<GameEntity> _group;
     private readonly IMovement _movement;
@@ -21,7 +20,7 @@ public MovementSystem(IMovement movement, IGroup<GameEntity> group, ILogger logg
         _logger = logger;
     }
 
-    public void FixedExecute(GameTime fixedGameTime)
+    public void Execute(GameTime fixedGameTime)
     {
         try
         {