Improved window and input handling.

Window now has some events.

Keyboard and mouse implemented.
This commit is contained in:
Harrison Deng 2020-05-27 20:21:28 -05:00
parent e6230c013b
commit af7c1b37f9
15 changed files with 610 additions and 86 deletions

View File

@ -0,0 +1,7 @@
namespace SlatedGameToolkit.Framework.DataTypes
{
public struct Colour {
public volatile float r, g, b;
}
}

View File

@ -4,22 +4,25 @@ using SDL2;
using Serilog;
using Serilog.Core;
using SlatedGameToolkit.Framework.Exceptions;
using SlatedGameToolkit.Framework.Graphics;
using SlatedGameToolkit.Framework.Graphics.Window;
using SlatedGameToolkit.Framework.Input;
using SlatedGameToolkit.Framework.Input.Devices;
using SlatedGameToolkit.Framework.StateSystem;
using SlatedGameToolkit.Framework.StateSystem.States;
namespace SlatedGameToolkit.Framework {
/// <summary>
/// The main engine that will host the game loop.
/// </summary>
public static class GameEngine {
public static readonly Logger logger = new LoggerConfiguration().
WriteTo.File("SlatedGameToolKit.Framework.Log", rollingInterval: RollingInterval.Day, fileSizeLimitBytes: 1048576, rollOnFileSizeLimit: true).
CreateLogger();
public static Logger Logger { get; private set; }
private static readonly object ignitionLock = new object();
private static readonly object deltaUpdateLock = new object();
private static Thread thread;
private static volatile bool exit = false, stopped = true;
private static volatile bool deltaChanged;
private static TimeSpan updateDeltaTime = TimeSpan.FromSeconds(1d/200d), frameDeltaTime = TimeSpan.FromSeconds(1d/60d);
private static TimeSpan updateDeltaTime = TimeSpan.FromSeconds(1d/40d), frameDeltaTime = TimeSpan.FromSeconds(1d/60d);
/// <summary>
/// The amount of updates per second.
@ -66,16 +69,10 @@ namespace SlatedGameToolkit.Framework {
}
}
public static bool Running {
get {
return !stopped;
}
}
private static void Loop(Object o) {
if (!(o is Manager)) throw new InternalFrameworkException(String.Format("Expected manager object for asynchronous loop. Got {0}", o));
Manager manager = (Manager) o;
manager.initialize();
if (!(o is IState)) throw new InternalFrameworkException(String.Format("Expected initial state object for asynchronous loop. Got {0}", o));
IState initialState = (IState) o;
Manager manager = new Manager(initialState);
DateTime currentTime = DateTime.Now;
TimeSpan timePassedFromLastUpdate;
TimeSpan timePassedFromLastRender;
@ -83,13 +80,14 @@ namespace SlatedGameToolkit.Framework {
TimeSpan frameDeltaTime;
deltaChanged = true;
stopped = false;
logger.Information("Game engine initiated.");
Logger.Information("Game engine initiated.");
while (!exit) {
//Pull latest deltas.
if (deltaChanged) {
lock (deltaUpdateLock) {
updateDeltaTime = GameEngine.updateDeltaTime;
frameDeltaTime = GameEngine.frameDeltaTime;
logger.Information(String.Format("Deltas were set. Update Delta: {0}, Render Delta: {1}", updateDeltaTime.TotalSeconds, frameDeltaTime.TotalSeconds));
Logger.Information(String.Format("Deltas were set. Update Delta: {0}, Render target Delta: {1}", updateDeltaTime.TotalSeconds, frameDeltaTime.TotalSeconds));
}
deltaChanged = false;
}
@ -100,20 +98,78 @@ namespace SlatedGameToolkit.Framework {
timePassedFromLastUpdate += difference;
while (timePassedFromLastUpdate > updateDeltaTime) {
//Events
SDL.SDL_Event SDL_Event;
while (SDL.SDL_PollEvent(out SDL_Event) != 0) {
switch (SDL_Event.type) {
case SDL.SDL_EventType.SDL_MOUSEMOTION:
Mouse.OnMouseMoved(SDL_Event.motion.x, SDL_Event.motion.y);
break;
case SDL.SDL_EventType.SDL_MOUSEWHEEL:
Mouse.OnScroll(SDL_Event.wheel.x, SDL_Event.wheel.y);
break;
case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN:
if (SDL.SDL_BUTTON_LEFT == SDL_Event.button.button) {
Mouse.OnLeftChange(true);
} else if (SDL.SDL_BUTTON_RIGHT == SDL_Event.button.button) {
Mouse.OnRightChange(true);
} else if (SDL.SDL_BUTTON_MIDDLE == SDL_Event.button.button) {
Mouse.OnMiddleChange(true);
}
break;
case SDL.SDL_EventType.SDL_MOUSEBUTTONUP:
if (SDL.SDL_BUTTON_LEFT == SDL_Event.button.button) {
Mouse.OnLeftChange(false);
} else if (SDL.SDL_BUTTON_RIGHT == SDL_Event.button.button) {
Mouse.OnRightChange(false);
} else if (SDL.SDL_BUTTON_MIDDLE == SDL_Event.button.button) {
Mouse.OnMiddleChange(false);
}
break;
case SDL.SDL_EventType.SDL_KEYDOWN:
Keyboard.OnKeyPressed((Key) SDL_Event.key.keysym.sym);
break;
case SDL.SDL_EventType.SDL_KEYUP:
Keyboard.OnKeyReleased((Key) SDL_Event.key.keysym.sym);
break;
case SDL.SDL_EventType.SDL_WINDOWEVENT:
WindowHandle handle = WindowManager.HandleFromID(SDL_Event.window.windowID);
switch (SDL_Event.window.windowEvent)
{
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED:
handle.OnResize(SDL_Event.window.data1, SDL_Event.window.data2);
break;
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST:
handle.OnFocusLost();
break;
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED:
handle.OnFocusGained();
break;
}
break;
case SDL.SDL_EventType.SDL_QUIT:
Stop();
break;
}
}
//Updates.
manager.update(updateDeltaTime.TotalMilliseconds);
timePassedFromLastUpdate -= updateDeltaTime;
}
timePassedFromLastRender += difference;
if (timePassedFromLastRender > frameDeltaTime) {
//Draw calls.
manager.render(timePassedFromLastUpdate.TotalMilliseconds / updateDeltaTime.TotalMilliseconds);
timePassedFromLastRender = TimeSpan.Zero;
}
}
manager.removeAllStates();
stopped = true;
manager.Dispose();
SDL.SDL_Quit();
logger.Information("Game engine has stopped.");
Logger.Information("Game engine has stopped.");
Logger.Dispose();
Logger = null;
}
/// <summary>
@ -134,25 +190,28 @@ namespace SlatedGameToolkit.Framework {
/// Requests to start the game engine.
/// </summary>
/// <returns>True iff the engine is not already running.</returns>
public static bool Ignite(Manager manager) {
if (manager == null) throw new ArgumentNullException("manager");
SDL.SDL_version SDLVersion;
SDL.SDL_version SDLBuiltVersion;
SDL.SDL_GetVersion(out SDLVersion);
SDL.SDL_VERSION(out SDLBuiltVersion);
logger.Information(String.Format("Attempting to initiate game engine with SDL version: {0}.{1}.{2}", SDLVersion.major, SDLVersion.minor, SDLVersion.patch));
if (!SDL.SDL_VERSION_ATLEAST(SDLBuiltVersion.major, SDLBuiltVersion.minor, SDLBuiltVersion.patch)) {
logger.Warning(String.Format("Engine was designed with SDL version {0}.{1}.{2}, currently running on {3}.{4}.{5}", SDLBuiltVersion.major, SDLBuiltVersion.minor, SDLBuiltVersion.patch, SDLVersion.major, SDLVersion.minor, SDLVersion.patch));
if (SDLVersion.major < 2) {
logger.Error("This engine was designed to work with SDL2. The version you're currently running is severely outdated.");
throw new FrameworkUsageException("Outdated SDL binaries.");
}
}
public static bool Ignite(IState initialState) {
lock (ignitionLock) {
if (initialState == null) throw new ArgumentNullException("initialState");
if (!stopped) {
logger.Warning("Engine is already running.");
Logger.Warning("Engine is already running.");
return false;
}
GameEngine.Logger = new LoggerConfiguration().MinimumLevel.Debug().
WriteTo.File("SlatedGameToolKit.log", fileSizeLimitBytes: 1048576, rollOnFileSizeLimit: true).
CreateLogger();
SDL.SDL_version SDLVersion;
SDL.SDL_version SDLBuiltVersion;
SDL.SDL_GetVersion(out SDLVersion);
SDL.SDL_VERSION(out SDLBuiltVersion);
Logger.Information(String.Format("Attempting to initiate game engine with SDL version: {0}.{1}.{2}", SDLVersion.major, SDLVersion.minor, SDLVersion.patch));
if (!SDL.SDL_VERSION_ATLEAST(SDLBuiltVersion.major, SDLBuiltVersion.minor, SDLBuiltVersion.patch)) {
Logger.Warning(String.Format("Engine was designed with SDL version {0}.{1}.{2}, currently running on {3}.{4}.{5}", SDLBuiltVersion.major, SDLBuiltVersion.minor, SDLBuiltVersion.patch, SDLVersion.major, SDLVersion.minor, SDLVersion.patch));
if (SDLVersion.major < 2) {
Logger.Error("This engine was designed to work with SDL2. The version you're currently running is severely outdated.");
throw new FrameworkUsageException("Outdated SDL binaries.");
}
}
exit = false;
if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO) != 0) {
throw new FrameworkSDLException();
@ -166,9 +225,15 @@ namespace SlatedGameToolkit.Framework {
if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE) < 0) throw new FrameworkSDLException();
thread = new Thread(Loop);
thread.Start(manager);
thread.Name = "SlatedGameToolkit Engine";
thread.Priority = ThreadPriority.AboveNormal;
thread.Start(initialState);
return true;
}
}
public static bool IsRunning() {
return !stopped;
}
}
}

View File

@ -0,0 +1,6 @@
namespace SlatedGameToolkit.Framework.Graphics
{
public delegate void GLClearColour(float r, float g, float b);
public delegate void GLClear(uint flags);
public delegate void GLViewport(int x, int y, int width, int height);
}

View File

@ -14,6 +14,8 @@ namespace SlatedGameToolkit.Framework.Graphics.Window
/// <param name="hitPoint">The window coordinates that are being interacted with.</param>
/// <returns>The region type.</returns>
public delegate WindowRegion WindowRegionHit(FloatVector2 hitPoint);
public delegate void WindowResize(int width, int height);
public delegate void WindowOperation();
/// <summary>
/// A handle for a window.
@ -24,18 +26,27 @@ namespace SlatedGameToolkit.Framework.Graphics.Window
/// <summary>
/// The pointer referencing the SDL window.
/// </summary>
public readonly IntPtr window;
private readonly IntPtr window;
/// <summary>
/// The pointer referencing the OpenGL context.
/// </summary>
public readonly IntPtr glContext;
private readonly IntPtr glContext;
public event WindowOperation focusGainedEvent, focusLostEvent;
/// <summary>
/// Invoked when this window resizes.
/// </summary>
public event WindowResize resizeEvent;
/// <summary>
/// Event for when the window is being interacted with.
/// </summary>
public event WindowRegionHit windowRegionHitEvent;
public readonly GLClearColour clearColour;
public readonly GLClear clear;
public readonly GLViewport viewport;
/// <summary>
/// Whether or not to show this window.
/// </summary>
@ -54,6 +65,36 @@ namespace SlatedGameToolkit.Framework.Graphics.Window
}
}
public bool VSync {
get {
IntPtr currentContext = SDL.SDL_GL_GetCurrentContext();
IntPtr currentWindow = SDL.SDL_GL_GetCurrentWindow();
bool diff = false;
if (currentContext != glContext || currentWindow != window) {
DrawToWindow();
diff = true;
}
bool vSync = SDL.SDL_GL_GetSwapInterval() != 0;
if (diff) SDL.SDL_GL_MakeCurrent(currentWindow, currentContext);
return vSync;
}
set {
IntPtr currentContext = SDL.SDL_GL_GetCurrentContext();
IntPtr currentWindow = SDL.SDL_GL_GetCurrentWindow();
bool diff = false;
if (currentContext != glContext || currentWindow != window) {
DrawToWindow();
diff = true;
}
if (SDL.SDL_GL_SetSwapInterval(value ? -1 : 0) < 0) {
if (SDL.SDL_GL_SetSwapInterval(value ? 1 : 0) < 0) {
throw new OptionalSDLException();
}
}
if (diff) SDL.SDL_GL_MakeCurrent(currentWindow, currentContext);
}
}
/// <summary>
/// The title displayed on the window.
/// </summary>
@ -189,8 +230,12 @@ namespace SlatedGameToolkit.Framework.Graphics.Window
}
}
public WindowHandle(string title, int x, int y, int width, int height, bool specialRegions, WindowOptions options) {
window = SDL.SDL_CreateWindow(title, x, y, width, height, SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL | options.ToSDLWindowFlag());
/// <summary>
/// Instantiates a window handle.
/// </summary>
public WindowHandle(string title, int x = -1, int y = -1, int width = 640, int height = 480, bool specialRegions = false, WindowOption options = default(WindowOption)) {
GameEngine.Logger.Information(String.Format("Starting openGL window with title \"{0}\"", title));
window = SDL.SDL_CreateWindow(title, x < 0 ? SDL.SDL_WINDOWPOS_CENTERED : x, y < 0 ? SDL.SDL_WINDOWPOS_CENTERED : y, width, height, SDL.SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS | SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL.SDL_WindowFlags.SDL_WINDOW_MOUSE_FOCUS | (SDL.SDL_WindowFlags) options);
if (window == null) {
throw new FrameworkSDLException();
}
@ -198,9 +243,19 @@ namespace SlatedGameToolkit.Framework.Graphics.Window
if (glContext == null) {
throw new FrameworkSDLException();
}
if (SDL.SDL_SetWindowHitTest(window, SpecialRegionHit, IntPtr.Zero) < 0) {
throw new OptionalSDLException();
clear = Marshal.GetDelegateForFunctionPointer<GLClear>(SDL.SDL_GL_GetProcAddress("glClear"));
if (clear == null) throw new FrameworkSDLException();
clearColour = Marshal.GetDelegateForFunctionPointer<GLClearColour>(SDL.SDL_GL_GetProcAddress("glClearColor"));
if (clearColour == null) throw new FrameworkSDLException();
viewport = Marshal.GetDelegateForFunctionPointer<GLViewport>(SDL.SDL_GL_GetProcAddress("glViewport"));
if (viewport == null) throw new FrameworkSDLException();
if (specialRegions) {
if (SDL.SDL_SetWindowHitTest(window, SpecialRegionHit, IntPtr.Zero) < 0) {
throw new OptionalSDLException();
}
}
WindowManager.RegisterWindow(this);
}
/// <summary>
@ -210,19 +265,30 @@ namespace SlatedGameToolkit.Framework.Graphics.Window
///
/// If the current context is already of this window, nothing happens.
/// </summary>
public void DrawToWindow() {
internal void DrawToWindow() {
if (SDL.SDL_GL_GetCurrentContext() != glContext) {
SDL.SDL_GL_MakeCurrent(window, glContext);
}
}
internal void SwapBuffer() {
SDL.SDL_GL_SwapWindow(window);
}
/// <summary>
/// Raises this window to be above other windows.
/// Attempts to raise the window to the top.
/// </summary>
public void RaiseToTop() {
SDL.SDL_RestoreWindow(window);
SDL.SDL_RaiseWindow(window);
}
public uint GetWindowID() {
uint id = SDL.SDL_GetWindowID(window);
if (id == 0) throw new FrameworkSDLException();
return id;
}
/// <summary>
/// Gets the index of the display that this window resides within.
/// </summary>
@ -237,7 +303,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Window
SDL.SDL_Point SDLPoint = Marshal.PtrToStructure<SDL.SDL_Point>(hitPtr);
FloatVector2 point = new FloatVector2(SDLPoint.x, SDLPoint.y);
WindowRegion region = windowRegionHitEvent.Invoke(point);
return region.ToSDLHitTestResult();
return (SDL.SDL_HitTestResult) region;
}
/// <summary>
@ -245,10 +311,23 @@ namespace SlatedGameToolkit.Framework.Graphics.Window
/// </summary>
public void Dispose()
{
WindowManager.DeregisterWindow(this);
SDL.SDL_GL_DeleteContext(glContext);
SDL.SDL_DestroyWindow(window);
}
internal void OnResize(int width, int height) {
resizeEvent?.Invoke(width, height);
}
internal void OnFocusLost() {
focusLostEvent?.Invoke();
}
internal void OnFocusGained() {
focusGainedEvent?.Invoke();
}
~WindowHandle() {
Dispose();
}

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace SlatedGameToolkit.Framework.Graphics.Window
{
internal static class WindowManager {
private static Dictionary<uint, WindowHandle> existingWindows = new Dictionary<uint, WindowHandle>();
internal static void RegisterWindow(WindowHandle windowHandle) {
GameEngine.Logger.Debug("Registering window: " + windowHandle.GetWindowID());
existingWindows.Add(windowHandle.GetWindowID(), windowHandle);
}
internal static void DeregisterWindow(WindowHandle windowHandle) {
GameEngine.Logger.Debug("Deregistering window: " + windowHandle.GetWindowID());
existingWindows.Remove(windowHandle.GetWindowID());
}
internal static WindowHandle HandleFromID(uint ID) {
return existingWindows[ID];
}
}
}

View File

@ -16,13 +16,4 @@ namespace SlatedGameToolkit.Framework.Graphics.Window
RIGHT = SDL.SDL_HitTestResult.SDL_HITTEST_RESIZE_RIGHT,
LEFT = SDL.SDL_HitTestResult.SDL_HITTEST_RESIZE_LEFT,
}
internal static class WindowRegionExtensions {
public static SDL.SDL_HitTestResult ToSDLHitTestResult(this WindowRegion region) {
SDL.SDL_HitTestResult result;
if (!Enum.TryParse(((int) region).ToString(), out result)) throw new InternalFrameworkException("Unable to convert window regions.");
return result;
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using SDL2;
using SlatedGameToolkit.Framework.Exceptions;
namespace SlatedGameToolkit.Framework.Graphics.Window
{
public enum WindowOption : uint
{
SHOWN = SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN,
HIGH_DPI = SDL.SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI,
HIDDEN = SDL.SDL_WindowFlags.SDL_WINDOW_HIDDEN,
}
}

View File

@ -1,19 +0,0 @@
using System;
using SDL2;
using SlatedGameToolkit.Framework.Exceptions;
namespace SlatedGameToolkit.Framework.Graphics.Window
{
public enum WindowOptions : uint
{
HIGH_DPI = SDL.SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI,
HIDDEN = SDL.SDL_WindowFlags.SDL_WINDOW_HIDDEN,
}
internal static class WindowOptionsExtension {
public static SDL.SDL_WindowFlags ToSDLWindowFlag(this WindowOptions options) {
SDL.SDL_WindowFlags sdlFlag;
if (!(Enum.TryParse(((uint) options).ToString(), out sdlFlag))) throw new InternalFrameworkException("Unable to convert window options.");
return sdlFlag;
}
}
}

View File

@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace SlatedGameToolkit.Framework.Input.Devices
{
public delegate void keyboardUpdate(Key keys, bool pressed);
public static class Keyboard {
private static HashSet<Key> pressedKeys = new HashSet<Key>();
/// <summary>
/// Whenever a keyboard update occurs.
/// </summary>
public static event keyboardUpdate keyboardUpdateEvent;
public static bool IsKeyPressed(Key key) {
return pressedKeys.Contains(key);
}
internal static void OnKeyPressed(Key key) {
pressedKeys.Add(key);
keyboardUpdateEvent?.Invoke(key, true);
}
internal static void OnKeyReleased(Key key) {
pressedKeys.Remove(key);
keyboardUpdateEvent?.Invoke(key, false);
}
}
}

View File

@ -0,0 +1,41 @@
namespace SlatedGameToolkit.Framework.Input.Devices
{
public delegate void MouseUpdate(bool leftDown, bool rightDown, bool middle, int x, int y, int scrollX, int scrollY);
public static class Mouse
{
public static event MouseUpdate mouseUpdateEvent;
public static bool LeftButtonPressed { get; private set; }
public static bool RightButtonPressed { get; private set; }
public static bool MiddleButtonPressed { get; private set; }
public static int ScrollChangeX { get; private set; }
public static int ScrollChangeY { get; private set; }
public static int X { get; private set; }
public static int Y{ get; private set; }
internal static void OnMouseMoved(int x, int y) {
X = x;
Y = y;
}
internal static void OnLeftChange(bool down) {
LeftButtonPressed = down;
mouseUpdateEvent?.Invoke(down, RightButtonPressed, MiddleButtonPressed, X, Y, ScrollChangeX, ScrollChangeY);
}
internal static void OnRightChange(bool down) {
RightButtonPressed = down;
mouseUpdateEvent?.Invoke(LeftButtonPressed, down, MiddleButtonPressed, X, Y, ScrollChangeX, ScrollChangeY);
}
internal static void OnMiddleChange(bool down) {
RightButtonPressed = down;
mouseUpdateEvent?.Invoke(LeftButtonPressed, RightButtonPressed, down, X, Y, ScrollChangeX, ScrollChangeY);
}
internal static void OnScroll(int scrollX, int scrollY) {
ScrollChangeX = scrollX;
ScrollChangeY = scrollY;
mouseUpdateEvent?.Invoke(LeftButtonPressed, RightButtonPressed, MiddleButtonPressed, X, Y, scrollX, scrollY);
}
}
}

View File

@ -0,0 +1,262 @@
using SDL2;
namespace SlatedGameToolkit.Framework.Input
{
public enum Key
{
K_RETURN = SDL.SDL_Keycode.SDLK_RETURN,
K_ESCAPE = SDL.SDL_Keycode.SDLK_ESCAPE, // '\033'
K_BACKSPACE = SDL.SDL_Keycode.SDLK_BACKSPACE,
K_TAB = SDL.SDL_Keycode.SDLK_TAB,
K_SPACE = SDL.SDL_Keycode.SDLK_SPACE,
K_EXCLAIM = SDL.SDL_Keycode.SDLK_EXCLAIM,
K_QUOTEDBL = SDL.SDL_Keycode.SDLK_QUOTEDBL,
K_HASH = SDL.SDL_Keycode.SDLK_HASH,
K_PERCENT = SDL.SDL_Keycode.SDLK_PERCENT,
K_DOLLAR = SDL.SDL_Keycode.SDLK_DOLLAR,
K_AMPERSAND = SDL.SDL_Keycode.SDLK_AMPERSAND,
K_QUOTE = SDL.SDL_Keycode.SDLK_QUOTE,
K_LEFTPAREN = SDL.SDL_Keycode.SDLK_KP_LEFTPAREN,
K_RIGHTPAREN = SDL.SDL_Keycode.SDLK_RIGHTPAREN,
K_ASTERISK = SDL.SDL_Keycode.SDLK_ASTERISK,
K_PLUS = SDL.SDL_Keycode.SDLK_PLUS,
K_COMMA = SDL.SDL_Keycode.SDLK_COMMA,
K_MINUS = SDL.SDL_Keycode.SDLK_MINUS,
K_PERIOD = SDL.SDL_Keycode.SDLK_PERIOD,
K_SLASH = SDL.SDL_Keycode.SDLK_SLASH,
K_0 = SDL.SDL_Keycode.SDLK_0,
K_1 = SDL.SDL_Keycode.SDLK_1,
K_2 = SDL.SDL_Keycode.SDLK_2,
K_3 = SDL.SDL_Keycode.SDLK_3,
K_4 = SDL.SDL_Keycode.SDLK_4,
K_5 = SDL.SDL_Keycode.SDLK_5,
K_6 = SDL.SDL_Keycode.SDLK_6,
K_7 = SDL.SDL_Keycode.SDLK_7,
K_8 = SDL.SDL_Keycode.SDLK_8,
K_9 = SDL.SDL_Keycode.SDLK_9,
K_COLON = SDL.SDL_Keycode.SDLK_COLON,
K_SEMICOLON = SDL.SDL_Keycode.SDLK_SEMICOLON,
K_LESS = SDL.SDL_Keycode.SDLK_LESS,
K_EQUALS = SDL.SDL_Keycode.SDLK_EQUALS,
K_GREATER = SDL.SDL_Keycode.SDLK_GREATER,
K_QUESTION = SDL.SDL_Keycode.SDLK_QUESTION,
K_AT = SDL.SDL_Keycode.SDLK_AT,
/*
Skip uppercase letters
*/
K_LEFTBRACKET = SDL.SDL_Keycode.SDLK_LEFTBRACKET,
K_BACKSLASH = SDL.SDL_Keycode.SDLK_BACKSLASH,
K_RIGHTBRACKET = SDL.SDL_Keycode.SDLK_RIGHTBRACKET,
K_CARET = SDL.SDL_Keycode.SDLK_CARET,
K_UNDERSCORE = SDL.SDL_Keycode.SDLK_UNDERSCORE,
K_BACKQUOTE = SDL.SDL_Keycode.SDLK_BACKQUOTE,
K_a = SDL.SDL_Keycode.SDLK_a,
K_b = SDL.SDL_Keycode.SDLK_b,
K_c = SDL.SDL_Keycode.SDLK_c,
K_d = SDL.SDL_Keycode.SDLK_d,
K_e = SDL.SDL_Keycode.SDLK_e,
K_f = SDL.SDL_Keycode.SDLK_f,
K_g = SDL.SDL_Keycode.SDLK_g,
K_h = SDL.SDL_Keycode.SDLK_h,
K_i = SDL.SDL_Keycode.SDLK_i,
K_j = SDL.SDL_Keycode.SDLK_j,
K_k = SDL.SDL_Keycode.SDLK_k,
K_l = SDL.SDL_Keycode.SDLK_l,
K_m = SDL.SDL_Keycode.SDLK_m,
K_n = SDL.SDL_Keycode.SDLK_n,
K_o = SDL.SDL_Keycode.SDLK_o,
K_p = SDL.SDL_Keycode.SDLK_p,
K_q = SDL.SDL_Keycode.SDLK_q,
K_r = SDL.SDL_Keycode.SDLK_r,
K_s = SDL.SDL_Keycode.SDLK_s,
K_t = SDL.SDL_Keycode.SDLK_t,
K_u = SDL.SDL_Keycode.SDLK_u,
K_v = SDL.SDL_Keycode.SDLK_v,
K_w = SDL.SDL_Keycode.SDLK_w,
K_x = SDL.SDL_Keycode.SDLK_x,
K_y = SDL.SDL_Keycode.SDLK_y,
K_z = SDL.SDL_Keycode.SDLK_z,
K_CAPSLOCK = SDL.SDL_Keycode.SDLK_CAPSLOCK,
K_F1 = SDL.SDL_Keycode.SDLK_F1,
K_F2 = SDL.SDL_Keycode.SDLK_F2,
K_F3 = SDL.SDL_Keycode.SDLK_F3,
K_F4 = SDL.SDL_Keycode.SDLK_F4,
K_F5 = SDL.SDL_Keycode.SDLK_F5,
K_F6 = SDL.SDL_Keycode.SDLK_F6,
K_F7 = SDL.SDL_Keycode.SDLK_F7,
K_F8 = SDL.SDL_Keycode.SDLK_F8,
K_F9 = SDL.SDL_Keycode.SDLK_F9,
K_F10 = SDL.SDL_Keycode.SDLK_F10,
K_F11 = SDL.SDL_Keycode.SDLK_F11,
K_F12 = SDL.SDL_Keycode.SDLK_F12,
K_PRINTSCREEN = SDL.SDL_Keycode.SDLK_PRINTSCREEN,
K_SCROLLLOCK = SDL.SDL_Keycode.SDLK_SCROLLLOCK,
K_PAUSE = SDL.SDL_Keycode.SDLK_PAUSE,
K_INSERT = SDL.SDL_Keycode.SDLK_INSERT,
K_HOME = SDL.SDL_Keycode.SDLK_HOME,
K_PAGEUP = SDL.SDL_Keycode.SDLK_PAGEUP,
K_DELETE = SDL.SDL_Keycode.SDLK_DELETE,
K_END = SDL.SDL_Keycode.SDLK_END,
K_PAGEDOWN = SDL.SDL_Keycode.SDLK_PAGEDOWN,
K_RIGHT = SDL.SDL_Keycode.SDLK_RIGHT,
K_LEFT = SDL.SDL_Keycode.SDLK_LEFT,
K_DOWN = SDL.SDL_Keycode.SDLK_DOWN,
K_UP = SDL.SDL_Keycode.SDLK_UP,
K_NUMLOCKCLEAR = SDL.SDL_Keycode.SDLK_NUMLOCKCLEAR,
K_KP_DIVIDE = SDL.SDL_Keycode.SDLK_KP_DIVIDE,
K_KP_MULTIPLY = SDL.SDL_Keycode.SDLK_KP_MULTIPLY,
K_KP_MINUS = SDL.SDL_Keycode.SDLK_MINUS,
K_KP_PLUS = SDL.SDL_Keycode.SDLK_KP_PLUS,
K_KP_ENTER = SDL.SDL_Keycode.SDLK_KP_ENTER,
K_KP_1 = SDL.SDL_Keycode.SDLK_KP_1,
K_KP_2 = SDL.SDL_Keycode.SDLK_KP_2,
K_KP_3 = SDL.SDL_Keycode.SDLK_KP_3,
K_KP_4 = SDL.SDL_Keycode.SDLK_KP_4,
K_KP_5 = SDL.SDL_Keycode.SDLK_KP_5,
K_KP_6 = SDL.SDL_Keycode.SDLK_KP_6,
K_KP_7 = SDL.SDL_Keycode.SDLK_KP_7,
K_KP_8 = SDL.SDL_Keycode.SDLK_KP_8,
K_KP_9 = SDL.SDL_Keycode.SDLK_KP_9,
K_KP_0 = SDL.SDL_Keycode.SDLK_KP_0,
K_KP_PERIOD = SDL.SDL_Keycode.SDLK_KP_0,
K_APPLICATION = SDL.SDL_Keycode.SDLK_APPLICATION,
K_POWER = SDL.SDL_Keycode.SDLK_POWER,
K_KP_EQUALS = SDL.SDL_Keycode.SDLK_EQUALS,
K_F13 = SDL.SDL_Keycode.SDLK_F13,
K_F14 = SDL.SDL_Keycode.SDLK_F14,
K_F15 = SDL.SDL_Keycode.SDLK_F15,
K_F16 = SDL.SDL_Keycode.SDLK_F16,
K_F17 = SDL.SDL_Keycode.SDLK_F17,
K_F18 = SDL.SDL_Keycode.SDLK_F18,
K_F19 = SDL.SDL_Keycode.SDLK_F19,
K_F20 = SDL.SDL_Keycode.SDLK_F20,
K_F21 = SDL.SDL_Keycode.SDLK_F21,
K_F22 = SDL.SDL_Keycode.SDLK_F22,
K_F23 = SDL.SDL_Keycode.SDLK_F23,
K_F24 = SDL.SDL_Keycode.SDLK_F24,
K_EXECUTE = SDL.SDL_Keycode.SDLK_EXECUTE,
K_HELP = SDL.SDL_Keycode.SDLK_HELP,
K_MENU = SDL.SDL_Keycode.SDLK_MENU,
K_SELECT = SDL.SDL_Keycode.SDLK_SELECT,
K_STOP = SDL.SDL_Keycode.SDLK_STOP,
K_AGAIN = SDL.SDL_Keycode.SDLK_AGAIN,
K_UNDO = SDL.SDL_Keycode.SDLK_UNDO,
K_CUT = SDL.SDL_Keycode.SDLK_CUT,
K_COPY = SDL.SDL_Keycode.SDLK_COPY,
K_PASTE = SDL.SDL_Keycode.SDLK_PASTE,
K_FIND = SDL.SDL_Keycode.SDLK_FIND,
K_MUTE = SDL.SDL_Keycode.SDLK_MUTE,
K_VOLUMEUP = SDL.SDL_Keycode.SDLK_VOLUMEUP,
K_VOLUMEDOWN = SDL.SDL_Keycode.SDLK_VOLUMEDOWN,
K_KP_COMMA = SDL.SDL_Keycode.SDLK_COMMA,
K_KP_EQUALSAS400 = SDL.SDL_Keycode.SDLK_KP_EQUALSAS400,
K_ALTERASE = SDL.SDL_Keycode.SDLK_ALTERASE,
K_SYSREQ = SDL.SDL_Keycode.SDLK_SYSREQ,
K_CANCEL = SDL.SDL_Keycode.SDLK_CANCEL,
K_CLEAR = SDL.SDL_Keycode.SDLK_CLEAR,
K_PRIOR = SDL.SDL_Keycode.SDLK_PRIOR,
K_RETURN2 = SDL.SDL_Keycode.SDLK_RETURN2,
K_SEPARATOR = SDL.SDL_Keycode.SDLK_SEPARATOR,
K_OUT = SDL.SDL_Keycode.SDLK_OUT,
K_OPER = SDL.SDL_Keycode.SDLK_OPER,
K_CLEARAGAIN = SDL.SDL_Keycode.SDLK_CLEARAGAIN,
K_CRSEL = SDL.SDL_Keycode.SDLK_CRSEL,
K_EXSEL = SDL.SDL_Keycode.SDLK_EXSEL,
K_KP_00 = SDL.SDL_Keycode.SDLK_KP_00,
K_KP_000 = SDL.SDL_Keycode.SDLK_KP_000,
K_THOUSANDSSEPARATOR = SDL.SDL_Keycode.SDLK_THOUSANDSSEPARATOR,
K_DECIMALSEPARATOR = SDL.SDL_Keycode.SDLK_DECIMALSEPARATOR,
K_CURRENCYUNIT = SDL.SDL_Keycode.SDLK_CURRENCYUNIT,
K_CURRENCYSUBUNIT = SDL.SDL_Keycode.SDLK_CURRENCYSUBUNIT,
K_KP_LEFTPAREN = SDL.SDL_Keycode.SDLK_LEFTPAREN,
K_KP_RIGHTPAREN = SDL.SDL_Keycode.SDLK_KP_RIGHTPAREN,
K_KP_LEFTBRACE = SDL.SDL_Keycode.SDLK_KP_LEFTBRACE,
K_KP_RIGHTBRACE = SDL.SDL_Keycode.SDLK_KP_RIGHTBRACE,
K_KP_TAB = SDL.SDL_Keycode.SDLK_KP_TAB,
K_KP_BACKSPACE = SDL.SDL_Keycode.SDLK_BACKSPACE,
K_KP_A = SDL.SDL_Keycode.SDLK_KP_A,
K_KP_B = SDL.SDL_Keycode.SDLK_KP_B,
K_KP_C = SDL.SDL_Keycode.SDLK_KP_C,
K_KP_D = SDL.SDL_Keycode.SDLK_KP_D,
K_KP_E = SDL.SDL_Keycode.SDLK_KP_E,
K_KP_F = SDL.SDL_Keycode.SDLK_KP_F,
K_KP_XOR = SDL.SDL_Keycode.SDLK_KP_XOR,
K_KP_POWER = SDL.SDL_Keycode.SDLK_KP_POWER,
K_KP_PERCENT = SDL.SDL_Keycode.SDLK_KP_PERCENT,
K_KP_LESS = SDL.SDL_Keycode.SDLK_KP_LESS,
K_KP_GREATER = SDL.SDL_Keycode.SDLK_KP_GREATER,
K_KP_AMPERSAND = SDL.SDL_Keycode.SDLK_KP_AMPERSAND,
K_KP_DBLAMPERSAND = SDL.SDL_Keycode.SDLK_KP_DBLAMPERSAND,
K_KP_VERTICALBAR = SDL.SDL_Keycode.SDLK_KP_VERTICALBAR,
K_KP_DBLVERTICALBAR = SDL.SDL_Keycode.SDLK_KP_DBLVERTICALBAR,
K_KP_COLON = SDL.SDL_Keycode.SDLK_KP_COLON,
K_KP_HASH = SDL.SDL_Keycode.SDLK_KP_HASH,
K_KP_SPACE = SDL.SDL_Keycode.SDLK_KP_SPACE,
K_KP_AT = SDL.SDL_Keycode.SDLK_KP_AT,
K_KP_EXCLAM = SDL.SDL_Keycode.SDLK_KP_EXCLAM,
K_KP_MEMSTORE = SDL.SDL_Keycode.SDLK_KP_MEMSTORE,
K_KP_MEMRECALL = SDL.SDL_Keycode.SDLK_KP_MEMRECALL,
K_KP_MEMCLEAR = SDL.SDL_Keycode.SDLK_KP_MEMCLEAR,
K_KP_MEMADD = SDL.SDL_Keycode.SDLK_KP_MEMADD,
K_KP_MEMSUBTRACT = SDL.SDL_Keycode.SDLK_KP_MEMSUBTRACT,
K_KP_MEMMULTIPLY = SDL.SDL_Keycode.SDLK_KP_MEMMULTIPLY,
K_KP_MEMDIVIDE = SDL.SDL_Keycode.SDLK_KP_MEMDIVIDE,
K_KP_PLUSMINUS = SDL.SDL_Keycode.SDLK_KP_PLUSMINUS,
K_KP_CLEAR = SDL.SDL_Keycode.SDLK_KP_CLEAR,
K_KP_CLEARENTRY = SDL.SDL_Keycode.SDLK_KP_CLEARENTRY,
K_KP_BINARY = SDL.SDL_Keycode.SDLK_KP_BINARY,
K_KP_OCTAL = SDL.SDL_Keycode.SDLK_KP_OCTAL,
K_KP_DECIMAL = SDL.SDL_Keycode.SDLK_KP_DECIMAL,
K_KP_HEXADECIMAL = SDL.SDL_Keycode.SDLK_KP_HEXADECIMAL,
K_LCTRL = SDL.SDL_Keycode.SDLK_LCTRL,
K_LSHIFT = SDL.SDL_Keycode.SDLK_LSHIFT,
K_LALT = SDL.SDL_Keycode.SDLK_LALT,
K_LGUI = SDL.SDL_Keycode.SDLK_LGUI,
K_RCTRL = SDL.SDL_Keycode.SDLK_RCTRL,
K_RSHIFT = SDL.SDL_Keycode.SDLK_RSHIFT,
K_RALT = SDL.SDL_Keycode.SDLK_RALT,
K_RGUI = SDL.SDL_Keycode.SDLK_RGUI,
K_MODE = SDL.SDL_Keycode.SDLK_MODE,
K_AUDIONEXT = SDL.SDL_Keycode.SDLK_AUDIONEXT,
K_AUDIOPREV = SDL.SDL_Keycode.SDLK_AUDIOPREV,
K_AUDIOSTOP = SDL.SDL_Keycode.SDLK_AUDIOSTOP,
K_AUDIOPLAY = SDL.SDL_Keycode.SDLK_AUDIOPLAY,
K_AUDIOMUTE = SDL.SDL_Keycode.SDLK_AUDIOMUTE,
K_MEDIASELECT = SDL.SDL_Keycode.SDLK_MEDIASELECT,
K_WWW = SDL.SDL_Keycode.SDLK_WWW,
K_MAIL = SDL.SDL_Keycode.SDLK_MAIL,
K_CALCULATOR = SDL.SDL_Keycode.SDLK_CALCULATOR,
K_COMPUTER = SDL.SDL_Keycode.SDLK_COMPUTER,
K_AC_SEARCH = SDL.SDL_Keycode.SDLK_AC_SEARCH,
K_AC_HOME = SDL.SDL_Keycode.SDLK_AC_HOME,
K_AC_BACK = SDL.SDL_Keycode.SDLK_AC_BACK,
K_AC_FORWARD = SDL.SDL_Keycode.SDLK_AC_FORWARD,
K_AC_STOP = SDL.SDL_Keycode.SDLK_AC_STOP,
K_AC_REFRESH = SDL.SDL_Keycode.SDLK_AC_REFRESH,
K_AC_BOOKMARKS = SDL.SDL_Keycode.SDLK_AC_BOOKMARKS,
K_BRIGHTNESSDOWN = SDL.SDL_Keycode.SDLK_BRIGHTNESSDOWN,
K_BRIGHTNESSUP = SDL.SDL_Keycode.SDLK_BRIGHTNESSUP,
K_DISPLAYSWITCH = SDL.SDL_Keycode.SDLK_DISPLAYSWITCH,
K_KBDILLUMTOGGLE = SDL.SDL_Keycode.SDLK_KBDILLUMTOGGLE,
K_KBDILLUMDOWN = SDL.SDL_Keycode.SDLK_KBDILLUMDOWN,
K_KBDILLUMUP = SDL.SDL_Keycode.SDLK_KBDILLUMUP,
K_EJECT = SDL.SDL_Keycode.SDLK_EJECT,
K_SLEEP = SDL.SDL_Keycode.SDLK_SLEEP,
K_APP1 = SDL.SDL_Keycode.SDLK_APP1,
K_APP2 = SDL.SDL_Keycode.SDLK_APP2,
K_AUDIOREWIND = SDL.SDL_Keycode.SDLK_AUDIOREWIND,
K_AUDIOFASTFORWARD = SDL.SDL_Keycode.SDLK_AUDIOFASTFORWARD
}
}

View File

@ -1,12 +1,18 @@
using System;
using System.Collections.Generic;
using System.Threading;
using SDL2;
using SlatedGameToolkit.Framework.DataTypes;
using SlatedGameToolkit.Framework.Graphics;
using SlatedGameToolkit.Framework.Graphics.Window;
using SlatedGameToolkit.Framework.StateSystem.States;
namespace SlatedGameToolkit.Framework.StateSystem
{
public sealed class Manager {
public sealed class Manager : IDisposable {
public WindowHandle CurrentWindow { get; private set; }
public Thread thread;
public Colour backgroundColour;
private IState currentState;
private IState nextState;
private Dictionary<string, IState> states;
@ -18,16 +24,12 @@ namespace SlatedGameToolkit.Framework.StateSystem
/// </summary>
/// <param name="initialState">The name of the initial state.</param>
/// <param name="states">The initial set of game states to be added.</param>
public Manager(string initialState, params IState[] states) {
internal Manager(IState initialState) {
if (initialState == null) throw new ArgumentNullException("initialState");
thread = Thread.CurrentThread;
this.states = new Dictionary<string, IState>();
thread = Thread.CurrentThread;
addStates(states);
if (!this.states.TryGetValue(initialState, out currentState)) throw new ArgumentException("The requested initial state name does not exist in the provided list of states.");
}
internal void initialize() {
thread = Thread.CurrentThread;
addState(initialState);
currentState = initialState;
}
internal void update(double delta) {
@ -41,7 +43,14 @@ namespace SlatedGameToolkit.Framework.StateSystem
}
internal void render(double delta) {
if (CurrentWindow != currentState.CurrentWindow) {
CurrentWindow = currentState.CurrentWindow;
CurrentWindow.DrawToWindow();
}
CurrentWindow.clearColour(backgroundColour.r, backgroundColour.g, backgroundColour.b);
CurrentWindow.clear(0x4000);
currentState.Render(delta);
CurrentWindow.SwapBuffer();
}
/// <summary>
@ -66,7 +75,7 @@ namespace SlatedGameToolkit.Framework.StateSystem
bool unique = true;
foreach (IState state in states)
{
unique = unique && addState(state) ? true : false;
unique = unique && addState(state);
}
return unique;
}
@ -81,6 +90,7 @@ namespace SlatedGameToolkit.Framework.StateSystem
public bool addState(IState state) {
if (thread != Thread.CurrentThread) throw new ThreadStateException("Cannot add a state from a different thread.");
try {
GameEngine.Logger.Debug("Adding state: " + state.getName());
this.states.Add(state.getName(), state);
} catch (ArgumentException) {
return false;
@ -100,6 +110,7 @@ namespace SlatedGameToolkit.Framework.StateSystem
if (thread != Thread.CurrentThread) throw new ThreadStateException("Cannot remove a state from a different thread.");
if (states[name] == currentState) return false;
IState state = states[name];
GameEngine.Logger.Debug("Removing state: " + name);
state.Dispose();
return states.Remove(name);
}
@ -109,10 +120,18 @@ namespace SlatedGameToolkit.Framework.StateSystem
/// Disposes of the removed states.
/// </summary>
public void removeAllStates() {
GameEngine.Logger.Debug("Beginning to remove all states...");
foreach (String state in this.states.Keys)
{
removeState(state);
}
GameEngine.Logger.Debug("Completed removing all states...");
}
public void Dispose()
{
currentState = null;
removeAllStates();
}
}
}

View File

@ -1,9 +1,12 @@
using System;
using SlatedGameToolkit.Framework.Graphics.Window;
namespace SlatedGameToolkit.Framework.StateSystem.States
{
public interface IState : IDisposable
{
WindowHandle CurrentWindow { get; }
/// <summary>
/// Called when this state should be deactivated.
/// Deactivate may be called multiple times:

View File

@ -15,22 +15,21 @@ namespace SlatedGameToolkit.Tools.Commands
if (args.Length != 1) return false;
args[0] = args[0].ToLower();
if (args[0].Equals("start")) {
if (GameEngine.Running) {
if (GameEngine.IsRunning()) {
interactable.Tell("Engine is already running!");
return true;
}
Manager manager = new Manager("main state", new MainState());
GameEngine.Ignite(manager);
GameEngine.Ignite(new MainState());
return true;
} else if (args[0].Equals("stop")) {
if (!GameEngine.Running) {
if (!GameEngine.IsRunning()) {
interactable.Tell("Engine was never running!");
return true;
}
GameEngine.Stop();
return true;
} else if (args[0].Equals("status")) {
interactable.Tell("Running: " + GameEngine.Running);
interactable.Tell("Running: " + GameEngine.IsRunning());
interactable.Tell("Target FPS: " + GameEngine.targetFPS);
interactable.Tell("Target Update Rate: " + GameEngine.UpdatesPerSecond);
return true;

View File

@ -1,3 +1,6 @@
using System;
using SlatedGameToolkit.Framework;
using SlatedGameToolkit.Framework.Graphics.Window;
using SlatedGameToolkit.Framework.StateSystem;
using SlatedGameToolkit.Framework.StateSystem.States;
@ -5,6 +8,10 @@ namespace SlatedGameToolkit.Tools.Utilities.GraphicalPlayground
{
public class MainState : IState
{
private WindowHandle window;
public WindowHandle CurrentWindow { get { return window;}}
public bool Activate()
{
return true;
@ -17,6 +24,7 @@ namespace SlatedGameToolkit.Tools.Utilities.GraphicalPlayground
public void Dispose()
{
window.Dispose();
}
public string getName()
@ -26,7 +34,8 @@ namespace SlatedGameToolkit.Tools.Utilities.GraphicalPlayground
public void Initialize(Manager manager)
{
window = new WindowHandle("SlatedGameToolkit Playground");
window.RaiseToTop();
}
public void Render(double delta)