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