From 412f0ca179c29bcb3fb32aefb567a11accd4520e Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 8 Apr 2022 00:03:42 -0700 Subject: [PATCH] game state system --- src/Game.cs | 140 ++++++++++++++++++++++++----------------------- src/GameState.cs | 26 +++++++++ 2 files changed, 99 insertions(+), 67 deletions(-) create mode 100644 src/GameState.cs diff --git a/src/Game.cs b/src/Game.cs index 8078e3a0..7d31dd2c 100644 --- a/src/Game.cs +++ b/src/Game.cs @@ -9,12 +9,11 @@ using System.Diagnostics; namespace MoonWorks { - public abstract class Game + public class Game { public TimeSpan MAX_DELTA_TIME = TimeSpan.FromMilliseconds(100); private bool quit = false; - bool debugMode; private Stopwatch gameTimer; private TimeSpan timestep; @@ -32,6 +31,8 @@ namespace MoonWorks public AudioDevice AudioDevice { get; } public Inputs Inputs { get; } + private GameState GameState = null; + private Dictionary moonWorksToRefreshPresentMode = new Dictionary { { PresentMode.Immediate, RefreshCS.Refresh.PresentMode.Immediate }, @@ -74,71 +75,22 @@ namespace MoonWorks ); AudioDevice = new AudioDevice(); - - this.debugMode = debugMode; } public void Run() { + #if DEBUG + if (GameState == null) + { + throw new NullReferenceException("Must call SetState before Run!"); + } + #endif + while (!quit) { - AdvanceElapsedTime(); - - /* We want to wait until the next frame, - * but we don't want to oversleep. Requesting repeated 1ms sleeps and - * seeing how long we actually slept for lets us estimate the worst case - * sleep precision so we don't oversleep the next frame. - */ - while (accumulatedElapsedTime + worstCaseSleepPrecision < timestep) - { - System.Threading.Thread.Sleep(1); - TimeSpan timeAdvancedSinceSleeping = AdvanceElapsedTime(); - UpdateEstimatedSleepPrecision(timeAdvancedSinceSleeping); - } - - /* Now that we have slept into the sleep precision threshold, we need to wait - * for just a little bit longer until the target elapsed time has been reached. - * SpinWait(1) works by pausing the thread for very short intervals, so it is - * an efficient and time-accurate way to wait out the rest of the time. - */ - while (accumulatedElapsedTime < timestep) - { - System.Threading.Thread.SpinWait(1); - AdvanceElapsedTime(); - } - - // Now that we are going to perform an update, let's handle SDL events. - HandleSDLEvents(); - - // Do not let any step take longer than our maximum. - if (accumulatedElapsedTime > MAX_DELTA_TIME) - { - accumulatedElapsedTime = MAX_DELTA_TIME; - } - - if (!quit) - { - while (accumulatedElapsedTime >= timestep) - { - Inputs.Mouse.Wheel = 0; - - Inputs.Update(); - AudioDevice.Update(); - - Update(timestep); - - accumulatedElapsedTime -= timestep; - } - - var alpha = accumulatedElapsedTime / timestep; - - Draw(timestep, alpha); - - } + Tick(); } - OnDestroy(); - AudioDevice.Dispose(); GraphicsDevice.Dispose(); Window.Dispose(); @@ -146,6 +98,68 @@ namespace MoonWorks SDL.SDL_Quit(); } + public void SetState(GameState gameState) + { + GameState = gameState; + GameState.Start(); + } + + private void Tick() + { + AdvanceElapsedTime(); + + /* We want to wait until the next frame, + * but we don't want to oversleep. Requesting repeated 1ms sleeps and + * seeing how long we actually slept for lets us estimate the worst case + * sleep precision so we don't oversleep the next frame. + */ + while (accumulatedElapsedTime + worstCaseSleepPrecision < timestep) + { + System.Threading.Thread.Sleep(1); + TimeSpan timeAdvancedSinceSleeping = AdvanceElapsedTime(); + UpdateEstimatedSleepPrecision(timeAdvancedSinceSleeping); + } + + /* Now that we have slept into the sleep precision threshold, we need to wait + * for just a little bit longer until the target elapsed time has been reached. + * SpinWait(1) works by pausing the thread for very short intervals, so it is + * an efficient and time-accurate way to wait out the rest of the time. + */ + while (accumulatedElapsedTime < timestep) + { + System.Threading.Thread.SpinWait(1); + AdvanceElapsedTime(); + } + + // Now that we are going to perform an update, let's handle SDL events. + HandleSDLEvents(); + + // Do not let any step take longer than our maximum. + if (accumulatedElapsedTime > MAX_DELTA_TIME) + { + accumulatedElapsedTime = MAX_DELTA_TIME; + } + + if (!quit) + { + while (accumulatedElapsedTime >= timestep) + { + Inputs.Mouse.Wheel = 0; + + Inputs.Update(); + AudioDevice.Update(); + + GameState.Update(timestep); + + accumulatedElapsedTime -= timestep; + } + + var alpha = accumulatedElapsedTime / timestep; + + GameState.Draw(timestep, alpha); + } + } + private void HandleSDLEvents() { while (SDL.SDL_PollEvent(out var _event) == 1) @@ -167,14 +181,6 @@ namespace MoonWorks } } - protected abstract void Update(TimeSpan dt); - - // alpha refers to a percentage value between the current and next state - protected abstract void Draw(TimeSpan dt, double alpha); - - // Clean up any objects you created in this function - protected abstract void OnDestroy(); - private void HandleTextInput(SDL2.SDL.SDL_Event evt) { // Based on the SDL2# LPUtf8StrMarshaler diff --git a/src/GameState.cs b/src/GameState.cs new file mode 100644 index 00000000..5a21c220 --- /dev/null +++ b/src/GameState.cs @@ -0,0 +1,26 @@ +using System; +using MoonWorks.Audio; +using MoonWorks.Graphics; +using MoonWorks.Input; + +namespace MoonWorks +{ + public abstract class GameState + { + protected readonly Game Game; + + public Window Window => Game.Window; + public GraphicsDevice GraphicsDevice => Game.GraphicsDevice; + public AudioDevice AudioDevice => Game.AudioDevice; + public Inputs Inputs => Game.Inputs; + + public GameState(Game game) + { + Game = game; + } + + public abstract void Start(); + public abstract void Update(TimeSpan delta); + public abstract void Draw(TimeSpan delta, double alpha); + } +}