game state system

main
cosmonaut 2022-04-08 00:03:42 -07:00
parent b252d0eb92
commit 412f0ca179
2 changed files with 99 additions and 67 deletions

View File

@ -9,12 +9,11 @@ using System.Diagnostics;
namespace MoonWorks namespace MoonWorks
{ {
public abstract class Game public class Game
{ {
public TimeSpan MAX_DELTA_TIME = TimeSpan.FromMilliseconds(100); public TimeSpan MAX_DELTA_TIME = TimeSpan.FromMilliseconds(100);
private bool quit = false; private bool quit = false;
bool debugMode;
private Stopwatch gameTimer; private Stopwatch gameTimer;
private TimeSpan timestep; private TimeSpan timestep;
@ -32,6 +31,8 @@ namespace MoonWorks
public AudioDevice AudioDevice { get; } public AudioDevice AudioDevice { get; }
public Inputs Inputs { get; } public Inputs Inputs { get; }
private GameState GameState = null;
private Dictionary<PresentMode, RefreshCS.Refresh.PresentMode> moonWorksToRefreshPresentMode = new Dictionary<PresentMode, RefreshCS.Refresh.PresentMode> private Dictionary<PresentMode, RefreshCS.Refresh.PresentMode> moonWorksToRefreshPresentMode = new Dictionary<PresentMode, RefreshCS.Refresh.PresentMode>
{ {
{ PresentMode.Immediate, RefreshCS.Refresh.PresentMode.Immediate }, { PresentMode.Immediate, RefreshCS.Refresh.PresentMode.Immediate },
@ -74,71 +75,22 @@ namespace MoonWorks
); );
AudioDevice = new AudioDevice(); AudioDevice = new AudioDevice();
this.debugMode = debugMode;
} }
public void Run() public void Run()
{ {
#if DEBUG
if (GameState == null)
{
throw new NullReferenceException("Must call SetState before Run!");
}
#endif
while (!quit) while (!quit)
{ {
AdvanceElapsedTime(); Tick();
/* 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);
}
} }
OnDestroy();
AudioDevice.Dispose(); AudioDevice.Dispose();
GraphicsDevice.Dispose(); GraphicsDevice.Dispose();
Window.Dispose(); Window.Dispose();
@ -146,6 +98,68 @@ namespace MoonWorks
SDL.SDL_Quit(); 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() private void HandleSDLEvents()
{ {
while (SDL.SDL_PollEvent(out var _event) == 1) 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) private void HandleTextInput(SDL2.SDL.SDL_Event evt)
{ {
// Based on the SDL2# LPUtf8StrMarshaler // Based on the SDL2# LPUtf8StrMarshaler

26
src/GameState.cs Normal file
View File

@ -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);
}
}