diff --git a/Examples/Example0_Sandbox/Example0_Sandbox.csproj b/Examples/Example0_Sandbox/Example0_Sandbox.csproj
new file mode 100644
index 0000000..5f39dfd
--- /dev/null
+++ b/Examples/Example0_Sandbox/Example0_Sandbox.csproj
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+ Exe
+ netcoreapp3.1
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/Example0_Sandbox/MainGame.cs b/Examples/Example0_Sandbox/MainGame.cs
new file mode 100644
index 0000000..85d58e3
--- /dev/null
+++ b/Examples/Example0_Sandbox/MainGame.cs
@@ -0,0 +1,68 @@
+using Microsoft.Extensions.Logging;
+using SharpDL;
+using SharpDL.Graphics;
+
+namespace Example0_Sandbox
+{
+ public class MainGame : IGame
+ {
+ private readonly ILogger logger;
+ private IGameEngine engine;
+ private Window window;
+ private Renderer renderer;
+
+ public MainGame(
+ IGameEngine engine,
+ ILogger logger = null)
+ {
+ this.engine = engine;
+ this.logger = logger;
+ engine.Initialize = () => Initialize();
+ engine.LoadContent = () => LoadContent();
+ engine.Update = (gameTime) => Update(gameTime);
+ engine.Draw = (gameTime) => Draw(gameTime);
+ engine.UnloadContent = () => UnloadContent();
+ }
+
+ public void Run()
+ {
+ engine.Start(InitializeType.Everything);
+ }
+
+ /// Initialize SDL and any sub-systems. Window and Renderer must be initialized before use.
+ ///
+ private void Initialize()
+ {
+ window = engine.WindowFactory.CreateWindow("Example 0 - Sandbox");
+ renderer = engine.RendererFactory.CreateRenderer(window);
+ renderer.SetRenderLogicalSize(1152, 720);
+ }
+
+ /// Load any game assets such as textures and audio.
+ ///
+ private void LoadContent()
+ {
+ }
+
+ /// Update the state of the game.
+ ///
+ ///
+ private void Update(GameTime gameTime)
+ {
+ }
+
+ /// Render the current state of the game.
+ ///
+ ///
+ private void Draw(GameTime gameTime)
+ {
+ renderer.RenderPresent();
+ }
+
+ /// Unload and dispose of any assets. Remember to dispose SDL-native objects!
+ ///
+ private void UnloadContent()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/Examples/Example0_Sandbox/Program.cs b/Examples/Example0_Sandbox/Program.cs
new file mode 100644
index 0000000..fec547b
--- /dev/null
+++ b/Examples/Example0_Sandbox/Program.cs
@@ -0,0 +1,37 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Console;
+using SharpDL;
+using SharpDL.Shared;
+
+namespace Example0_Sandbox
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ ServiceProvider serviceProvider = GetServiceProvider();
+ var game = serviceProvider.GetService();
+ game.Run();
+ }
+
+ private static ServiceProvider GetServiceProvider()
+ {
+ var services = new ServiceCollection();
+ ConfigureServices(services);
+ var serviceProvider = services.BuildServiceProvider();
+ return serviceProvider;
+ }
+
+ private static void ConfigureServices(ServiceCollection services)
+ {
+ services.AddSharpGame()
+ .AddLogging(config => {
+ config.AddConsole();
+ })
+ .Configure(options => {
+ options.AddFilter(null, LogLevel.Trace);
+ });
+ }
+ }
+}
diff --git a/Examples/SharpDL_Examples.sln b/Examples/SharpDL_Examples.sln
index bd2c6dd..31aef26 100644
--- a/Examples/SharpDL_Examples.sln
+++ b/Examples/SharpDL_Examples.sln
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example1_BlankWindow", "Exa
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example2_DrawTexture", "Example2_DrawTexture\Example2_DrawTexture.csproj", "{A877AE4B-FFF1-4447-BE1D-7D18CE034F58}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example0_Sandbox", "Example0_Sandbox\Example0_Sandbox.csproj", "{9368165A-A423-44BC-AE32-709C79848CB5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -31,6 +33,14 @@ Global
{A877AE4B-FFF1-4447-BE1D-7D18CE034F58}.Release|Any CPU.Build.0 = Release|Any CPU
{A877AE4B-FFF1-4447-BE1D-7D18CE034F58}.Release|x64.ActiveCfg = Release|Any CPU
{A877AE4B-FFF1-4447-BE1D-7D18CE034F58}.Release|x64.Build.0 = Release|Any CPU
+ {9368165A-A423-44BC-AE32-709C79848CB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9368165A-A423-44BC-AE32-709C79848CB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9368165A-A423-44BC-AE32-709C79848CB5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9368165A-A423-44BC-AE32-709C79848CB5}.Debug|x64.Build.0 = Debug|Any CPU
+ {9368165A-A423-44BC-AE32-709C79848CB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9368165A-A423-44BC-AE32-709C79848CB5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9368165A-A423-44BC-AE32-709C79848CB5}.Release|x64.ActiveCfg = Release|Any CPU
+ {9368165A-A423-44BC-AE32-709C79848CB5}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SharpDL/Events/EventManager.cs b/SharpDL/Events/EventManager.cs
new file mode 100644
index 0000000..eb8fd9e
--- /dev/null
+++ b/SharpDL/Events/EventManager.cs
@@ -0,0 +1,164 @@
+using System;
+using SDL2;
+using SharpDL.Input;
+
+namespace SharpDL.Events
+{
+ public class EventManager : IEventManager
+ {
+ public event EventHandler MouseWheelScrolling;
+
+ public event EventHandler MouseButtonPressed;
+
+ public event EventHandler MouseButtonReleased;
+
+ public event EventHandler MouseMoving;
+
+ public event EventHandler TextInputting;
+
+ public event EventHandler TextEditing;
+
+ public event EventHandler KeyPressed;
+
+ public event EventHandler KeyReleased;
+
+ public event EventHandler VideoDeviceSystemEvent;
+
+ public event EventHandler Quitting;
+
+ public event EventHandler Exiting;
+
+ public event EventHandler WindowShown;
+
+ public event EventHandler WindowHidden;
+
+ public event EventHandler WindowExposed;
+
+ public event EventHandler WindowMoved;
+
+ public event EventHandler WindowResized;
+
+ public event EventHandler WindowSizeChanged;
+
+ public event EventHandler WindowMinimized;
+
+ public event EventHandler WindowMaximized;
+
+ public event EventHandler WindowRestored;
+
+ public event EventHandler WindowEntered;
+
+ public event EventHandler WindowLeave;
+
+ public event EventHandler WindowFocusGained;
+
+ public event EventHandler WindowFocusLost;
+
+ public event EventHandler WindowClosed;
+
+ /// Raises the Exiting event. Exiting occurs when an unrecoverable exception occurs or the
+ /// user directly exits the game.
+ /// ///
+ ///
+ ///
+ public void RaiseExiting(object sender, EventArgs args)
+ {
+ RaiseEvent(Exiting, args);
+ }
+
+ public void RaiseEvent(SDL.SDL_Event rawEvent)
+ {
+ var eventType = (GameEventType)rawEvent.type;
+ switch(eventType)
+ {
+ case GameEventType.First:
+ return;
+ case GameEventType.Window:
+ var windowEventType = (WindowEventType)rawEvent.window.windowEvent;
+ switch(windowEventType)
+ {
+ case WindowEventType.Close: RaiseEvent(WindowClosed, rawEvent); break;
+ case WindowEventType.Enter: RaiseEvent(WindowEntered, rawEvent); break;
+ case WindowEventType.Exposed: RaiseEvent(WindowExposed, rawEvent); break;
+ case WindowEventType.FocusGained: RaiseEvent(WindowFocusGained, rawEvent); break;
+ case WindowEventType.FocusLost: RaiseEvent(WindowFocusLost, rawEvent); break;
+ case WindowEventType.Hidden: RaiseEvent(WindowHidden, rawEvent); break;
+ case WindowEventType.Leave: RaiseEvent(WindowLeave, rawEvent); break;
+ case WindowEventType.Maximized: RaiseEvent(WindowMaximized, rawEvent); break;
+ case WindowEventType.Minimized: RaiseEvent(WindowMinimized, rawEvent); break;
+ case WindowEventType.Moved: RaiseEvent(WindowMoved, rawEvent); break;
+ case WindowEventType.Resized: RaiseEvent(WindowResized, rawEvent); break;
+ case WindowEventType.Restored: RaiseEvent(WindowRestored, rawEvent); break;
+ case WindowEventType.Shown: RaiseEvent(WindowShown, rawEvent); break;
+ case WindowEventType.SizeChanged: RaiseEvent(WindowSizeChanged, rawEvent); break;
+ }
+ break;
+ case GameEventType.Quit:
+ RaiseEvent(Quitting, rawEvent); break;
+ case GameEventType.VideoDeviceSystemEvent:
+ RaiseEvent(VideoDeviceSystemEvent, rawEvent); break;
+ case GameEventType.TextEditing:
+ RaiseEvent(TextEditing, rawEvent); break;
+ case GameEventType.TextInput:
+ RaiseEvent(TextInputting, rawEvent); break;
+ case GameEventType.KeyDown:
+ case GameEventType.KeyUp:
+ var keyState = (KeyState)rawEvent.key.state;
+ if (keyState == KeyState.Pressed)
+ RaiseEvent(KeyPressed, rawEvent);
+ else if (keyState == KeyState.Released)
+ RaiseEvent(KeyReleased, rawEvent);
+ break;
+ case GameEventType.MouseMotion:
+ Mouse.UpdateMousePosition(rawEvent.motion.x, rawEvent.motion.y);
+ RaiseEvent(MouseMoving, rawEvent);
+ break;
+ case GameEventType.MouseButtonDown:
+ case GameEventType.MouseButtonUp:
+ var mouseButtonState = (MouseButtonState)rawEvent.button.state;
+ if (mouseButtonState == MouseButtonState.Pressed)
+ RaiseEvent(MouseButtonPressed, rawEvent);
+ else if (mouseButtonState == MouseButtonState.Released)
+ RaiseEvent(MouseButtonReleased, rawEvent);
+ break;
+ case GameEventType.MouseWheel:
+ RaiseEvent(MouseWheelScrolling, rawEvent); break;
+ }
+ }
+
+ /// Raises an event handler with arguments parsed from the SDL event parameter.
+ /// This method does nothing if there are no event subscribers.
+ ///
+ ///
+ ///
+ ///
+ private void RaiseEvent(EventHandler eventHandler, SDL.SDL_Event rawEvent)
+ where T : EventArgs
+ {
+ var eventArgs = CreateEventArgs(rawEvent);
+ RaiseEvent(eventHandler, eventArgs);
+ }
+
+ /// Raises an event handler using the event arguments parameter.
+ /// This method does nothing if there are no event subscribers.
+ ///
+ ///
+ ///
+ ///
+ private void RaiseEvent(EventHandler eventHandler, T eventArgs)
+ where T : EventArgs
+ {
+ if (eventHandler != null)
+ {
+ eventHandler(this, eventArgs);
+ }
+ }
+
+ private static T CreateEventArgs(SDL.SDL_Event rawEvent)
+ where T : class
+ {
+ return Activator.CreateInstance(typeof(T),
+ new object[] { rawEvent }) as T;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/Events/GameEventArgs.cs b/SharpDL/Events/GameEventArgs.cs
index 5afbd92..280d0c7 100644
--- a/SharpDL/Events/GameEventArgs.cs
+++ b/SharpDL/Events/GameEventArgs.cs
@@ -1,9 +1,5 @@
using SDL2;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace SharpDL.Events
{
diff --git a/SharpDL/Events/GameEventArgsFactory.cs b/SharpDL/Events/GameEventArgsFactory.cs
deleted file mode 100644
index dfa5a7d..0000000
--- a/SharpDL/Events/GameEventArgsFactory.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using SDL2;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SharpDL.Events
-{
- public static class GameEventArgsFactory
- where T : class
- {
- public static T Create(SDL.SDL_Event rawEvent)
- {
- return Activator.CreateInstance(typeof(T),
- new object[] { rawEvent }) as T;
- }
- }
-}
diff --git a/SharpDL/Events/GameEventType.cs b/SharpDL/Events/GameEventType.cs
index fa3797e..f77a06b 100644
--- a/SharpDL/Events/GameEventType.cs
+++ b/SharpDL/Events/GameEventType.cs
@@ -1,17 +1,12 @@
using SDL2;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace SharpDL.Events
{
public enum GameEventType : uint
{
- None = 0,
+ First = 0,
Quit = SDL.SDL_EventType.SDL_QUIT,
- WindowEvent = SDL.SDL_EventType.SDL_WINDOWEVENT,
+ Window = SDL.SDL_EventType.SDL_WINDOWEVENT,
VideoDeviceSystemEvent = SDL.SDL_EventType.SDL_SYSWMEVENT,
KeyDown = SDL.SDL_EventType.SDL_KEYDOWN,
KeyUp = SDL.SDL_EventType.SDL_KEYUP,
diff --git a/SharpDL/Events/IEventManager.cs b/SharpDL/Events/IEventManager.cs
new file mode 100644
index 0000000..a60a823
--- /dev/null
+++ b/SharpDL/Events/IEventManager.cs
@@ -0,0 +1,36 @@
+using System;
+using SDL2;
+
+namespace SharpDL.Events
+{
+ public interface IEventManager
+ {
+ event EventHandler MouseWheelScrolling;
+ event EventHandler MouseButtonPressed;
+ event EventHandler MouseButtonReleased;
+ event EventHandler MouseMoving;
+ event EventHandler TextInputting;
+ event EventHandler TextEditing;
+ event EventHandler KeyPressed;
+ event EventHandler KeyReleased;
+ event EventHandler VideoDeviceSystemEvent;
+ event EventHandler Quitting;
+ event EventHandler Exiting;
+ event EventHandler WindowShown;
+ event EventHandler WindowHidden;
+ event EventHandler WindowExposed;
+ event EventHandler WindowMoved;
+ event EventHandler WindowResized;
+ event EventHandler WindowSizeChanged;
+ event EventHandler WindowMinimized;
+ event EventHandler WindowMaximized;
+ event EventHandler WindowRestored;
+ event EventHandler WindowEntered;
+ event EventHandler WindowLeave;
+ event EventHandler WindowFocusGained;
+ event EventHandler WindowFocusLost;
+ event EventHandler WindowClosed;
+ void RaiseExiting(object sender, EventArgs args);
+ void RaiseEvent(SDL.SDL_Event rawEvent);
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/Events/MouseMotionEventArgs.cs b/SharpDL/Events/MouseMotionEventArgs.cs
index feeb881..3311a00 100644
--- a/SharpDL/Events/MouseMotionEventArgs.cs
+++ b/SharpDL/Events/MouseMotionEventArgs.cs
@@ -14,7 +14,7 @@ public class MouseMotionEventArgs : GameEventArgs
public UInt32 MouseDeviceID { get; private set; }
- public IList MouseButtonsPressed { get; private set; }
+ public IEnumerable MouseButtonsPressed { get; private set; }
///
/// Mouse X position relative to the window origin. Setter is public because we may need to intercept and adjust the value for detecting click/hover events
diff --git a/SharpDL/Events/QuitEventArgs.cs b/SharpDL/Events/QuitEventArgs.cs
index 314abb4..f17274b 100644
--- a/SharpDL/Events/QuitEventArgs.cs
+++ b/SharpDL/Events/QuitEventArgs.cs
@@ -1,9 +1,4 @@
using SDL2;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace SharpDL.Events
{
diff --git a/SharpDL/Events/VideoDeviceSystemEventArgs.cs b/SharpDL/Events/VideoDeviceSystemEventArgs.cs
index 7ccafeb..a5e0e66 100644
--- a/SharpDL/Events/VideoDeviceSystemEventArgs.cs
+++ b/SharpDL/Events/VideoDeviceSystemEventArgs.cs
@@ -1,9 +1,4 @@
using SDL2;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace SharpDL.Events
{
diff --git a/SharpDL/Events/WindowEventArgs.cs b/SharpDL/Events/WindowEventArgs.cs
index 23c586b..33be1bd 100644
--- a/SharpDL/Events/WindowEventArgs.cs
+++ b/SharpDL/Events/WindowEventArgs.cs
@@ -1,9 +1,5 @@
using SDL2;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace SharpDL.Events
{
diff --git a/SharpDL/Events/WindowEventType.cs b/SharpDL/Events/WindowEventType.cs
index 06e053d..0d47258 100644
--- a/SharpDL/Events/WindowEventType.cs
+++ b/SharpDL/Events/WindowEventType.cs
@@ -1,9 +1,4 @@
using SDL2;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace SharpDL.Events
{
diff --git a/SharpDL/Game.cs b/SharpDL/Game.cs
deleted file mode 100644
index 1ac421c..0000000
--- a/SharpDL/Game.cs
+++ /dev/null
@@ -1,491 +0,0 @@
-using SDL2;
-using SharpDL.Events;
-using SharpDL.Graphics;
-using SharpDL.Input;
-using System;
-
-namespace SharpDL
-{
- public class Game : IDisposable
- {
- #region Members
-
- private const uint EMPTY_UINT = 0;
- private const int EMPTY_INT = -1;
- private const float FRAMES_PER_SECOND = 60f;
- private TimeSpan accumulatedElapsedTime = TimeSpan.Zero;
- private TimeSpan targetElapsedTime = TimeSpan.FromSeconds(1 / FRAMES_PER_SECOND);
- private bool isFrameRateCapped = true;
- private GameTime gameTime = new GameTime();
- private Timer gameTimer = new Timer();
-
- #endregion Members
-
- #region Properties
-
- protected Window Window { get; private set; }
-
- protected Renderer Renderer { get; private set; }
-
- protected bool IsActive { get; private set; }
-
- protected bool IsExiting { get; private set; }
-
- #endregion Properties
-
- #region Constructors
-
- /// Default constructor of the base Game class does nothing. Only when Initialize is called
- /// is anything useful done.
- ///
- public Game()
- {
- WindowClosed += Game_WindowClosed;
- }
-
- private void Game_WindowClosed(object sender, GameEventArgs e)
- {
- IsExiting = true;
- }
-
- #endregion Constructors
-
- #region Events
-
- public event EventHandler MouseWheelScrolling;
-
- public event EventHandler MouseButtonPressed;
-
- public event EventHandler MouseButtonReleased;
-
- public event EventHandler MouseMoving;
-
- public event EventHandler TextInputting;
-
- public event EventHandler TextEditing;
-
- public event EventHandler KeyPressed;
-
- public event EventHandler KeyReleased;
-
- public event EventHandler VideoDeviceSystemEvent;
-
- public event EventHandler Quitting;
-
- public event EventHandler Activated;
-
- public event EventHandler Deactivated;
-
- public event EventHandler Disposed;
-
- public event EventHandler Exiting;
-
- public event EventHandler WindowShown;
-
- public event EventHandler WindowHidden;
-
- public event EventHandler WindowExposed;
-
- public event EventHandler WindowMoved;
-
- public event EventHandler WindowResized;
-
- public event EventHandler WindowSizeChanged;
-
- public event EventHandler WindowMinimized;
-
- public event EventHandler WindowMaximized;
-
- public event EventHandler WindowRestored;
-
- public event EventHandler WindowEntered;
-
- public event EventHandler WindowLeave;
-
- public event EventHandler WindowFocusGained;
-
- public event EventHandler WindowFocusLost;
-
- public event EventHandler WindowClosed;
-
- /// Raises the Activated event. Activation occurs when the game gains focus.
- ///
- ///
- ///
- protected virtual void OnActivated(object sender, EventArgs args)
- {
- RaiseEvent(Activated, args);
- }
-
- /// Raises the Deactivated event. Deactivation occurs when the game loses focus.
- ///
- ///
- ///
- protected virtual void OnDeactivated(object sender, EventArgs args)
- {
- RaiseEvent(Deactivated, args);
- }
-
- /// Raises the Exiting event. Exiting occurs when an unrecoverable exception occurs or the
- /// user directly exits the game.
- ///
- ///
- ///
- protected virtual void OnExiting(object sender, EventArgs args)
- {
- RaiseEvent(Exiting, args);
- }
-
- /// Checks if there are any event subscribers prior to raising the event.
- ///
- ///
- ///
- ///
- private void RaiseEvent(EventHandler eventHandler, T eventArguments)
- where T : EventArgs
- {
- if (eventHandler != null)
- eventHandler(this, eventArguments);
- }
-
- #endregion Events
-
- #region Game Cycle Control
-
- /// Begins the game by performing the following cycle events in this order: Initialize, LoadContent,
- /// CheckInputs, Update, Draw, UnloadContent.
- ///
- public void Run()
- {
- Initialize();
- LoadContent();
-
- while (!IsExiting)
- {
- SDL.SDL_Event rawEvent = new SDL.SDL_Event();
- while (SDL.SDL_PollEvent(out rawEvent) == 1)
- RaiseGameEventFromRawEvent(rawEvent);
-
- Tick();
- }
-
- UnloadContent();
- }
-
- // The passed raw SDL_Event object is translated into a SharpDL game object and raised using
- // the appropriate EventHandler.
- //
- //
- private void RaiseGameEventFromRawEvent(SDL.SDL_Event rawEvent)
- {
- if (rawEvent.type == SDL.SDL_EventType.SDL_FIRSTEVENT)
- return;
- else if (rawEvent.type == SDL.SDL_EventType.SDL_WINDOWEVENT)
- {
- WindowEventArgs eventArgs = GameEventArgsFactory.Create(rawEvent);
- if (eventArgs.SubEventType == WindowEventType.Close)
- RaiseEvent(WindowClosed, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.Enter)
- RaiseEvent(WindowEntered, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.Exposed)
- RaiseEvent(WindowExposed, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.FocusGained)
- RaiseEvent(WindowFocusGained, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.FocusLost)
- RaiseEvent(WindowFocusLost, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.Hidden)
- RaiseEvent(WindowHidden, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.Leave)
- RaiseEvent(WindowLeave, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.Maximized)
- RaiseEvent(WindowMaximized, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.Minimized)
- RaiseEvent(WindowMinimized, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.Moved)
- RaiseEvent(WindowMoved, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.Resized)
- RaiseEvent(WindowResized, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.Restored)
- RaiseEvent(WindowRestored, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.Shown)
- RaiseEvent(WindowShown, eventArgs);
- else if (eventArgs.SubEventType == WindowEventType.SizeChanged)
- RaiseEvent(WindowSizeChanged, eventArgs);
- }
- else if (rawEvent.type == SDL.SDL_EventType.SDL_QUIT)
- {
- QuitEventArgs eventArgs = GameEventArgsFactory.Create(rawEvent);
- RaiseEvent(Quitting, eventArgs);
- }
- else if (rawEvent.type == SDL.SDL_EventType.SDL_SYSWMEVENT)
- {
- VideoDeviceSystemEventArgs eventArgs = GameEventArgsFactory.Create(rawEvent);
- RaiseEvent(VideoDeviceSystemEvent, eventArgs);
- }
- else if (rawEvent.type == SDL.SDL_EventType.SDL_KEYDOWN
- || rawEvent.type == SDL.SDL_EventType.SDL_KEYUP)
- {
- KeyboardEventArgs eventArgs = GameEventArgsFactory.Create(rawEvent);
- if (eventArgs.State == KeyState.Pressed)
- RaiseEvent(KeyPressed, eventArgs);
- else if (eventArgs.State == KeyState.Released)
- RaiseEvent(KeyReleased, eventArgs);
- }
- else if (rawEvent.type == SDL.SDL_EventType.SDL_TEXTEDITING)
- {
- TextEditingEventArgs eventArgs = GameEventArgsFactory.Create(rawEvent);
- RaiseEvent(TextEditing, eventArgs);
- }
- else if (rawEvent.type == SDL.SDL_EventType.SDL_TEXTINPUT)
- {
- TextInputEventArgs eventArgs = GameEventArgsFactory.Create(rawEvent);
- RaiseEvent(TextInputting, eventArgs);
- }
- else if (rawEvent.type == SDL.SDL_EventType.SDL_MOUSEMOTION)
- {
- MouseMotionEventArgs eventArgs = GameEventArgsFactory.Create(rawEvent);
- Mouse.UpdateMousePosition(eventArgs.RelativeToWindowX, eventArgs.RelativeToWindowY);
- RaiseEvent(MouseMoving, eventArgs);
- }
- else if (rawEvent.type == SDL.SDL_EventType.SDL_MOUSEBUTTONUP
- || rawEvent.type == SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN)
- {
- MouseButtonEventArgs eventArgs = GameEventArgsFactory.Create(rawEvent);
-
- if (eventArgs.State == MouseButtonState.Pressed)
- RaiseEvent(MouseButtonPressed, eventArgs);
- else if (eventArgs.State == MouseButtonState.Released)
- RaiseEvent(MouseButtonReleased, eventArgs);
- }
- else if (rawEvent.type == SDL.SDL_EventType.SDL_MOUSEWHEEL)
- {
- MouseWheelEventArgs eventArgs = GameEventArgsFactory.Create(rawEvent);
- RaiseEvent(MouseWheelScrolling, eventArgs);
- }
- }
-
- /// A tick is equal to a single time step forward in the game state. During each tick, the game will update total game time,
- /// elapsed update time, and frame rates. It is important to note that the implementation is based on a Fixed Time Step algorithm where
- /// each update and draw occur in the same constant fixed intervals. Additionally, the game will call the Update and Draw game cycle
- /// methods to be overridden by each implementation's specific Game Update and Draw logic. This method is based heavily on MonoGame's
- /// tick implementation and suggestions from Glenn Fiedler's blog (http://gafferongames.com/game-physics/fix-your-timestep/).
- ///
- private void Tick()
- {
- while (isFrameRateCapped && (accumulatedElapsedTime < targetElapsedTime))
- {
- accumulatedElapsedTime += gameTimer.ElapsedTime;
- gameTimer.Stop();
- gameTimer.Start();
-
- if (isFrameRateCapped && (accumulatedElapsedTime < targetElapsedTime))
- {
- TimeSpan sleepTime = targetElapsedTime - accumulatedElapsedTime;
- SDL.SDL_Delay((UInt32)sleepTime.TotalMilliseconds);
- }
- }
-
- if (accumulatedElapsedTime > TimeSpan.FromSeconds(0.5))
- accumulatedElapsedTime = TimeSpan.FromSeconds(0.5);
-
- if (isFrameRateCapped)
- {
- int stepCount = 0;
-
- while (accumulatedElapsedTime >= targetElapsedTime)
- {
- gameTime.TotalGameTime += targetElapsedTime;
- accumulatedElapsedTime -= targetElapsedTime;
- stepCount++;
-
- Update(gameTime);
- }
-
- gameTime.ElapsedGameTime = TimeSpan.FromTicks(targetElapsedTime.Ticks * stepCount);
- }
-
- Draw(gameTime);
- }
-
- /// Raises the Exiting event and disposes of this instance.
- ///
- public void Quit()
- {
- IsExiting = true;
- RaiseEvent(Exiting, EventArgs.Empty);
- }
-
- #endregion Game Cycle Control
-
- #region Game Cycle
-
- ///
- /// Initializes the game by calling initialize on the SDL2 instance with "EVERYTHING".
- ///
- protected virtual void Initialize()
- {
- Initialize(SDL.SDL_INIT_EVERYTHING);
- }
-
- /// Initializes the game by calling initialize on the SDL2 instance with the passed flags
- /// or "EVERYTHING" if 0. Additionally, this method will initialize SDL_ttf and SDL_image to load fonts and images.
- ///
- /// Bit flags indicating the way in which SDL should be initialized
- protected virtual void Initialize(uint flags)
- {
- if (flags == EMPTY_UINT)
- flags = SDL.SDL_INIT_EVERYTHING;
-
- if (SDL.SDL_Init(flags) != 0)
- {
- throw new InvalidOperationException(String.Format("SDL_Init: {0}", SDL.SDL_GetError()));
- }
-
- if (SDL_ttf.TTF_Init() != 0)
- {
- throw new InvalidOperationException(String.Format("TTF_Init: {0}", SDL.SDL_GetError()));
- }
-
- int initImageResult = SDL_image.IMG_Init(SDL_image.IMG_InitFlags.IMG_INIT_PNG);
- if ((initImageResult & (int)SDL_image.IMG_InitFlags.IMG_INIT_PNG) != (int)SDL_image.IMG_InitFlags.IMG_INIT_PNG)
- {
- throw new InvalidOperationException(String.Format("IMG_Init: {0}", SDL.SDL_GetError()));
- }
- }
-
- /// Used for potentially long lasting operations that should only occur relatively rarely. Usually, this
- /// method is used to load images, textures, maps, sounds, videos, and other game assets at the beginning of a level or area.
- ///
- protected virtual void LoadContent()
- {
- }
-
- //TimeSpan elapsedTime = TimeSpan.Zero;
- //int frameRate = 0;
- //int frameCounter = 0;
-
- /// Update the state of the game such as positions, health, entity properties, and more.
- /// This is called before Draw in the main game loop.
- ///
- /// Allows access to total game time and elapsed game time since the last update
- protected virtual void Update(GameTime gameTime)
- {
- Mouse.UpdateMouseState();
-
- //if (rawEvents.Count > 0)
- // RaiseGameEventFromRawEvent(rawEvents.Dequeue());
-
- //elapsedTime += gameTime.ElapsedGameTime;
-
- //if (elapsedTime >= TimeSpan.FromSeconds(1))
- //{
- // elapsedTime -= TimeSpan.FromSeconds(1);
- // frameRate = frameCounter;
- // frameCounter = 0;
- //}
- }
-
- //TrueTypeText fpsText;
-
- /// Draw the current state of the game such as textures, surfaces, maps, and other visual content.
- /// This is called after Update in the main game loop.
- ///
- /// Allows access to total game time and elapsed game time since the last update
- protected virtual void Draw(GameTime gameTime)
- {
- //frameCounter++;
-
- //string fps = String.Format("FPS: {0}", frameRate);
-
- //fpsText.UpdateText(fps);
-
- //Renderer.RenderTexture(fpsText.Texture, 0, 100);
-
- //Renderer.RenderPresent();
- }
-
- /// Used to unload game assets that were loaded during the LoadContent method. Usually, you use this to free
- /// any resources that should not be lingering any longer or are no longer required.
- ///
- protected virtual void UnloadContent()
- {
- Dispose();
- }
-
- #endregion Game Cycle
-
- #region Initializers
-
- /// Creates a SDL window to render content within.
- ///
- /// Title of the window
- /// X position of the top left corner
- /// Y position of the top left corner
- /// Width of the window
- /// Height of the window
- /// Bit flags indicating the way in which the window should be created
- protected void CreateWindow(string title, int x, int y, int width, int height, WindowFlags flags)
- {
- Window = new Window(title, x, y, width, height, flags);
- }
-
- /// Creates a SDL Renderer to copy and draw textures to a window
- ///
- /// Bit flags indicating the way in which the renderer should be created
- protected void CreateRenderer(RendererFlags flags)
- {
- CreateRenderer(EMPTY_INT, flags);
- }
-
- /// Creates a SDL Renderer to copy and draw textures to a window
- ///
- /// Index of the renderering driver. -1 to choose the first available.
- /// Bit flags indicating the way in which the renderer should be created
- protected void CreateRenderer(int index, RendererFlags flags)
- {
- if (Window == null)
- {
- throw new InvalidOperationException("Window has not been initialized. You must first create a Window before creating a Renderer.");
- }
-
- Renderer = new Renderer(this.Window, index, flags);
-
- SDL2.SDL.SDL_SetHint(SDL2.SDL.SDL_HINT_RENDER_SCALE_QUALITY, "linear");
- }
-
- #endregion Initializers
-
- #region Dispose
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- ~Game()
- {
- Dispose(false);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (Window != null)
- {
- Window.Dispose();
- }
-
- if (Renderer != null)
- {
- Renderer.Dispose();
- }
-
- SDL_ttf.TTF_Quit();
- SDL_image.IMG_Quit();
- SDL.SDL_Quit();
- RaiseEvent(Disposed, EventArgs.Empty);
- }
-
- #endregion Dispose
- }
-}
\ No newline at end of file
diff --git a/SharpDL/GameEngine.cs b/SharpDL/GameEngine.cs
new file mode 100644
index 0000000..4409d43
--- /dev/null
+++ b/SharpDL/GameEngine.cs
@@ -0,0 +1,278 @@
+using Microsoft.Extensions.Logging;
+using SDL2;
+using SharpDL.Events;
+using SharpDL.Graphics;
+using SharpDL.Input;
+using System;
+
+namespace SharpDL
+{
+ public sealed class GameEngine : IGameEngine
+ {
+ #region Members
+
+ private const float fixedFramesPerSecond = 60f;
+ private bool isFrameRateCapped = true;
+ private readonly ILogger logger;
+ private readonly GameTime gameTime = new GameTime();
+ private readonly Timer gameTimer = new Timer();
+ private readonly TimeSpan targetElapsedTime = TimeSpan.FromSeconds(1 / fixedFramesPerSecond);
+ private readonly TimeSpan maxElapsedTime = TimeSpan.FromSeconds(0.5);
+ private TimeSpan accumulatedElapsedTime = TimeSpan.Zero;
+
+ #endregion Members
+
+ #region Properties
+
+ public IWindowFactory WindowFactory { get; private set; }
+
+ public IRendererFactory RendererFactory { get; private set; }
+
+ public IEventManager EventManager { get; private set; }
+
+ private bool IsActive { get; set; }
+
+ private bool IsExiting { get; set; }
+
+ public Action Initialize { private get; set; }
+
+ public Action LoadContent { private get; set; }
+
+ public Action Update { private get; set; }
+
+ public Action Draw { private get; set; }
+
+ public Action UnloadContent { private get; set; }
+
+ #endregion Properties
+
+ #region Constructors
+
+ /// Default constructor of the base Game class does nothing. Only when Initialize is called
+ /// is anything useful done.
+ ///
+ public GameEngine(
+ IWindowFactory windowFactory,
+ IRendererFactory rendererFactory,
+ IEventManager eventManager,
+ ILogger logger = null)
+ {
+ WindowFactory = windowFactory ?? throw new ArgumentNullException(nameof(windowFactory));
+ RendererFactory = rendererFactory ?? throw new ArgumentNullException(nameof(rendererFactory));
+ EventManager = eventManager ?? throw new ArgumentNullException(nameof(eventManager));
+ this.logger = logger;
+
+ EventManager.WindowClosed += OnExiting;
+ EventManager.Quitting += OnExiting;
+ }
+
+ #endregion Constructors
+
+ #region Events
+
+ private void OnExiting(object sender, GameEventArgs e)
+ {
+ IsExiting = true;
+ }
+
+ #endregion Events
+
+ #region Game Cycle Control
+
+ /// Begins the game by performing the following cycle events in this order: Initialize, LoadContent,
+ /// CheckInputs, Update, Draw, UnloadContent.
+ ///
+ public void Start(InitializeType types)
+ {
+ PerformInitialize(types);
+ PerformLoadContent();
+
+ while (!IsExiting)
+ {
+ SDL.SDL_Event rawEvent = new SDL.SDL_Event();
+ while (SDL.SDL_PollEvent(out rawEvent) == 1)
+ {
+ logger?.LogTrace($"SDL_Event: {rawEvent.type.ToString()}");
+ EventManager.RaiseEvent(rawEvent);
+ }
+
+ Tick();
+ }
+
+ PerformUnloadContent();
+ Dispose();
+ }
+
+ /// A tick is equal to a single time step forward in the game state. During each tick, the game will update total game time,
+ /// elapsed update time, and frame rates. It is important to note that the implementation is based on a Fixed Time Step algorithm where
+ /// each update and draw occur in the same constant fixed intervals. Additionally, the game will call the Update and Draw game cycle
+ /// methods to be overridden by each implementation's specific Game Update and Draw logic. This method is based heavily on MonoGame's
+ /// tick implementation and suggestions from Glenn Fiedler's blog (http://gafferongames.com/game-physics/fix-your-timestep/).
+ ///
+ private void Tick()
+ {
+ // If our frame rate is capped, we want to wait until we have elapsed enough time to have a fixed-step
+ // At 60 FPS, the target elapsed time is 1/60 or 0.01667~ seconds.
+ while (isFrameRateCapped && (accumulatedElapsedTime < targetElapsedTime))
+ {
+ accumulatedElapsedTime += gameTimer.ElapsedTime;
+ gameTimer.Start();
+
+ if (isFrameRateCapped && (accumulatedElapsedTime < targetElapsedTime))
+ {
+ // Sleep for as long as we need to reach the target elapsed time
+ TimeSpan sleepTime = targetElapsedTime - accumulatedElapsedTime;
+ SDL.SDL_Delay((uint)sleepTime.TotalMilliseconds);
+ }
+ }
+
+ // Don't allow any updates to go beyond the max update time
+ if (accumulatedElapsedTime > maxElapsedTime)
+ accumulatedElapsedTime = maxElapsedTime;
+
+ // Fixed time step update
+ if (isFrameRateCapped)
+ {
+ int stepCount = 0;
+
+ // If we have waited longer than the target time (non-precision timers/waits?), we need to advance
+ // the game state in a fixed step interval.
+ while (accumulatedElapsedTime >= targetElapsedTime)
+ {
+ gameTime.TotalGameTime += targetElapsedTime;
+ accumulatedElapsedTime -= targetElapsedTime;
+ stepCount++;
+
+ PerformUpdate(gameTime);
+ }
+
+ // In normal scenarios, this will advance the elapsed time by the target, but in cases where
+ // we have had to "catch up" because of non-precise waits, we will need to take the fixed steps
+ // into account.
+ gameTime.ElapsedGameTime = TimeSpan.FromTicks(targetElapsedTime.Ticks * stepCount);
+ }
+ // Variable time step update
+ else
+ {
+ gameTime.ElapsedGameTime = accumulatedElapsedTime;
+ gameTime.TotalGameTime += targetElapsedTime;
+ accumulatedElapsedTime = TimeSpan.Zero;
+
+ PerformUpdate(gameTime);
+ }
+
+ PerformDraw(gameTime);
+ }
+
+ /// Raises the Exiting event and disposes of this instance.
+ ///
+ public void End()
+ {
+ IsExiting = true;
+ EventManager.RaiseExiting(this, EventArgs.Empty);
+ }
+
+ #endregion Game Cycle Control
+
+ #region Game Cycle
+
+ /// Override to initialize any custom objects or large helpers that are required by the game.
+ ///
+ private void PerformInitialize(InitializeType types)
+ {
+ InitializeBase(types);
+ Initialize();
+ }
+
+ /// Initializes the game by calling initialize on the SDL2 instance with the passed flags
+ /// or "EVERYTHING" if 0. Additionally, this method will initialize SDL_ttf and SDL_image to load fonts and images.
+ ///
+ /// Bit flags indicating the way in which SDL should be initialized
+ private void InitializeBase(InitializeType types)
+ {
+ if (SDL.SDL_Init((uint)types) != 0)
+ {
+ throw new InvalidOperationException($"SDL_Init: {SDL.SDL_GetError()}");
+ }
+
+ if (SDL_ttf.TTF_Init() != 0)
+ {
+ throw new InvalidOperationException($"TTF_Init: {SDL.SDL_GetError()}");
+ }
+
+ SDL_image.IMG_InitFlags initImageFlags =
+ SDL_image.IMG_InitFlags.IMG_INIT_JPG
+ | SDL_image.IMG_InitFlags.IMG_INIT_PNG
+ | SDL_image.IMG_InitFlags.IMG_INIT_TIF
+ | SDL_image.IMG_InitFlags.IMG_INIT_WEBP;
+ int initImageResult = SDL_image.IMG_Init(initImageFlags);
+ if ((initImageResult & (int)initImageFlags) != (int)initImageFlags)
+ {
+ throw new InvalidOperationException($"IMG_Init: {SDL.SDL_GetError()}");
+ }
+ }
+
+ /// Used for potentially long lasting operations that should only occur relatively rarely. Usually, this
+ /// method is used to load images, textures, maps, sounds, videos, and other game assets at the beginning of a level or area.
+ ///
+ private void PerformLoadContent()
+ {
+ LoadContent();
+ }
+
+ /// Update the state of the game such as positions, health, entity properties, and more.
+ /// This is called before Draw in the main game loop.
+ ///
+ /// Allows access to total game time and elapsed game time since the last update
+ private void PerformUpdate(GameTime gameTime)
+ {
+ Mouse.UpdateMouseState();
+ Update(gameTime);
+ }
+
+ /// Draw the current state of the game such as textures, surfaces, maps, and other visual content.
+ /// This is called after Update in the main game loop.
+ ///
+ /// Allows access to total game time and elapsed game time since the last update
+ private void PerformDraw(GameTime gameTime)
+ {
+ Draw(gameTime);
+ }
+
+ /// Used to unload game assets that were loaded during the LoadContent method. Usually, you use this to free
+ /// any resources that should not be lingering any longer or are no longer required.
+ ///
+ private void PerformUnloadContent()
+ {
+ UnloadContent();
+ }
+
+ #endregion Game Cycle
+
+ #region Dispose
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ~GameEngine()
+ {
+ Dispose(false);
+ }
+
+ /// Override to dispose of any custom objects that you've instantiated. Always call
+ /// base.Dispose() so that the base class objects are disposed as well.
+ ///
+ ///
+ private void Dispose(bool disposing)
+ {
+ SDL_ttf.TTF_Quit();
+ SDL_image.IMG_Quit();
+ SDL.SDL_Quit();
+ }
+
+ #endregion Dispose
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/GameTime.cs b/SharpDL/GameTime.cs
index a842da5..2babbd6 100644
--- a/SharpDL/GameTime.cs
+++ b/SharpDL/GameTime.cs
@@ -1,9 +1,4 @@
-using SDL2;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System;
namespace SharpDL
{
diff --git a/SharpDL/Graphics/IRenderer.cs b/SharpDL/Graphics/IRenderer.cs
new file mode 100644
index 0000000..08bcde7
--- /dev/null
+++ b/SharpDL/Graphics/IRenderer.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+
+namespace SharpDL.Graphics
+{
+ public interface IRenderer : IDisposable
+ {
+ /// Window in which this Renderer was created and will display to.
+ ///
+ IWindow Window { get; }
+
+ /// The index of the rendering driver to initialize, or -1 to initialize the first one supporting the requested flags.
+ ///
+ int Index { get; }
+
+ /// Initial behavior flags of the Renderer upon creation.
+ ///
+ IEnumerable Flags { get; }
+
+ /// Native pointer to Renderer as stored in memory by SDL2. TODO: should this be on the interface?
+ ///
+ IntPtr Handle { get; }
+
+ /// Clears the screen to whatever color is set in the Renderer.
+ ///
+ void ClearScreen();
+
+ /// Updates the Window with any rendering committed to the Renderer.
+ ///
+ void RenderPresent();
+
+ /// Resets the Renderer's render target back to null.
+ ///
+ void ResetRenderTarget();
+
+ /// Updates the Renderer's blend mode such as Alpha, Additive, or Modulation.
+ ///
+ /// Blend mode to select.
+ void SetBlendMode(BlendMode blendMode);
+
+ /// Updates the Renderer's current draw color to use with Rectangles, Lines, and Points.
+ ///
+ /// Red
+ /// Green
+ /// Blue
+ /// Alpha
+ void SetDrawColor(byte r, byte g, byte b, byte a);
+
+ /// Sets the logical size in which the Renderer will draw. Useful when we want to render to a device independent resolution.
+ ///
+ /// Width of the logical size
+ /// Height of the logical size
+ void SetRenderLogicalSize(int width, int height);
+
+ /// Updates the Renderer's current texture target for rendering operations.
+ ///
+ /// Texture to render to.
+ void SetRenderTarget(RenderTarget renderTarget);
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/Graphics/IRendererFactory.cs b/SharpDL/Graphics/IRendererFactory.cs
new file mode 100644
index 0000000..12cba04
--- /dev/null
+++ b/SharpDL/Graphics/IRendererFactory.cs
@@ -0,0 +1,28 @@
+namespace SharpDL.Graphics
+{
+ public interface IRendererFactory
+ {
+
+ /// Creates a Renderer paired with a Window to perform rendering.
+ ///
+ /// The window where rendering is displayed
+ /// Instance of a Renderer.
+ ///
+ IRenderer CreateRenderer(Window window);
+
+ /// Creates a Renderer paired with a Window to perform rendering.
+ ///
+ /// The window where rendering is displayed
+ /// The index of the rendering driver to initialize, or -1 to initialize the first one supporting the requested flags
+ /// Instance of a Renderer.
+ IRenderer CreateRenderer(Window window, int index);
+
+ /// Creates a Renderer paired with a Window to perform rendering.
+ ///
+ /// The window where rendering is displayed
+ /// The index of the rendering driver to initialize, or -1 to initialize the first one supporting the requested flags
+ /// 0, or one or more RendererFlags OR'd together
+ /// Instance of a Renderer.
+ IRenderer CreateRenderer(Window window, int index, RendererFlags flags);
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/Graphics/IWindow.cs b/SharpDL/Graphics/IWindow.cs
new file mode 100644
index 0000000..47a4a7d
--- /dev/null
+++ b/SharpDL/Graphics/IWindow.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+
+namespace SharpDL.Graphics
+{
+ public interface IWindow : IDisposable
+ {
+ /// Title of the Window upon creation.
+ ///
+ string Title { get; }
+
+ /// Initial X coordinate of Window upon creation. Value does not update when Window is moved.
+ ///
+ int X { get; }
+
+ /// Initial Y coordinate of Window upon creation. Value does not update when Window is moved.
+ ///
+ int Y { get; }
+
+ /// Initial width of Window upon creation. Value does not update when Window is resized.
+ ///
+ int Width { get; }
+
+ /// Initial height of Window upon creation. Value does not update when Window is resized.
+ ///
+ int Height { get; }
+
+ /// Initial behavior flags of the Window upon creation.
+ ///
+ IEnumerable Flags { get; }
+
+ /// Native pointer to Window as stored in memory by SDL2. TODO: should this be on the interface?
+ ///
+ IntPtr Handle { get; }
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/Graphics/IWindowFactory.cs b/SharpDL/Graphics/IWindowFactory.cs
new file mode 100644
index 0000000..cdbd363
--- /dev/null
+++ b/SharpDL/Graphics/IWindowFactory.cs
@@ -0,0 +1,40 @@
+namespace SharpDL.Graphics
+{
+ public interface IWindowFactory
+ {
+ /// Creates a Window used for display and rendering.
+ ///
+ /// String displayed in the Window title bar.
+ /// Instance of a Window.
+ IWindow CreateWindow(string title);
+
+ /// Creates a Window used for display and rendering.
+ ///
+ /// String displayed in the Window title bar.
+ /// X coordinate to position the Window.
+ /// Y coordinate to position the Window.
+ /// Instance of a Window.
+ IWindow CreateWindow(string title, int x, int y);
+
+ /// Creates a Window used for display and rendering.
+ ///
+ /// String displayed in the Window title bar.
+ /// X coordinate to position the Window.
+ /// Y coordinate to position the Window.
+ /// Width of the Window.
+ /// Height of the Window.
+ /// Instance of a Window.
+ IWindow CreateWindow(string title, int x, int y, int width, int height);
+
+ /// Creates a Window used for display and rendering.
+ ///
+ /// String displayed in the Window title bar.
+ /// X coordinate to position the Window.
+ /// Y coordinate to position the Window.
+ /// Width of the Window.
+ /// Height of the Window.
+ /// Flags to give special behaviors and features to the Window.
+ /// Instance of a Window.
+ IWindow CreateWindow(string title, int x, int y, int width, int height, WindowFlags flags);
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/Graphics/Renderer.cs b/SharpDL/Graphics/Renderer.cs
index f199e0e..eabe008 100644
--- a/SharpDL/Graphics/Renderer.cs
+++ b/SharpDL/Graphics/Renderer.cs
@@ -1,18 +1,18 @@
-using SDL2;
+using Microsoft.Extensions.Logging;
+using SDL2;
using SharpDL.Shared;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
namespace SharpDL.Graphics
{
- public class Renderer : IDisposable
+ public class Renderer : IRenderer
{
- //private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+ private readonly ILogger logger;
private List flags = new List();
- public Window Window { get; private set; }
+ public IWindow Window { get; private set; }
public int Index { get; private set; }
@@ -20,13 +20,19 @@ public class Renderer : IDisposable
public IntPtr Handle { get; private set; }
- public Renderer(Window window, int index, RendererFlags flags)
+ internal Renderer(IWindow window, int index, RendererFlags flags, ILogger logger = null)
{
if (window == null)
{
- throw new ArgumentNullException(Errors.E_WINDOW_NULL);
+ throw new ArgumentNullException(nameof(window), "Window has not been initialized. You must first create a Window before creating a Renderer.");
}
+ if (index < -1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ this.logger = logger;
Window = window;
Index = index;
@@ -59,9 +65,9 @@ public void ClearScreen()
internal void RenderTexture(IntPtr textureHandle, float positionX, float positionY, int sourceWidth, int sourceHeight, double angle, Vector center)
{
- if(textureHandle == IntPtr.Zero)
+ if (textureHandle == IntPtr.Zero)
{
- throw new ArgumentNullException("textureHandle", Errors.E_TEXTURE_NULL);
+ throw new ArgumentNullException(nameof(textureHandle), Errors.E_TEXTURE_NULL);
}
// SDL only accepts integer positions (x,y) in the rendering Rect
@@ -86,7 +92,7 @@ internal void RenderTexture(IntPtr textureHandle, float positionX, float positio
{
if (textureHandle == IntPtr.Zero)
{
- throw new ArgumentNullException("textureHandle", Errors.E_TEXTURE_NULL);
+ throw new ArgumentNullException(nameof(textureHandle), Errors.E_TEXTURE_NULL);
}
int width = source.Width;
@@ -142,7 +148,7 @@ public void SetBlendMode(BlendMode blendMode)
int result = SDL2.SDL.SDL_SetRenderDrawBlendMode(Handle, (SDL2.SDL.SDL_BlendMode)blendMode);
if (Utilities.IsError(result))
{
- throw new InvalidOperationException(Utilities.GetErrorMessage("SDL_SetDrawBlendMode"));
+ throw new InvalidOperationException(Utilities.GetErrorMessage("SDL_SetRenderDrawBlendMode"));
}
}
@@ -160,6 +166,16 @@ public void SetDrawColor(byte r, byte g, byte b, byte a)
public void SetRenderLogicalSize(int width, int height)
{
+ if (width < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(width));
+ }
+
+ if (height < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(height));
+ }
+
ThrowExceptionIfRendererIsNull();
int result = SDL2.SDL.SDL_RenderSetLogicalSize(Handle, width, height);
@@ -171,7 +187,7 @@ public void SetRenderLogicalSize(int width, int height)
private void ThrowExceptionIfRendererIsNull()
{
- if(Handle == IntPtr.Zero)
+ if (Handle == IntPtr.Zero)
{
throw new InvalidOperationException(Errors.E_RENDERER_NULL);
}
@@ -185,7 +201,7 @@ public void Dispose()
~Renderer()
{
- //log.Debug("A renderer resource has leaked. Did you forget to dispose the object?");
+ logger?.LogWarning("A renderer resource has leaked. Did you forget to dispose the object?");
}
private void Dispose(bool disposing)
diff --git a/SharpDL/Graphics/RendererFactory.cs b/SharpDL/Graphics/RendererFactory.cs
new file mode 100644
index 0000000..23210a4
--- /dev/null
+++ b/SharpDL/Graphics/RendererFactory.cs
@@ -0,0 +1,47 @@
+using System;
+using Microsoft.Extensions.Logging;
+using SharpDL.Shared;
+
+namespace SharpDL.Graphics
+{
+ public class RendererFactory : IRendererFactory
+ {
+ private readonly ILogger logger;
+ private readonly ILogger loggerRenderer;
+
+ public RendererFactory(
+ ILogger logger = null,
+ ILogger loggerRenderer = null)
+ {
+ this.logger = logger;
+ this.loggerRenderer = loggerRenderer;
+ }
+
+ public IRenderer CreateRenderer(Window window)
+ {
+ return CreateRenderer(window, -1, RendererFlags.None);
+ }
+
+ public IRenderer CreateRenderer(Window window, int index)
+ {
+ return CreateRenderer(window, index, RendererFlags.None);
+ }
+
+ public IRenderer CreateRenderer(Window window, int index, RendererFlags flags)
+ {
+ try
+ {
+ var renderer = new Renderer(window, index, flags, loggerRenderer);
+ logger?.LogTrace($"Renderer created. Handle = {renderer.Handle}, Window Title = {window.Title}, Window Handle = {window.Handle}.");
+ SDL2.SDL.SDL_SetHint(SDL2.SDL.SDL_HINT_RENDER_SCALE_QUALITY, "linear");
+ return renderer;
+ }
+ catch(Exception ex)
+ {
+ logger?.LogError(ex, ex.Message);
+ throw;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/Graphics/RendererFlags.cs b/SharpDL/Graphics/RendererFlags.cs
index 7740d1a..3467816 100644
--- a/SharpDL/Graphics/RendererFlags.cs
+++ b/SharpDL/Graphics/RendererFlags.cs
@@ -6,6 +6,7 @@ namespace SharpDL.Graphics
[Flags]
public enum RendererFlags : uint
{
+ None = 0,
RendererAccelerated = SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED,
RendererPresentVSync = SDL.SDL_RendererFlags.SDL_RENDERER_PRESENTVSYNC,
SupportRenderTargets = SDL.SDL_RendererFlags.SDL_RENDERER_TARGETTEXTURE
diff --git a/SharpDL/Graphics/Window.cs b/SharpDL/Graphics/Window.cs
index 1e427c5..1acabcd 100644
--- a/SharpDL/Graphics/Window.cs
+++ b/SharpDL/Graphics/Window.cs
@@ -1,12 +1,13 @@
-using SDL2;
+using Microsoft.Extensions.Logging;
+using SDL2;
using System;
using System.Collections.Generic;
namespace SharpDL.Graphics
{
- public class Window : IDisposable
+ public class Window : IWindow
{
- //private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+ private readonly ILogger logger;
public string Title { get; private set; }
@@ -22,13 +23,25 @@ public class Window : IDisposable
public IntPtr Handle { get; private set; }
- public Window(string title, int x, int y, int width, int height, WindowFlags flags)
+ internal Window(string title, int x, int y, int width, int height, WindowFlags flags, ILogger logger = null)
{
- if (String.IsNullOrEmpty(title))
+ if (string.IsNullOrWhiteSpace(title))
{
title = "SharpDL Window";
}
+ if (width < 0)
+ {
+ width = 0;
+ }
+
+ if (height < 0)
+ {
+ height = 0;
+ }
+
+ this.logger = logger;
+
Title = title;
X = x;
Y = y;
@@ -37,15 +50,19 @@ public Window(string title, int x, int y, int width, int height, WindowFlags fla
List copyFlags = new List();
foreach (WindowFlags flag in Enum.GetValues(typeof(WindowFlags)))
+ {
if (flags.HasFlag(flag))
+ {
copyFlags.Add(flag);
+ }
+ }
Flags = copyFlags;
Handle = SDL.SDL_CreateWindow(this.Title, this.X, this.Y, this.Width, this.Height, (SDL.SDL_WindowFlags)flags);
if (Handle == IntPtr.Zero)
{
- throw new InvalidOperationException(String.Format("SDL_CreateWindow: {0}", SDL.SDL_GetError()));
+ throw new InvalidOperationException($"SDL_CreateWindow: {SDL.SDL_GetError()}");
}
}
@@ -57,7 +74,7 @@ public void Dispose()
~Window()
{
- //log.Debug("A window resource has leaked. Did you forget to dispose the object?");
+ logger?.LogWarning("A window resource has leaked. Did you forget to dispose the object?");
}
private void Dispose(bool isDisposing)
diff --git a/SharpDL/Graphics/WindowFactory.cs b/SharpDL/Graphics/WindowFactory.cs
new file mode 100644
index 0000000..cad6070
--- /dev/null
+++ b/SharpDL/Graphics/WindowFactory.cs
@@ -0,0 +1,50 @@
+using System;
+using Microsoft.Extensions.Logging;
+
+namespace SharpDL.Graphics
+{
+ public class WindowFactory : IWindowFactory
+ {
+ private readonly ILogger logger;
+ private readonly ILogger loggerWindow;
+
+ public WindowFactory(
+ ILogger logger = null,
+ ILogger loggerWindow = null)
+ {
+ this.logger = logger;
+ this.loggerWindow = loggerWindow;
+ }
+
+ public IWindow CreateWindow(string title)
+ {
+ return CreateWindow(title, 100, 100, 1280, 720, WindowFlags.Shown);
+ }
+
+ public IWindow CreateWindow(string title, int x, int y)
+ {
+ return CreateWindow(title, x, y, 1280, 720, WindowFlags.Shown);
+ }
+
+ public IWindow CreateWindow(string title, int x, int y, int width, int height)
+ {
+ return CreateWindow(title, x, y, width, height, WindowFlags.Shown);
+ }
+
+ public IWindow CreateWindow(string title, int x, int y, int width, int height, WindowFlags flags)
+ {
+ try
+ {
+ var window = new Window(title, x, y, width, height, flags, loggerWindow);
+ logger?.LogTrace($"Window created. Title = {window.Title}, X = {window.X}, Y = {window.Y}, Width = {window.Width}, Height = {window.Height}, Handle = {window.Handle}.");
+ return window;
+ }
+ catch(Exception ex)
+ {
+ logger?.LogError(ex, ex.Message);
+ throw;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/IGame.cs b/SharpDL/IGame.cs
new file mode 100644
index 0000000..aef8cac
--- /dev/null
+++ b/SharpDL/IGame.cs
@@ -0,0 +1,10 @@
+namespace SharpDL
+{
+ public interface IGame
+ {
+ ///
+ /// Entry point to the main game. This method should start the game engine.
+ ///
+ void Run();
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/IGameEngine.cs b/SharpDL/IGameEngine.cs
new file mode 100644
index 0000000..23f27ef
--- /dev/null
+++ b/SharpDL/IGameEngine.cs
@@ -0,0 +1,59 @@
+using System;
+using SharpDL.Events;
+using SharpDL.Graphics;
+
+namespace SharpDL
+{
+ public interface IGameEngine : IDisposable
+ {
+ /// Defines the Initialize method that should be injected into the game loop.
+ /// This method should initialize any long running game assets, systems, and services.
+ ///
+ Action Initialize { set; }
+
+ /// Defines the LoadContent method that should be injected into the game loop.
+ /// This method should load any game assets, images, sounds, models, etc.
+ ///
+ Action LoadContent { set; }
+
+ /// Defines the Update method that should be injected into the game loop.
+ /// This method should update the game state when the game loop ticks forward in time.
+ ///
+ Action Update { set; }
+
+ /// Defines the Draw method that should be injected into the game loop.
+ /// This method should render any assets, surfaces, textures, animations to a renderer.
+ ///
+ Action Draw { set; }
+
+ /// Defines the UnloadContent method that should be injected into the game loop.
+ /// This method should dispose of any game assets (especially any unmanaged resources).
+ ///
+ Action UnloadContent { set; }
+
+ /// Used to create windows in which a renderer will display assets.
+ ///
+ IWindowFactory WindowFactory { get; }
+
+ /// Used to create renderers tied to a window in which assets will be displayed.
+ ///
+ IRendererFactory RendererFactory { get; }
+
+ /// Used to subscribe to game loop events.
+ ///
+ IEventManager EventManager { get; }
+
+ /// Starts the game engine by initializing chosen subsystems.
+ /// This method should start the game loop and utilize the various Action properties
+ /// defined on the IGameEngine interface.
+ ///
+ /// Enumerated subsystems to initialize.
+ void Start(InitializeType initilizeTypes);
+
+ ///
+ /// Ends the game engine.
+ /// This method should dispose of and stop any subsystems and remaining assets.
+ ///
+ void End();
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/InitializeType.cs b/SharpDL/InitializeType.cs
new file mode 100644
index 0000000..84b823a
--- /dev/null
+++ b/SharpDL/InitializeType.cs
@@ -0,0 +1,20 @@
+using System;
+using SDL2;
+
+namespace SharpDL
+{
+ [Flags]
+ public enum InitializeType : uint
+ {
+ Timer = SDL.SDL_INIT_TIMER,
+ Audio = SDL.SDL_INIT_AUDIO,
+ Video = SDL.SDL_INIT_VIDEO,
+ Joystick = SDL.SDL_INIT_JOYSTICK,
+ Haptic = SDL.SDL_INIT_HAPTIC,
+ GameController = SDL.SDL_INIT_GAMECONTROLLER,
+ Events = SDL.SDL_INIT_EVENTS,
+ Sensor = SDL.SDL_INIT_SENSOR,
+ Everything = SDL.SDL_INIT_EVERYTHING,
+ NoParachute = SDL.SDL_INIT_NOPARACHUTE
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/MessageBox.cs b/SharpDL/MessageBox.cs
index ab2c6ca..83363c1 100644
--- a/SharpDL/MessageBox.cs
+++ b/SharpDL/MessageBox.cs
@@ -1,31 +1,27 @@
using SDL2;
using SharpDL.Graphics;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace SharpDL
{
- public static class MessageBox
- {
- public enum MessageBoxType : uint
- {
- Error = SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR,
- Information = SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_INFORMATION,
- Warning = SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_WARNING
- }
+ public enum MessageBoxType : uint
+ {
+ Error = SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR,
+ Information = SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_INFORMATION,
+ Warning = SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_WARNING
+ }
- public static void Show(MessageBoxType messageBoxType,
- String title, String message, Window parentWindow = null)
- {
- IntPtr parentWindowHandle = IntPtr.Zero;
- if (parentWindow != null)
- parentWindowHandle = parentWindow.Handle;
+ public static class MessageBox
+ {
+ public static void Show(MessageBoxType messageBoxType,
+ String title, String message, Window parentWindow = null)
+ {
+ IntPtr parentWindowHandle = IntPtr.Zero;
+ if (parentWindow != null)
+ parentWindowHandle = parentWindow.Handle;
- SDL.SDL_ShowSimpleMessageBox((SDL.SDL_MessageBoxFlags)messageBoxType,
- title, message, parentWindowHandle);
- }
- }
+ SDL.SDL_ShowSimpleMessageBox((SDL.SDL_MessageBoxFlags)messageBoxType,
+ title, message, parentWindowHandle);
+ }
+ }
}
diff --git a/SharpDL/Shared/Errors.cs b/SharpDL/Shared/Errors.cs
index 57902f9..bb705d7 100644
--- a/SharpDL/Shared/Errors.cs
+++ b/SharpDL/Shared/Errors.cs
@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SharpDL.Shared
+namespace SharpDL.Shared
{
public static class Errors
{
diff --git a/SharpDL/Shared/ServiceCollectionExtensions.cs b/SharpDL/Shared/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..288d2e9
--- /dev/null
+++ b/SharpDL/Shared/ServiceCollectionExtensions.cs
@@ -0,0 +1,33 @@
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using SharpDL.Events;
+using SharpDL.Graphics;
+
+namespace SharpDL.Shared
+{
+ public static class SharpGameServiceCollectionExtensions
+ {
+ /// Registers the base game services with the specified specified Microsoft.Extensions.DependencyInjection.IServiceCollection.
+ ///
+ /// Collection of services to register base game.
+ /// Type of main game class that inherits from SharpDL.Game to register with container.
+ /// The Microsoft.Extensions.DependencyInjection.IServiceCollection so that additional calls can be chained.
+ public static IServiceCollection AddSharpGame(this IServiceCollection services)
+ where T : class, IGame
+ {
+ if(services == null)
+ {
+ throw new ArgumentNullException(nameof(services));
+ }
+
+ services.TryAdd(ServiceDescriptor.Singleton());
+ services.TryAdd(ServiceDescriptor.Singleton());
+ services.TryAdd(ServiceDescriptor.Singleton());
+ services.TryAdd(ServiceDescriptor.Singleton());
+ services.TryAdd(ServiceDescriptor.Singleton());
+
+ return services;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SharpDL/Shared/Utilities.cs b/SharpDL/Shared/Utilities.cs
index 1f33447..c6c1b12 100644
--- a/SharpDL/Shared/Utilities.cs
+++ b/SharpDL/Shared/Utilities.cs
@@ -1,9 +1,5 @@
using SDL2;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace SharpDL.Shared
{
diff --git a/SharpDL/SharpDL.csproj b/SharpDL/SharpDL.csproj
index 857aa00..6c91963 100644
--- a/SharpDL/SharpDL.csproj
+++ b/SharpDL/SharpDL.csproj
@@ -23,6 +23,8 @@
+
+
\ No newline at end of file
diff --git a/SharpDL/Timer.cs b/SharpDL/Timer.cs
index 3663d6e..6c6a178 100644
--- a/SharpDL/Timer.cs
+++ b/SharpDL/Timer.cs
@@ -1,22 +1,18 @@
using SDL2;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace SharpDL
{
public class Timer
{
- private UInt32 startTicks;
- private UInt32 pausedTicks;
- bool isStarted;
- bool isPaused;
+ private uint startedAtTicks = 0;
+ private uint pausedTicks = 0;
+ private bool isStarted = false;
+ private bool isPaused = false;
- public TimeSpan StartTime
+ public TimeSpan StartedAtTime
{
- get { return TimeSpan.FromMilliseconds((double)startTicks); }
+ get { return TimeSpan.FromMilliseconds((double)startedAtTicks); }
}
public TimeSpan ElapsedTime
@@ -28,7 +24,7 @@ public TimeSpan ElapsedTime
if (isPaused)
return TimeSpan.FromMilliseconds((double)pausedTicks);
else
- return TimeSpan.FromMilliseconds((double)(SDL.SDL_GetTicks() - startTicks));
+ return TimeSpan.FromMilliseconds((double)(SDL.SDL_GetTicks() - startedAtTicks));
}
else
return TimeSpan.Zero;
@@ -39,19 +35,11 @@ public TimeSpan ElapsedTime
public bool IsPaused { get { return isPaused; } }
- public Timer()
- {
- startTicks = 0;
- pausedTicks = 0;
- isPaused = false;
- isStarted = false;
- }
-
public void Start()
{
isStarted = true;
isPaused = false;
- startTicks = SDL.SDL_GetTicks();
+ startedAtTicks = SDL.SDL_GetTicks();
}
public void Stop()
@@ -65,7 +53,7 @@ public void Pause()
if (isStarted && !isPaused)
{
isPaused = true;
- pausedTicks = SDL.SDL_GetTicks() - startTicks;
+ pausedTicks = SDL.SDL_GetTicks() - startedAtTicks;
}
}
@@ -74,7 +62,7 @@ public void Unpause()
if (isPaused)
{
isPaused = false;
- startTicks = SDL.SDL_GetTicks() - pausedTicks;
+ startedAtTicks = SDL.SDL_GetTicks() - pausedTicks;
pausedTicks = 0;
}
}