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 {