2021-01-20 03:34:26 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using SDL2;
|
2021-01-20 02:06:10 +00:00
|
|
|
|
using MoonWorks.Audio;
|
2021-01-20 03:34:26 +00:00
|
|
|
|
using MoonWorks.Graphics;
|
2021-01-22 08:20:07 +00:00
|
|
|
|
using MoonWorks.Input;
|
2021-01-25 21:02:35 +00:00
|
|
|
|
using MoonWorks.Window;
|
2021-03-25 22:57:26 +00:00
|
|
|
|
using System.Text;
|
2021-01-19 07:29:07 +00:00
|
|
|
|
|
|
|
|
|
namespace MoonWorks
|
|
|
|
|
{
|
|
|
|
|
public abstract class Game
|
|
|
|
|
{
|
2021-01-23 07:43:48 +00:00
|
|
|
|
public const double MAX_DELTA_TIME = 0.1;
|
|
|
|
|
|
2021-01-19 07:29:07 +00:00
|
|
|
|
private bool quit = false;
|
|
|
|
|
private double timestep;
|
|
|
|
|
ulong currentTime = SDL.SDL_GetPerformanceCounter();
|
|
|
|
|
double accumulator = 0;
|
2021-01-19 19:24:23 +00:00
|
|
|
|
bool debugMode;
|
2021-01-19 07:29:07 +00:00
|
|
|
|
|
2021-01-25 21:02:35 +00:00
|
|
|
|
public OSWindow Window { get; }
|
2021-01-19 07:29:07 +00:00
|
|
|
|
public GraphicsDevice GraphicsDevice { get; }
|
2021-01-20 02:06:10 +00:00
|
|
|
|
public AudioDevice AudioDevice { get; }
|
2021-01-22 08:20:07 +00:00
|
|
|
|
public Inputs Inputs { get; }
|
2021-01-19 07:29:07 +00:00
|
|
|
|
|
|
|
|
|
private Dictionary<PresentMode, RefreshCS.Refresh.PresentMode> moonWorksToRefreshPresentMode = new Dictionary<PresentMode, RefreshCS.Refresh.PresentMode>
|
|
|
|
|
{
|
|
|
|
|
{ PresentMode.Immediate, RefreshCS.Refresh.PresentMode.Immediate },
|
|
|
|
|
{ PresentMode.Mailbox, RefreshCS.Refresh.PresentMode.Mailbox },
|
|
|
|
|
{ PresentMode.FIFO, RefreshCS.Refresh.PresentMode.FIFO },
|
|
|
|
|
{ PresentMode.FIFORelaxed, RefreshCS.Refresh.PresentMode.FIFORelaxed }
|
|
|
|
|
};
|
|
|
|
|
|
2021-01-19 19:24:23 +00:00
|
|
|
|
public Game(
|
|
|
|
|
WindowCreateInfo windowCreateInfo,
|
|
|
|
|
PresentMode presentMode,
|
|
|
|
|
int targetTimestep = 60,
|
|
|
|
|
bool debugMode = false
|
|
|
|
|
) {
|
2021-01-19 07:29:07 +00:00
|
|
|
|
timestep = 1.0 / targetTimestep;
|
|
|
|
|
|
|
|
|
|
if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_TIMER | SDL.SDL_INIT_GAMECONTROLLER) < 0)
|
|
|
|
|
{
|
|
|
|
|
System.Console.WriteLine("Failed to initialize SDL!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-20 02:06:10 +00:00
|
|
|
|
Logger.Initialize();
|
|
|
|
|
|
2021-01-22 08:20:07 +00:00
|
|
|
|
Inputs = new Inputs();
|
2021-01-19 07:29:07 +00:00
|
|
|
|
|
2021-01-25 21:02:35 +00:00
|
|
|
|
Window = new OSWindow(windowCreateInfo);
|
2021-01-19 07:29:07 +00:00
|
|
|
|
|
2021-01-19 19:24:23 +00:00
|
|
|
|
GraphicsDevice = new GraphicsDevice(
|
|
|
|
|
Window.Handle,
|
|
|
|
|
moonWorksToRefreshPresentMode[presentMode],
|
|
|
|
|
debugMode
|
|
|
|
|
);
|
|
|
|
|
|
2021-01-20 02:06:10 +00:00
|
|
|
|
AudioDevice = new AudioDevice();
|
|
|
|
|
|
2021-01-19 19:24:23 +00:00
|
|
|
|
this.debugMode = debugMode;
|
2021-01-19 07:29:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Run()
|
|
|
|
|
{
|
|
|
|
|
while (!quit)
|
|
|
|
|
{
|
|
|
|
|
var newTime = SDL.SDL_GetPerformanceCounter();
|
|
|
|
|
double frameTime = (newTime - currentTime) / (double)SDL.SDL_GetPerformanceFrequency();
|
|
|
|
|
|
2021-01-23 07:43:48 +00:00
|
|
|
|
if (frameTime > MAX_DELTA_TIME)
|
2021-01-19 07:29:07 +00:00
|
|
|
|
{
|
2021-01-23 07:43:48 +00:00
|
|
|
|
frameTime = MAX_DELTA_TIME;
|
2021-01-19 07:29:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentTime = newTime;
|
|
|
|
|
|
|
|
|
|
accumulator += frameTime;
|
|
|
|
|
|
|
|
|
|
if (!quit)
|
|
|
|
|
{
|
|
|
|
|
while (accumulator >= timestep)
|
|
|
|
|
{
|
2021-03-26 19:20:05 +00:00
|
|
|
|
Inputs.Mouse.Wheel = 0;
|
|
|
|
|
|
2021-01-20 04:23:09 +00:00
|
|
|
|
HandleSDLEvents();
|
|
|
|
|
|
2021-01-22 08:20:07 +00:00
|
|
|
|
Inputs.Update();
|
2021-01-20 05:33:25 +00:00
|
|
|
|
AudioDevice.Update();
|
2021-01-19 07:29:07 +00:00
|
|
|
|
|
|
|
|
|
Update(timestep);
|
|
|
|
|
|
|
|
|
|
accumulator -= timestep;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 21:02:35 +00:00
|
|
|
|
double alpha = accumulator / timestep;
|
|
|
|
|
|
2021-01-26 22:09:10 +00:00
|
|
|
|
Draw(timestep, alpha);
|
2021-01-19 07:29:07 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-20 04:23:09 +00:00
|
|
|
|
private void HandleSDLEvents()
|
|
|
|
|
{
|
|
|
|
|
while (SDL.SDL_PollEvent(out var _event) == 1)
|
|
|
|
|
{
|
|
|
|
|
switch (_event.type)
|
|
|
|
|
{
|
|
|
|
|
case SDL.SDL_EventType.SDL_QUIT:
|
|
|
|
|
quit = true;
|
|
|
|
|
break;
|
2021-03-25 22:57:26 +00:00
|
|
|
|
|
|
|
|
|
case SDL.SDL_EventType.SDL_TEXTINPUT:
|
|
|
|
|
HandleTextInput(_event);
|
|
|
|
|
break;
|
2021-03-26 19:20:05 +00:00
|
|
|
|
|
|
|
|
|
case SDL.SDL_EventType.SDL_MOUSEWHEEL:
|
|
|
|
|
Inputs.Mouse.Wheel += _event.wheel.y;
|
|
|
|
|
break;
|
2021-01-20 04:23:09 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-19 07:29:07 +00:00
|
|
|
|
protected abstract void Update(double dt);
|
|
|
|
|
|
2021-01-25 21:02:35 +00:00
|
|
|
|
protected abstract void Draw(double dt, double alpha);
|
2021-03-25 22:57:26 +00:00
|
|
|
|
|
|
|
|
|
private void HandleTextInput(SDL2.SDL.SDL_Event evt)
|
|
|
|
|
{
|
|
|
|
|
// Based on the SDL2# LPUtf8StrMarshaler
|
|
|
|
|
unsafe
|
|
|
|
|
{
|
|
|
|
|
int bytes = MeasureStringLength(evt.text.text);
|
|
|
|
|
if (bytes > 0)
|
|
|
|
|
{
|
|
|
|
|
/* UTF8 will never encode more characters
|
|
|
|
|
* than bytes in a string, so bytes is a
|
|
|
|
|
* suitable upper estimate of size needed
|
|
|
|
|
*/
|
|
|
|
|
char* charsBuffer = stackalloc char[bytes];
|
|
|
|
|
int chars = Encoding.UTF8.GetChars(
|
|
|
|
|
evt.text.text,
|
|
|
|
|
bytes,
|
|
|
|
|
charsBuffer,
|
|
|
|
|
bytes
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < chars; i += 1)
|
|
|
|
|
{
|
|
|
|
|
Inputs.OnTextInput(charsBuffer[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private unsafe static int MeasureStringLength(byte* ptr)
|
|
|
|
|
{
|
|
|
|
|
int bytes;
|
|
|
|
|
for (bytes = 0; *ptr != 0; ptr += 1, bytes += 1);
|
|
|
|
|
return bytes;
|
|
|
|
|
}
|
2021-01-19 07:29:07 +00:00
|
|
|
|
}
|
|
|
|
|
}
|