diff --git a/BloomFramework/BloomFramework.vcxproj b/BloomFramework/BloomFramework.vcxproj
index 50fcde3c..5bb58856 100644
--- a/BloomFramework/BloomFramework.vcxproj
+++ b/BloomFramework/BloomFramework.vcxproj
@@ -189,11 +189,14 @@
+
+
+
@@ -220,6 +223,8 @@
+
+
diff --git a/BloomFramework/BloomFramework.vcxproj.filters b/BloomFramework/BloomFramework.vcxproj.filters
index 2f9c77f8..6b301136 100644
--- a/BloomFramework/BloomFramework.vcxproj.filters
+++ b/BloomFramework/BloomFramework.vcxproj.filters
@@ -25,6 +25,12 @@
{901b4a12-4da2-4795-b576-f703b5eb63bc}
+
+ {bc7594da-4383-42e6-9da9-5122304c4bfa}
+
+
+ {1f775a83-4d98-4f93-bf11-b69ad3949381}
+
{91153568-16e8-4684-82a5-dce4be5c6640}
@@ -69,6 +75,9 @@
Source Files\Graphics
+
+ Source Files\Input
+
Source Files\Graphics
@@ -87,6 +96,9 @@
Source Files\Graphics
+
+ Source Files\Input
+
@@ -161,6 +173,12 @@
Header Files\Graphics
+
+ Header Files\Input
+
+
+ Header Files\Input
+
Header Files\Components
@@ -194,6 +212,9 @@
Header Files\Graphics
+
+ Header Files\Input
+
diff --git a/BloomFramework/include/Framework.h b/BloomFramework/include/Framework.h
index 865e9cd8..dd66e04f 100644
--- a/BloomFramework/include/Framework.h
+++ b/BloomFramework/include/Framework.h
@@ -12,3 +12,4 @@
#include "Graphics/Font.h"
#include "Graphics/FontStore.h"
#include "Graphics/SpriteText.h"
+#include "Input/InputManager.h"
\ No newline at end of file
diff --git a/BloomFramework/include/Game.h b/BloomFramework/include/Game.h
index 251d5185..1eb64543 100644
--- a/BloomFramework/include/Game.h
+++ b/BloomFramework/include/Game.h
@@ -3,7 +3,7 @@
#include "stdIncludes.h"
#include "Graphics/TextureStore.h"
#include "Timer.h"
-
+#include "Input/InputManager.h"
namespace bloom {
class BLOOMFRAMEWORK_API Game {
using TextureStore = bloom::graphics::TextureStore;
@@ -44,6 +44,7 @@ namespace bloom {
TextureStore textures = TextureStore(m_renderer);
Timer timer;
+ input::InputManager input;
protected:
SDL_Renderer * m_renderer = nullptr;
diff --git a/BloomFramework/include/Input/InputDefinitions.h b/BloomFramework/include/Input/InputDefinitions.h
new file mode 100644
index 00000000..7110ac4f
--- /dev/null
+++ b/BloomFramework/include/Input/InputDefinitions.h
@@ -0,0 +1,260 @@
+#pragma once
+
+#include "stdIncludes.h"
+
+namespace bloom::input {
+ enum class KeyboardKey {
+ Unknown = SDL_SCANCODE_UNKNOWN,
+ A = SDL_SCANCODE_A,
+ B = SDL_SCANCODE_B,
+ C = SDL_SCANCODE_C,
+ D = SDL_SCANCODE_D,
+ E = SDL_SCANCODE_E,
+ F = SDL_SCANCODE_F,
+ G = SDL_SCANCODE_G,
+ H = SDL_SCANCODE_H,
+ I = SDL_SCANCODE_I,
+ J = SDL_SCANCODE_J,
+ K = SDL_SCANCODE_K,
+ L = SDL_SCANCODE_L,
+ M = SDL_SCANCODE_M,
+ N = SDL_SCANCODE_N,
+ O = SDL_SCANCODE_O,
+ P = SDL_SCANCODE_P,
+ Q = SDL_SCANCODE_Q,
+ R = SDL_SCANCODE_R,
+ S = SDL_SCANCODE_S,
+ T = SDL_SCANCODE_T,
+ U = SDL_SCANCODE_U,
+ V = SDL_SCANCODE_V,
+ W = SDL_SCANCODE_W,
+ X = SDL_SCANCODE_X,
+ Y = SDL_SCANCODE_Y,
+ Z = SDL_SCANCODE_Z,
+ Num1 = SDL_SCANCODE_1,
+ Num2 = SDL_SCANCODE_2,
+ Num3 = SDL_SCANCODE_3,
+ Num4 = SDL_SCANCODE_4,
+ Num5 = SDL_SCANCODE_5,
+ Num6 = SDL_SCANCODE_6,
+ Num7 = SDL_SCANCODE_7,
+ Num8 = SDL_SCANCODE_8,
+ Num9 = SDL_SCANCODE_9,
+ Num0 = SDL_SCANCODE_0,
+ Return = SDL_SCANCODE_RETURN,
+ Escape = SDL_SCANCODE_ESCAPE,
+ Backspace = SDL_SCANCODE_BACKSPACE,
+ Tab = SDL_SCANCODE_TAB,
+ Space = SDL_SCANCODE_SPACE,
+ Minus = SDL_SCANCODE_MINUS,
+ Equals = SDL_SCANCODE_EQUALS,
+ LeftBracket = SDL_SCANCODE_LEFTBRACKET,
+ RightBracket = SDL_SCANCODE_RIGHTBRACKET,
+ Backslash = SDL_SCANCODE_BACKSLASH,
+ NonUsHash = SDL_SCANCODE_NONUSHASH,
+ Semicolon = SDL_SCANCODE_SEMICOLON,
+ Apostrophe = SDL_SCANCODE_APOSTROPHE,
+ Grave = SDL_SCANCODE_GRAVE,
+ Comma = SDL_SCANCODE_COMMA,
+ Period = SDL_SCANCODE_PERIOD,
+ Slash = SDL_SCANCODE_SLASH,
+ CapsLock = SDL_SCANCODE_CAPSLOCK,
+ F1 = SDL_SCANCODE_F1,
+ F2 = SDL_SCANCODE_F2,
+ F3 = SDL_SCANCODE_F3,
+ F4 = SDL_SCANCODE_F4,
+ F5 = SDL_SCANCODE_F5,
+ F6 = SDL_SCANCODE_F6,
+ F7 = SDL_SCANCODE_F7,
+ F8 = SDL_SCANCODE_F8,
+ F9 = SDL_SCANCODE_F9,
+ F10 = SDL_SCANCODE_F10,
+ F11 = SDL_SCANCODE_F11,
+ F12 = SDL_SCANCODE_F12,
+ PrintScreen = SDL_SCANCODE_PRINTSCREEN,
+ ScrollLock = SDL_SCANCODE_SCROLLLOCK,
+ Pause = SDL_SCANCODE_PAUSE,
+ Insert = SDL_SCANCODE_INSERT,
+ Home = SDL_SCANCODE_HOME,
+ PageUp = SDL_SCANCODE_PAGEUP,
+ Delete = SDL_SCANCODE_DELETE,
+ End = SDL_SCANCODE_END,
+ PageDown = SDL_SCANCODE_PAGEDOWN,
+ Right = SDL_SCANCODE_RIGHT,
+ Left = SDL_SCANCODE_LEFT,
+ Down = SDL_SCANCODE_DOWN,
+ Up = SDL_SCANCODE_UP,
+ NumLockClear = SDL_SCANCODE_NUMLOCKCLEAR,
+ KpDivide = SDL_SCANCODE_KP_DIVIDE,
+ KpMultiply = SDL_SCANCODE_KP_MULTIPLY,
+ KpMinus = SDL_SCANCODE_KP_MINUS,
+ KpPlus = SDL_SCANCODE_KP_PLUS,
+ KpEnter = SDL_SCANCODE_KP_ENTER,
+ Kp1 = SDL_SCANCODE_KP_1,
+ Kp2 = SDL_SCANCODE_KP_2,
+ Kp3 = SDL_SCANCODE_KP_3,
+ Kp4 = SDL_SCANCODE_KP_4,
+ Kp5 = SDL_SCANCODE_KP_5,
+ Kp6 = SDL_SCANCODE_KP_6,
+ Kp7 = SDL_SCANCODE_KP_7,
+ Kp8 = SDL_SCANCODE_KP_8,
+ Kp9 = SDL_SCANCODE_KP_9,
+ Kp0 = SDL_SCANCODE_KP_0,
+ KpPeriod = SDL_SCANCODE_KP_PERIOD,
+ NonUsBackslash = SDL_SCANCODE_NONUSBACKSLASH,
+ Application = SDL_SCANCODE_APPLICATION,
+ Power = SDL_SCANCODE_POWER,
+ KpEquals = SDL_SCANCODE_KP_EQUALS,
+ F13 = SDL_SCANCODE_F13,
+ F14 = SDL_SCANCODE_F14,
+ F15 = SDL_SCANCODE_F15,
+ F16 = SDL_SCANCODE_F16,
+ F17 = SDL_SCANCODE_F17,
+ F18 = SDL_SCANCODE_F18,
+ F19 = SDL_SCANCODE_F19,
+ F20 = SDL_SCANCODE_F20,
+ F21 = SDL_SCANCODE_F21,
+ F22 = SDL_SCANCODE_F22,
+ F23 = SDL_SCANCODE_F23,
+ F24 = SDL_SCANCODE_F24,
+ Execute = SDL_SCANCODE_EXECUTE,
+ Help = SDL_SCANCODE_HELP,
+ Menu = SDL_SCANCODE_MENU,
+ Select = SDL_SCANCODE_SELECT,
+ Stop = SDL_SCANCODE_STOP,
+ Again = SDL_SCANCODE_AGAIN,
+ Undo = SDL_SCANCODE_UNDO,
+ Cut = SDL_SCANCODE_CUT,
+ Copy = SDL_SCANCODE_COPY,
+ Paste = SDL_SCANCODE_PASTE,
+ Find = SDL_SCANCODE_FIND,
+ Mute = SDL_SCANCODE_MUTE,
+ VolumeUp = SDL_SCANCODE_VOLUMEUP,
+ VolumeDown = SDL_SCANCODE_VOLUMEDOWN,
+ KpComma = SDL_SCANCODE_KP_COMMA,
+ KpEqualsAS400 = SDL_SCANCODE_KP_EQUALSAS400,
+ International1 = SDL_SCANCODE_INTERNATIONAL1,
+ International2 = SDL_SCANCODE_INTERNATIONAL2,
+ International3 = SDL_SCANCODE_INTERNATIONAL3,
+ International4 = SDL_SCANCODE_INTERNATIONAL4,
+ International5 = SDL_SCANCODE_INTERNATIONAL5,
+ International6 = SDL_SCANCODE_INTERNATIONAL6,
+ International7 = SDL_SCANCODE_INTERNATIONAL7,
+ International8 = SDL_SCANCODE_INTERNATIONAL8,
+ International9 = SDL_SCANCODE_INTERNATIONAL9,
+ Lang1 = SDL_SCANCODE_LANG1,
+ Lang2 = SDL_SCANCODE_LANG2,
+ Lang3 = SDL_SCANCODE_LANG3,
+ Lang4 = SDL_SCANCODE_LANG4,
+ Lang5 = SDL_SCANCODE_LANG5,
+ Lang6 = SDL_SCANCODE_LANG6,
+ Lang7 = SDL_SCANCODE_LANG7,
+ Lang8 = SDL_SCANCODE_LANG8,
+ Lang9 = SDL_SCANCODE_LANG9,
+ AltErase = SDL_SCANCODE_ALTERASE,
+ SysReq = SDL_SCANCODE_SYSREQ,
+ Cancel = SDL_SCANCODE_CANCEL,
+ Clear = SDL_SCANCODE_CLEAR,
+ Prior = SDL_SCANCODE_PRIOR,
+ Return2 = SDL_SCANCODE_RETURN2,
+ Separator = SDL_SCANCODE_SEPARATOR,
+ Out = SDL_SCANCODE_OUT,
+ Oper = SDL_SCANCODE_OPER,
+ ClearAgain = SDL_SCANCODE_CLEARAGAIN,
+ CrSel = SDL_SCANCODE_CRSEL,
+ ExSel = SDL_SCANCODE_EXSEL,
+ Kp00 = SDL_SCANCODE_KP_00,
+ Kp000 = SDL_SCANCODE_KP_000,
+ ThousandsSeparator = SDL_SCANCODE_THOUSANDSSEPARATOR,
+ DecimalSeparator = SDL_SCANCODE_DECIMALSEPARATOR,
+ CurrencyUnit = SDL_SCANCODE_CURRENCYUNIT,
+ CurrencySubUnit = SDL_SCANCODE_CURRENCYSUBUNIT,
+ KpLeftParen = SDL_SCANCODE_KP_LEFTPAREN,
+ KpRightParen = SDL_SCANCODE_KP_RIGHTPAREN,
+ KpLeftBrace = SDL_SCANCODE_KP_LEFTBRACE,
+ KpRightBrace = SDL_SCANCODE_KP_RIGHTBRACE,
+ KpTab = SDL_SCANCODE_KP_TAB,
+ KpBackspace = SDL_SCANCODE_KP_BACKSPACE,
+ KpA = SDL_SCANCODE_KP_A,
+ KpB = SDL_SCANCODE_KP_B,
+ KpC = SDL_SCANCODE_KP_C,
+ KpD = SDL_SCANCODE_KP_D,
+ KpE = SDL_SCANCODE_KP_E,
+ KpF = SDL_SCANCODE_KP_F,
+ KpXor = SDL_SCANCODE_KP_XOR,
+ KpPower = SDL_SCANCODE_KP_POWER,
+ KpPercent = SDL_SCANCODE_KP_PERCENT,
+ KpLess = SDL_SCANCODE_KP_LESS,
+ KpGreater = SDL_SCANCODE_KP_GREATER,
+ KpAmpersand = SDL_SCANCODE_KP_AMPERSAND,
+ KpDblAmpersand = SDL_SCANCODE_KP_DBLAMPERSAND,
+ KpVerticalBar = SDL_SCANCODE_KP_VERTICALBAR,
+ KpDblVerticalBar = SDL_SCANCODE_KP_DBLVERTICALBAR,
+ KpColon = SDL_SCANCODE_KP_COLON,
+ KpHash = SDL_SCANCODE_KP_HASH,
+ KpSpace = SDL_SCANCODE_KP_SPACE,
+ KpAt = SDL_SCANCODE_KP_AT,
+ KpExclam = SDL_SCANCODE_KP_EXCLAM,
+ KpMemStore = SDL_SCANCODE_KP_MEMSTORE,
+ KpMemRecall = SDL_SCANCODE_KP_MEMRECALL,
+ KpMemClear = SDL_SCANCODE_KP_MEMCLEAR,
+ KpMemAdd = SDL_SCANCODE_KP_MEMADD,
+ KpMemSubtract = SDL_SCANCODE_KP_MEMSUBTRACT,
+ KpMemMultiply = SDL_SCANCODE_KP_MEMMULTIPLY,
+ KpMemDivide = SDL_SCANCODE_KP_MEMDIVIDE,
+ KpPlusMinus = SDL_SCANCODE_KP_PLUSMINUS,
+ KpClear = SDL_SCANCODE_KP_CLEAR,
+ KpClearEntry = SDL_SCANCODE_KP_CLEARENTRY,
+ KpBinary = SDL_SCANCODE_KP_BINARY,
+ KpOctal = SDL_SCANCODE_KP_OCTAL,
+ KpDecimal = SDL_SCANCODE_KP_DECIMAL,
+ KpHexadecimal = SDL_SCANCODE_KP_HEXADECIMAL,
+ LCtrl = SDL_SCANCODE_LCTRL,
+ LShift = SDL_SCANCODE_LSHIFT,
+ LAlt = SDL_SCANCODE_LALT,
+ LGui = SDL_SCANCODE_LGUI,
+ RCtrl = SDL_SCANCODE_RCTRL,
+ RShift = SDL_SCANCODE_RSHIFT,
+ RAlt = SDL_SCANCODE_RALT,
+ RGui = SDL_SCANCODE_RGUI,
+ Mode = SDL_SCANCODE_MODE,
+ AudioNext = SDL_SCANCODE_AUDIONEXT,
+ AudioPrev = SDL_SCANCODE_AUDIOPREV,
+ AudioStop = SDL_SCANCODE_AUDIOSTOP,
+ AudioPlay = SDL_SCANCODE_AUDIOPLAY,
+ AudioMute = SDL_SCANCODE_AUDIOMUTE,
+ MediaSelect = SDL_SCANCODE_MEDIASELECT,
+ Www = SDL_SCANCODE_WWW,
+ Mail = SDL_SCANCODE_MAIL,
+ Calculator = SDL_SCANCODE_CALCULATOR,
+ Computer = SDL_SCANCODE_COMPUTER,
+ AcSearch = SDL_SCANCODE_AC_SEARCH,
+ AcHome = SDL_SCANCODE_AC_HOME,
+ AcBack = SDL_SCANCODE_AC_BACK,
+ AcForward = SDL_SCANCODE_AC_FORWARD,
+ AcStop = SDL_SCANCODE_AC_STOP,
+ AcRefresh = SDL_SCANCODE_AC_REFRESH,
+ AcBookmarks = SDL_SCANCODE_AC_BOOKMARKS,
+ BrightnessDown = SDL_SCANCODE_BRIGHTNESSDOWN,
+ BrightnessUp = SDL_SCANCODE_BRIGHTNESSUP,
+ DisplaySwitch = SDL_SCANCODE_DISPLAYSWITCH,
+ KbdIllumToggle = SDL_SCANCODE_KBDILLUMTOGGLE,
+ KbdIllumDown = SDL_SCANCODE_KBDILLUMDOWN,
+ KbdIllumUp = SDL_SCANCODE_KBDILLUMUP,
+ Eject = SDL_SCANCODE_EJECT,
+ Sleep = SDL_SCANCODE_SLEEP,
+
+ KEYBOARD_SIZE = SDL_NUM_SCANCODES // This defines the keyboard size
+ };
+
+ enum class MouseButton {
+ Left = SDL_BUTTON_LEFT,
+ Middle = SDL_BUTTON_MIDDLE,
+ Right = SDL_BUTTON_RIGHT,
+ X1 = SDL_BUTTON_X1,
+ X2 = SDL_BUTTON_X2,
+
+ MOUSE_SIZE // No button, just to define max
+ // array size.
+ };
+}
\ No newline at end of file
diff --git a/BloomFramework/include/Input/InputEvents.h b/BloomFramework/include/Input/InputEvents.h
new file mode 100644
index 00000000..f0dd978a
--- /dev/null
+++ b/BloomFramework/include/Input/InputEvents.h
@@ -0,0 +1,110 @@
+#pragma once
+
+#include "stdIncludes.h"
+#include
+#include "InputDefinitions.h"
+#include "Components/Position.h"
+
+namespace bloom::input {
+ class BLOOMFRAMEWORK_API InputManager;
+
+ enum class EventType {
+ NoEvent,
+ QuitEvent,
+ KeyboardEvent,
+ MouseEvent,
+ UnknownEvent
+ };
+
+ class BLOOMFRAMEWORK_API KeyboardEvent {
+ friend class InputManager;
+
+ public:
+ KeyboardEvent() noexcept;
+
+ bool wasDown(KeyboardKey key) const noexcept;
+ bool wasUp(KeyboardKey key) const noexcept;
+
+ bool isPressed(KeyboardKey key) const noexcept;
+ bool stateChanged(KeyboardKey key) const noexcept;
+ void lock() noexcept;
+ void unlock() noexcept;
+ bool shift() const noexcept;
+ bool ctrl() const noexcept;
+ bool alt() const noexcept;
+ bool capsLock() const noexcept;
+ char toChar() const;
+
+ operator char() const {
+ return toChar();
+ }
+
+ class SymRecorder {
+ friend class KeyboardEvent;
+
+ public:
+ void start() noexcept;
+ std::string stop();
+ void clear();
+ void cancel();
+ std::string transfer();
+ const std::string& get() const;
+
+ private:
+ void append(char sym);
+
+ bool m_state{ false };
+ std::string m_str{};
+ } recorder{};
+
+ private:
+ void reset();
+ void set(const SDL_KeyboardEvent& kbe) noexcept;
+ static bool isPrintable(SDL_Keycode key) noexcept;
+ void updateModKeys();
+
+ std::bitset(KeyboardKey::KEYBOARD_SIZE)> m_keyboard{};
+ std::bitset(KeyboardKey::KEYBOARD_SIZE)> m_stateChanged{};
+ char m_char{'\0'};
+
+ bool m_lockState = false;
+ };
+
+ class BLOOMFRAMEWORK_API MouseEvent {
+ friend class InputManager;
+
+ public:
+ using Coordinates = components::Position;
+
+ MouseEvent() noexcept;
+
+ bool wasDown(MouseButton button) const noexcept;
+ bool wasUp(MouseButton button) const noexcept;
+
+ uint8_t isPressed(MouseButton button) const noexcept;
+ bool stateChanged(MouseButton button) const noexcept;
+ void lock() noexcept;
+ void unlock() noexcept;
+ int getX() const noexcept;
+ int getY() const noexcept;
+ Coordinates getPos() const noexcept;
+ Coordinates getOffset() const noexcept;
+ Coordinates getScroll() const noexcept;
+ bool isInside(const SDL_Rect& rectangle) const noexcept;
+
+ private:
+ void reset() noexcept;
+ void set(const SDL_MouseButtonEvent& mbe) noexcept;
+ void set(const SDL_MouseMotionEvent& mme) noexcept;
+ void set(const SDL_MouseWheelEvent& mwe) noexcept;
+
+ std::array(MouseButton::MOUSE_SIZE)> m_mouse{};
+ std::bitset(MouseButton::MOUSE_SIZE)> m_stateChanged{ 0 };
+
+ Coordinates m_pos{ 0, 0 },
+ m_offset{ 0, 0 },
+ m_scroll{ 0, 0 };
+
+ bool m_lockState = false;
+ };
+}
diff --git a/BloomFramework/include/Input/InputManager.h b/BloomFramework/include/Input/InputManager.h
new file mode 100644
index 00000000..a8320efb
--- /dev/null
+++ b/BloomFramework/include/Input/InputManager.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include
+#include "stdIncludes.h"
+/// All keys are defined here.
+#include "InputEvents.h"
+
+namespace bloom::input {
+ class BLOOMFRAMEWORK_API InputManager {
+ public:
+ bool update(bool continueOnQuit = false);
+ // If timeout < 0, the function will wait for the event indefinitely
+ EventType wait(int timeout = -1);
+ bool quitRequested() noexcept;
+
+ void lock() noexcept;
+ void unlock() noexcept;
+
+ KeyboardEvent keyboard{};
+ MouseEvent mouse{};
+
+ private:
+ void reset();
+ void handle();
+
+ bool m_quitState = false;
+ SDL_Event m_intlEvent{};
+ EventType m_lastType = EventType::NoEvent;
+ };
+}
\ No newline at end of file
diff --git a/BloomFramework/src/Game.cpp b/BloomFramework/src/Game.cpp
index 4d47bc3d..07a1e3a8 100644
--- a/BloomFramework/src/Game.cpp
+++ b/BloomFramework/src/Game.cpp
@@ -98,9 +98,8 @@ namespace bloom {
}
void Game::handleEvents() {
- SDL_PollEvent(&m_event);
-
- if (m_event.type == SDL_QUIT)
+ input.update();
+ if (input.quitRequested())
m_isRunning = false;
}
diff --git a/BloomFramework/src/Input/InputEvents.cpp b/BloomFramework/src/Input/InputEvents.cpp
new file mode 100644
index 00000000..c52b19fb
--- /dev/null
+++ b/BloomFramework/src/Input/InputEvents.cpp
@@ -0,0 +1,216 @@
+#include "Input/InputEvents.h"
+
+namespace bloom::input {
+ constexpr bool checkKey(bool lockState, KeyboardKey key) { return !lockState && key != KeyboardKey::KEYBOARD_SIZE; }
+
+ constexpr bool checkButton(bool lockState, MouseButton button) { return !lockState && button != MouseButton::MOUSE_SIZE; }
+
+ constexpr bool isLockKey(SDL_Scancode key) {
+ return key == SDL_SCANCODE_CAPSLOCK || key == SDL_SCANCODE_NUMLOCKCLEAR || key == SDL_SCANCODE_SCROLLLOCK;
+ }
+
+
+ KeyboardEvent::KeyboardEvent() noexcept {
+ int numKeys = 0;
+ const auto kb = SDL_GetKeyboardState(&numKeys);
+ const size_t keys = std::min(static_cast(numKeys), m_keyboard.size());
+ for (size_t i = 0; i < keys; ++i) {
+ m_keyboard.set(i, kb[i]);
+ }
+ updateModKeys();
+ }
+
+ bool KeyboardEvent::wasDown(KeyboardKey key) const noexcept {
+ return (isPressed(key) && m_stateChanged[static_cast(key)]);
+ }
+
+ bool KeyboardEvent::wasUp(KeyboardKey key) const noexcept {
+ return (!isPressed(key) && m_stateChanged[static_cast(key)]);
+ }
+
+ bool KeyboardEvent::isPressed(KeyboardKey key) const noexcept {
+ return (checkKey(m_lockState, key)
+ && m_keyboard[static_cast(key)]);
+ }
+
+ bool KeyboardEvent::stateChanged(KeyboardKey key) const noexcept {
+ return (checkKey(m_lockState, key)
+ && m_stateChanged[static_cast(key)]);
+ }
+
+ void KeyboardEvent::lock() noexcept {
+ m_lockState = true;
+ }
+
+ void KeyboardEvent::unlock() noexcept {
+ m_lockState = false;
+ }
+
+ bool KeyboardEvent::shift() const noexcept {
+ return (isPressed(KeyboardKey::LShift) || isPressed(KeyboardKey::RShift));
+ }
+
+ bool KeyboardEvent::ctrl() const noexcept {
+ return (isPressed(KeyboardKey::LCtrl) || isPressed(KeyboardKey::RCtrl));
+ }
+
+ bool KeyboardEvent::alt() const noexcept {
+ return (isPressed(KeyboardKey::LAlt) || isPressed(KeyboardKey::RAlt));
+ }
+
+ bool KeyboardEvent::capsLock() const noexcept {
+ return isPressed(KeyboardKey::CapsLock);
+ }
+
+ bool KeyboardEvent::isPrintable(SDL_Keycode key) noexcept {
+ return (key >= SDLK_SPACE && key <= SDLK_z || key == SDLK_RETURN || key == SDLK_BACKSPACE);
+ }
+
+ void KeyboardEvent::updateModKeys() {
+ const auto mod = SDL_GetModState();
+ m_keyboard.set(SDL_SCANCODE_CAPSLOCK, mod & KMOD_CAPS);
+ m_keyboard.set(SDL_SCANCODE_NUMLOCKCLEAR, mod & KMOD_NUM);
+ }
+
+ char KeyboardEvent::toChar() const {
+ return m_char;
+ }
+
+ void KeyboardEvent::reset() {
+ m_char = '\0';
+ updateModKeys();
+ m_stateChanged.reset();
+ }
+
+ void KeyboardEvent::set(const SDL_KeyboardEvent& kbe) noexcept {
+ if (isLockKey(kbe.keysym.scancode)) {
+ if (kbe.state) {
+ m_keyboard.flip(kbe.keysym.scancode);
+ m_stateChanged.set(kbe.keysym.scancode);
+ }
+ }
+ else {
+ if (static_cast(kbe.state) != m_keyboard[kbe.keysym.scancode])
+ m_stateChanged.set(kbe.keysym.scancode);
+ m_keyboard.set(kbe.keysym.scancode, kbe.state);
+ }
+ if (kbe.state && isPrintable(kbe.keysym.sym)) {
+ m_char = static_cast(kbe.keysym.sym);
+ if ((shift() || capsLock()) && kbe.keysym.sym >= SDLK_a && kbe.keysym.sym <= SDLK_z)
+ m_char -= ('a' - 'A');
+ recorder.append(m_char);
+ }
+ }
+
+ void KeyboardEvent::SymRecorder::start() noexcept { m_state = true; }
+
+ void KeyboardEvent::SymRecorder::cancel() { m_state = false; m_str.clear(); }
+
+ std::string KeyboardEvent::SymRecorder::transfer() { return std::move(m_str); }
+
+ const std::string& KeyboardEvent::SymRecorder::get() const { return m_str; }
+
+ std::string KeyboardEvent::SymRecorder::stop() { m_state = false; return std::move(m_str); }
+
+ void KeyboardEvent::SymRecorder::clear() { m_str.clear(); }
+
+ void KeyboardEvent::SymRecorder::append(char sym) {
+ if (m_state) {
+ switch (sym) {
+ case '\0':
+ break;
+ case '\r': case '\n':
+ m_str += "\r\n";
+ case '\b':
+ if (!m_str.empty())
+ m_str.pop_back();
+ break;
+ default:
+ m_str.push_back(sym);
+ }
+ }
+ }
+
+
+ MouseEvent::MouseEvent() noexcept {
+ const auto mouseState = SDL_GetMouseState(nullptr, nullptr);
+ for (size_t i = 1; i <= 3; ++i) {
+ m_mouse[i] = mouseState & SDL_BUTTON(i);
+ }
+ }
+
+ bool MouseEvent::wasDown(MouseButton button) const noexcept {
+ return (isPressed(button) && m_stateChanged[static_cast(button)]);
+ }
+
+ bool MouseEvent::wasUp(MouseButton button) const noexcept {
+ return (!isPressed(button) && m_stateChanged[static_cast(button)]);
+ }
+
+ uint8_t MouseEvent::isPressed(MouseButton button) const noexcept {
+ if (checkButton(m_lockState, button))
+ return m_mouse[static_cast(button)];
+ return 0;
+ }
+
+ bool MouseEvent::stateChanged(MouseButton button) const noexcept {
+ return (checkButton(m_lockState, button)
+ && m_stateChanged[static_cast(button)]);
+ }
+
+ int MouseEvent::getX() const noexcept {
+ return m_pos.x;
+ }
+
+ int MouseEvent::getY() const noexcept {
+ return m_pos.y;
+ }
+
+ MouseEvent::Coordinates MouseEvent::getPos() const noexcept {
+ return m_pos;
+ }
+
+ MouseEvent::Coordinates MouseEvent::getOffset() const noexcept {
+ return m_offset;
+ }
+
+ MouseEvent::Coordinates MouseEvent::getScroll() const noexcept {
+ return m_scroll;
+ }
+
+ bool MouseEvent::isInside(const SDL_Rect& rectangle) const noexcept {
+ return ((m_pos.x >= rectangle.x)
+ && (m_pos.x <= rectangle.x + rectangle.w)
+ && (m_pos.y >= rectangle.y)
+ && (m_pos.y <= rectangle.y + rectangle.h));
+ }
+
+ void MouseEvent::lock() noexcept {
+ m_lockState = true;
+ }
+
+ void MouseEvent::unlock() noexcept {
+ m_lockState = false;
+ }
+
+ void MouseEvent::reset() noexcept {
+ m_stateChanged.reset();
+ m_scroll.x = m_scroll.y = m_offset.x = m_offset.y = 0;
+ }
+
+ void MouseEvent::set(const SDL_MouseButtonEvent& mbe) noexcept {
+ if (static_cast(m_mouse[mbe.button]) != static_cast(mbe.state))
+ m_stateChanged.set(mbe.button);
+ m_mouse[mbe.button] = mbe.state ? mbe.clicks : 0;
+ m_pos.x = mbe.x; m_pos.y = mbe.y;
+ }
+
+ void MouseEvent::set(const SDL_MouseMotionEvent& mme) noexcept {
+ m_pos.x = mme.x; m_pos.y = mme.y;
+ m_offset.x = mme.xrel; m_offset.y = mme.yrel;
+ }
+
+ void MouseEvent::set(const SDL_MouseWheelEvent& mwe) noexcept {
+ m_scroll.x = mwe.x; m_scroll.y = mwe.y;
+ }
+}
\ No newline at end of file
diff --git a/BloomFramework/src/Input/InputManager.cpp b/BloomFramework/src/Input/InputManager.cpp
new file mode 100644
index 00000000..a9ce167f
--- /dev/null
+++ b/BloomFramework/src/Input/InputManager.cpp
@@ -0,0 +1,117 @@
+#include "Input/InputManager.h"
+
+namespace bloom::input {
+ bool InputManager::update(bool continueOnQuit) {
+ reset();
+ //keyboard.m_keyState.fill(0);
+ //mouse.m_mouseState.fill(0);
+
+ //mouse.m_mouseMoveX = 0;
+ //mouse.m_mouseMoveY = 0;
+ //keyboard.m_char.reset();
+ //keyboard.m_keyboard = SDL_GetKeyboardState(nullptr);
+ //mouse.m_mouse = SDL_GetMouseState(&mouse.m_mouseX, &mouse.m_mouseY);
+
+ bool caught = false;
+
+ // Get key events from the OS
+ while (SDL_PollEvent(&m_intlEvent)) {
+ handle();
+ caught = true;
+ if (m_quitState && !continueOnQuit)
+ break;
+ }
+ if (!caught)
+ m_lastType = EventType::NoEvent;
+ return caught;
+ }
+
+ EventType InputManager::wait(int timeout) {
+ int caught;
+ if (timeout < 0)
+ caught = SDL_WaitEvent(&m_intlEvent);
+ else
+ caught = SDL_WaitEventTimeout(&m_intlEvent, timeout);
+ if (caught)
+ handle();
+ else
+ m_lastType = EventType::NoEvent;
+ return m_lastType;
+ }
+
+ void InputManager::handle() {
+ switch (m_intlEvent.type) {
+ case SDL_QUIT: {
+ m_lastType = EventType::QuitEvent;
+ m_quitState = true;
+ break;
+ }
+ case SDL_KEYDOWN: case SDL_KEYUP: {
+ m_lastType = EventType::KeyboardEvent;
+ keyboard.set(m_intlEvent.key);
+ //SDL_Keysym pressedKey = m_intlEvent.key.keysym;
+
+ //keyboard.m_keyState[pressedKey.scancode] = 1;
+
+ //if (keyboard.isPrintable(pressedKey.sym)) {
+ // if (pressedKey.sym == SDLK_BACKSPACE)
+ // keyboard.m_char = "\b \b";
+ // else
+ // keyboard.m_char = pressedKey.sym;
+ //}
+
+ break;
+ }
+ case SDL_MOUSEMOTION: {
+ m_lastType = EventType::MouseEvent;
+ mouse.set(m_intlEvent.motion);
+ //mouse.m_mouseX = m_intlEvent.motion.x;
+ //mouse.m_mouseY = m_intlEvent.motion.y;
+ //mouse.m_mouseMoveX = m_intlEvent.motion.xrel;
+ //mouse.m_mouseMoveY = m_intlEvent.motion.yrel;
+ break;
+ }
+ case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: {
+ m_lastType = EventType::MouseEvent;
+ mouse.set(m_intlEvent.button);
+ //mouse.m_mouseState[m_intlEvent.button.button] = 1;
+ break;
+ }
+ case SDL_MOUSEWHEEL: {
+ m_lastType = EventType::MouseEvent;
+ mouse.set(m_intlEvent.wheel);
+ //mouse.m_scrollX = m_intlEvent.wheel.x;
+ //mouse.m_scrollY = m_intlEvent.wheel.y;
+ break;
+ }
+ default:
+ m_lastType = EventType::UnknownEvent;
+ break;
+ }
+ }
+
+ void InputManager::reset() {
+ keyboard.reset();
+ mouse.reset();
+ }
+
+ void InputManager::lock() noexcept {
+ keyboard.lock();
+ mouse.lock();
+ }
+
+ void InputManager::unlock() noexcept {
+ keyboard.unlock();
+ mouse.unlock();
+ }
+
+ bool InputManager::quitRequested() noexcept {
+ auto tmp = m_quitState;
+ m_quitState = false;
+ return tmp;
+ }
+
+ //EventType InputManager::getType() const noexcept {
+ // return m_lastType;
+ //}
+}
\ No newline at end of file
diff --git a/Test Bench/GameObjectTest/RandomizerSystem.h b/Test Bench/GameObjectTest/RandomizerSystem.h
index 238d97a0..f5a1478c 100644
--- a/Test Bench/GameObjectTest/RandomizerSystem.h
+++ b/Test Bench/GameObjectTest/RandomizerSystem.h
@@ -10,7 +10,7 @@ class RandomPositionSystem : public bloom::systems::System {
public:
void update(std::optional deltaTime = std::nullopt) override {
m_registry.view().each(
- [this](auto entity, Position & pos) {
+ [=](auto entity, Position & pos) {
if (!m_registry.has(entity)) {
pos.x = rand() % 672;
pos.y = rand() % 472;
diff --git a/Test Bench/GameObjectTest/TestControllableObject.h b/Test Bench/GameObjectTest/TestControllableObject.h
new file mode 100644
index 00000000..1cb059f3
--- /dev/null
+++ b/Test Bench/GameObjectTest/TestControllableObject.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "Framework.h"
+#include "NoRandomComponent.h"
+
+class TestMovableObject : public bloom::GameObject {
+ using Position = bloom::components::Position;
+ using Size = bloom::components::Size;
+ using Sprite = bloom::components::Sprite;
+ using bloom::GameObject::GameObject;
+
+public:
+ void init() override {}
+
+ void init(SDL_Rect pos_and_size = SDL_Rect{ 0,0, 50, 50 }, const std::filesystem::path texturePath = "Assets/Box.png", std::optional srcRect = std::nullopt) {
+ pos = &m_registry.replace(m_entity, pos_and_size.x, pos_and_size.y);
+ m_registry.assign(m_entity, pos_and_size.w, pos_and_size.h);
+ auto tmp = m_gameInstance->textures.load(texturePath);
+ m_registry.assign(m_entity, tmp, srcRect);
+ m_registry.assign(m_entity);
+ }
+
+ void updatePos(int xOffset, int yOffset) {
+ pos->x += xOffset;
+ pos->y += yOffset;
+ }
+
+private:
+ Position* pos;
+};
\ No newline at end of file
diff --git a/Test Bench/Test Bench.vcxproj b/Test Bench/Test Bench.vcxproj
index facda3ba..72edc050 100644
--- a/Test Bench/Test Bench.vcxproj
+++ b/Test Bench/Test Bench.vcxproj
@@ -205,6 +205,7 @@
+
diff --git a/Test Bench/Test Bench.vcxproj.filters b/Test Bench/Test Bench.vcxproj.filters
index 95773fdd..d1d5e3bf 100644
--- a/Test Bench/Test Bench.vcxproj.filters
+++ b/Test Bench/Test Bench.vcxproj.filters
@@ -52,5 +52,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/Test Bench/data/Assets/Box.png b/Test Bench/data/Assets/Box.png
new file mode 100644
index 00000000..6846c314
Binary files /dev/null and b/Test Bench/data/Assets/Box.png differ
diff --git a/Test Bench/data/Assets/testCursor.png b/Test Bench/data/Assets/testCursor.png
new file mode 100644
index 00000000..b483f525
Binary files /dev/null and b/Test Bench/data/Assets/testCursor.png differ
diff --git a/Test Bench/main.cpp b/Test Bench/main.cpp
index 724b913e..9e434117 100644
--- a/Test Bench/main.cpp
+++ b/Test Bench/main.cpp
@@ -9,6 +9,7 @@
#include "GameObjectTest/RandomizerSystem.h"
#include "GameObjectTest/TestAnimatedGameObject.h"
#include "GameObjectTest/AnimationChangerSystem.h"
+#include "GameObjectTest/TestControllableObject.h"
#include "getExePath.h"
using namespace bloom;
@@ -34,7 +35,7 @@ void test_player(const std::filesystem::path& dataDir) {
fs::path soundsPath = dataDir / L"Sounds";
//MusicTrack track1{ musicPath / L"music_007.mp3" };
-
+ music.queue.setVolume(0.0);
music.push(musicPath / L"music_001.mp3");
music.push(musicPath / L"music_002.mp3");
music.push(musicPath / L"music_003.mp3");
@@ -81,11 +82,14 @@ void test_drawer(const std::filesystem::path& dataDir) {
if (!std::filesystem::exists(assetsPath))
throw bloom::Exception{ "Test Bench", "Required assets can't be found" };
- fs::path spriteSheetPath = assetsPath / "OverworldTestSpritesheet.png";
- fs::path testCharPath = assetsPath / "TestChar.png";
+ std::filesystem::path spriteSheetPath = assetsPath / L"OverworldTestSpritesheet.png";
+ std::filesystem::path testCharPath = assetsPath / L"TestChar.png";
+ std::filesystem::path testCursorPath = assetsPath / "testCursor.png";
+ std::filesystem::path testBoxPath = assetsPath / "Box.png";
fs::path fontPath = fontsPath / "Fira Code.ttf";
game->textures.load(spriteSheetPath, SDL_Color{ 64, 176, 104, 113 });
game->textures.load(testCharPath, SDL_Color{ 144,168,0,0 });
+ game->textures.load(testCursorPath);
FontStore fonts;
constexpr size_t UI_font = 0;
@@ -119,8 +123,7 @@ void test_drawer(const std::filesystem::path& dataDir) {
AnimationChangerSystem animChangerTest(testRegistry);
bloom::systems::AnimationSystem animSysTest(testRegistry);
bloom::systems::RenderSystem renderSysTest(testRegistry);
- //game->textures.load(spriteSheetPath, SDL_Color{ 64, 176, 104, 113 });
- //game->textures.load(testCharPath, SDL_Color{ 144,168,0,0 });
+ TestChar cursor = TestChar(testRegistry, game);
TestChar testSprite = TestChar(testRegistry, game);
testSprite.init(SDL_Rect{ 0, 0, 128, 128 }, spriteSheetPath, SDL_Rect{ 0,0,32,32 });
renderSysTest.update();
@@ -137,21 +140,74 @@ void test_drawer(const std::filesystem::path& dataDir) {
// Randomizes position of entities(excluding those with `NoRandomPos` Component.
RandomPositionSystem randomizer(testRegistry);
+
+ cursor.init(SDL_Rect{ 0,0,39,55 }, testCursorPath);
+ cursor.disableRandomPos();
TestAnimChar testAnim(testRegistry, game);
testAnim.init(testCharPath);
+ TestMovableObject testMovable(testRegistry, game);
+ testMovable.init(SDL_Rect{ 0,0, 50, 50 }, testBoxPath);
// Test SpriteText2
std::string deltaTimeText{ "fps: " };
-
+
// If manual control of entities is required, this is the method to do so.
- auto & testGOpos = testRegistry.get(testGO.getEntityID());
+ auto& testGOpos = testRegistry.get(testGO.getEntityID());
- auto & testGOsize = testRegistry.get(testGO.getEntityID());
+ auto& testGOsize = testRegistry.get(testGO.getEntityID());
int testX = rstep(10), testY = rstep(10);
+ auto& cursorPos = testRegistry.get(cursor.getEntityID());
+
while (game->isRunning()) {
testGOpos.x += testX;
testGOpos.y += testY;
+
+ cursorPos.x = game->input.mouse.getX();
+ cursorPos.y = game->input.mouse.getY();
+ switch (char sym = game->input.keyboard) {
+ case '\0':
+ break;
+ case '\b':
+ std::cout << "\b \b";
+ break;
+ case '\n': case '\r':
+ std::cout << "\r\n";
+ break;
+ default:
+ std::cout << sym;
+ }
+
+ // vvv wasUp and wasDown testing vvv
+ if (game->input.keyboard.wasDown(input::KeyboardKey::Num0))
+ std::cout << std::endl << "zero was pressed" << std::endl;
+ if (game->input.keyboard.wasUp(input::KeyboardKey::Num0))
+ std::cout << std::endl << "zero was released" << std::endl;
+ if (game->input.keyboard.wasDown(input::KeyboardKey::CapsLock))
+ std::cout << std::endl << "caps lock was pressed" << std::endl;
+ if (game->input.keyboard.wasUp(input::KeyboardKey::CapsLock))
+ std::cout << std::endl << "caps lock was released" << std::endl;
+ if (game->input.mouse.wasDown(input::MouseButton::Left))
+ std::cout << std::endl << "left mouse button was pressed" << std::endl;
+ if (game->input.mouse.wasUp(input::MouseButton::Left))
+ std::cout << std::endl << "left mouse button was released" << std::endl;
+ // ^^^ wasUp and wasDown testing ^^^
+
+ // vvv isPressed testing vvv
+ int xOffset = 0, yOffset = 0;
+ if (game->input.keyboard.isPressed(input::KeyboardKey::W) || game->input.keyboard.isPressed(input::KeyboardKey::Up))
+ yOffset -= 5;
+ if (game->input.keyboard.isPressed(input::KeyboardKey::S) || game->input.keyboard.isPressed(input::KeyboardKey::Down))
+ yOffset += 5;
+ if (game->input.keyboard.isPressed(input::KeyboardKey::A) || game->input.keyboard.isPressed(input::KeyboardKey::Left))
+ xOffset -= 5;
+ if (game->input.keyboard.isPressed(input::KeyboardKey::D) || game->input.keyboard.isPressed(input::KeyboardKey::Right))
+ xOffset += 5;
+ // ^^^ isPressed testing ^^^
+
+ testMovable.updatePos(xOffset, yOffset);
+
+
if (testGOpos.x >= WINDOW_WIDTH) {
testGOpos.x = -testGOsize.w; testX = rstep(10); testY = rstep(10);
}
@@ -180,6 +236,7 @@ void test_drawer(const std::filesystem::path& dataDir) {
game->delay(framedelay - frametime);
}
}
+ std::cout << std::endl;
game->destroy();
}