diff options
author | max-001 <maxsmits21@kpnmail.nl> | 2024-12-17 14:36:33 +0100 |
---|---|---|
committer | max-001 <maxsmits21@kpnmail.nl> | 2024-12-17 14:36:33 +0100 |
commit | 473f807b1ec0fc67f40c65b310e132e4b83a76a3 (patch) | |
tree | b5509f1fea0238707ae887ee26ccea2fc8f59a69 /src/crepe | |
parent | f2a95686ec6d33d15f38fef4420700cefc875e89 (diff) | |
parent | c96ef5f62a1369d66e8eba9bf8ed192e3cf8e716 (diff) |
Merge remote-tracking branch 'origin/master' into max/game
Diffstat (limited to 'src/crepe')
-rw-r--r-- | src/crepe/Particle.cpp | 13 | ||||
-rw-r--r-- | src/crepe/Particle.h | 14 | ||||
-rw-r--r-- | src/crepe/api/Button.cpp | 3 | ||||
-rw-r--r-- | src/crepe/api/Button.h | 12 | ||||
-rw-r--r-- | src/crepe/api/Config.h | 5 | ||||
-rw-r--r-- | src/crepe/api/Event.h | 98 | ||||
-rw-r--r-- | src/crepe/api/KeyCodes.h | 6 | ||||
-rw-r--r-- | src/crepe/api/LoopManager.cpp | 1 | ||||
-rw-r--r-- | src/crepe/api/ParticleEmitter.cpp | 7 | ||||
-rw-r--r-- | src/crepe/api/ParticleEmitter.h | 53 | ||||
-rw-r--r-- | src/crepe/api/Script.cpp | 15 | ||||
-rw-r--r-- | src/crepe/api/Script.h | 18 | ||||
-rw-r--r-- | src/crepe/facade/EventData.h | 54 | ||||
-rw-r--r-- | src/crepe/facade/SDLContext.cpp | 261 | ||||
-rw-r--r-- | src/crepe/facade/SDLContext.h | 166 | ||||
-rw-r--r-- | src/crepe/system/InputSystem.cpp | 259 | ||||
-rw-r--r-- | src/crepe/system/InputSystem.h | 40 | ||||
-rw-r--r-- | src/crepe/system/ParticleSystem.cpp | 89 | ||||
-rw-r--r-- | src/crepe/system/ParticleSystem.h | 22 | ||||
-rw-r--r-- | src/crepe/system/RenderSystem.cpp | 4 |
20 files changed, 684 insertions, 456 deletions
diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp index 485a0d4..b340826 100644 --- a/src/crepe/Particle.cpp +++ b/src/crepe/Particle.cpp @@ -2,8 +2,8 @@ using namespace crepe; -void Particle::reset(uint32_t lifespan, const vec2 & position, const vec2 & velocity, - double angle) { +void Particle::reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity, + float angle) { // Initialize the particle state this->time_in_life = 0; this->lifespan = lifespan; @@ -15,16 +15,17 @@ void Particle::reset(uint32_t lifespan, const vec2 & position, const vec2 & velo this->force_over_time = {0, 0}; } -void Particle::update() { +void Particle::update(double dt) { // Deactivate particle if it has exceeded its lifespan - if (++time_in_life >= lifespan) { + time_in_life += dt; + if (time_in_life >= lifespan) { this->active = false; return; } // Update velocity based on accumulated force and update position - this->velocity += force_over_time; - this->position += velocity; + this->velocity += force_over_time * dt; + this->position += velocity * dt; } void Particle::stop_movement() { diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h index d0397c9..ee0cd66 100644 --- a/src/crepe/Particle.h +++ b/src/crepe/Particle.h @@ -14,8 +14,6 @@ namespace crepe { * can also be reset or stopped. */ class Particle { - // TODO: add friend particleSsytem and rendersystem. Unit test will fail. - public: //! Position of the particle in 2D space. vec2 position; @@ -24,13 +22,13 @@ public: //! Accumulated force affecting the particle over time. vec2 force_over_time; //! Total lifespan of the particle in milliseconds. - uint32_t lifespan; + float lifespan; //! Active state of the particle; true if it is in use, false otherwise. bool active = false; //! The time the particle has been alive, in milliseconds. - uint32_t time_in_life = 0; + float time_in_life = 0; //! The angle at which the particle is oriented or moving. - double angle = 0; + float angle = 0; /** * \brief Resets the particle with new properties. @@ -43,14 +41,16 @@ public: * \param velocity The initial velocity of the particle. * \param angle The angle of the particle's trajectory or orientation. */ - void reset(uint32_t lifespan, const vec2 & position, const vec2 & velocity, double angle); + void reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity, + float angle); /** * \brief Updates the particle's state. * * Advances the particle's position based on its velocity and applies accumulated forces. * Deactivates the particle if its lifespan has expired. + * \param dt The amount of fixed delta time that has passed. */ - void update(); + void update(double dt); /** * \brief Stops the particle's movement. * diff --git a/src/crepe/api/Button.cpp b/src/crepe/api/Button.cpp index 76f74f0..305922c 100644 --- a/src/crepe/api/Button.cpp +++ b/src/crepe/api/Button.cpp @@ -3,9 +3,8 @@ namespace crepe { Button::Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset, - const std::function<void()> & on_click, bool is_toggle) + const std::function<void()> & on_click) : UIObject(id, dimensions, offset), - is_toggle(is_toggle), on_click(on_click) {} } // namespace crepe diff --git a/src/crepe/api/Button.h b/src/crepe/api/Button.h index 61b18d7..08f5dec 100644 --- a/src/crepe/api/Button.h +++ b/src/crepe/api/Button.h @@ -15,19 +15,11 @@ public: * \param id The unique ID of the game object associated with this button. * \param dimensions The width and height of the UIObject * \param offset The offset relative this GameObjects Transform - * \param is_toggle Optional flag to indicate if the button is a toggle button. Defaults to false. * \param on_click callback function that will be invoked when the button is clicked. */ Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset, - const std::function<void()> & on_click, bool is_toggle = false); + const std::function<void()> & on_click); - /** - * \brief Indicates if the button is a toggle button (can be pressed and released). - * - * A toggle button allows for a pressed/released state, whereas a regular button - * typically only has an on-click state. - */ - bool is_toggle = false; // TODO: create separate toggle button class /** * \brief The callback function to be executed when the button is clicked. @@ -56,8 +48,6 @@ public: private: //! friend relation for is_pressed and hover variables friend class InputSystem; - //! Indicates whether the toggle button is pressed - bool is_pressed = false; //! Indicates whether the mouse is currently hovering over the button bool hover = false; diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index ca2d3f1..ed1cf38 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -76,6 +76,11 @@ struct Config final { */ std::string root_pattern = ".crepe-root"; } asset; + //! Configuration for click tolerance. + struct { + //! The maximum number of pixels the mouse can move between MouseDown and MouseUp events to be considered a click. + int click_tolerance = 5; + } input; //! Audio system settings struct { diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h index f2f3daf..73bf461 100644 --- a/src/crepe/api/Event.h +++ b/src/crepe/api/Event.h @@ -3,7 +3,8 @@ #include <string> -#include "KeyCodes.h" +#include "api/KeyCodes.h" +#include "types.h" namespace crepe { @@ -38,11 +39,8 @@ public: */ class MousePressEvent : public Event { public: - //! X-coordinate of the mouse position at the time of the event. - int mouse_x = 0; - - //! Y-coordinate of the mouse position at the time of the event. - int mouse_y = 0; + //! mouse position in world coordinates (game units). + vec2 mouse_pos = {0, 0}; //! The mouse button that was pressed. MouseButton button = MouseButton::NONE; @@ -53,11 +51,8 @@ public: */ class MouseClickEvent : public Event { public: - //! X-coordinate of the mouse position at the time of the event. - int mouse_x = 0; - - //! Y-coordinate of the mouse position at the time of the event. - int mouse_y = 0; + //! mouse position in world coordinates (game units). + vec2 mouse_pos = {0, 0}; //! The mouse button that was clicked. MouseButton button = MouseButton::NONE; @@ -68,11 +63,8 @@ public: */ class MouseReleaseEvent : public Event { public: - //! X-coordinate of the mouse position at the time of the event. - int mouse_x = 0; - - //! Y-coordinate of the mouse position at the time of the event. - int mouse_y = 0; + //! mouse position in world coordinates (game units). + vec2 mouse_pos = {0, 0}; //! The mouse button that was released. MouseButton button = MouseButton::NONE; @@ -83,17 +75,10 @@ public: */ class MouseMoveEvent : public Event { public: - //! X-coordinate of the mouse position at the time of the event. - int mouse_x = 0; - - //! Y-coordinate of the mouse position at the time of the event. - int mouse_y = 0; - - // Movement since last event in x - int delta_x = 0; - - // Movement since last event in y - int delta_y = 0; + //! mouse position in world coordinates (game units). + vec2 mouse_pos = {0, 0}; + //! The change in mouse position relative to the last position (in pixels). + ivec2 mouse_delta = {0, 0}; }; /** @@ -101,12 +86,8 @@ public: */ class MouseScrollEvent : public Event { public: - //! X-coordinate of the mouse position at the time of the event. - int mouse_x = 0; - - //! Y-coordinate of the mouse position at the time of the event. - int mouse_y = 0; - + //! mouse position in world coordinates (game units) when the scroll happened. + vec2 mouse_pos = {0, 0}; //! scroll direction (-1 = down, 1 = up) int scroll_direction = 0; //! scroll amount in y axis (from and away from the person). @@ -127,4 +108,55 @@ public: */ class ShutDownEvent : public Event {}; +/** + * \brief Event triggered to indicate the window is overlapped by another window. + * + * When two windows overlap the bottom window gets distorted and that window has to be redrawn. + */ +class WindowExposeEvent : public Event {}; + +/** + * \brief Event triggered to indicate the window is resized. + */ +class WindowResizeEvent : public Event { +public: + //! new window dimensions + ivec2 dimensions = {0, 0}; +}; + +/** + * \brief Event triggered to indicate the window is moved. + */ +class WindowMoveEvent : public Event { +public: + //! The change in position relative to the last position (in pixels). + ivec2 delta_move = {0, 0}; +}; + +/** + * \brief Event triggered to indicate the window is minimized. + */ +class WindowMinimizeEvent : public Event {}; + +/** + * \brief Event triggered to indicate the window is maximized + */ +class WindowMaximizeEvent : public Event {}; + +/** + * \brief Event triggered to indicate the window gained focus + * + * This event is triggered when the window receives focus, meaning it becomes the active window + * for user interaction. + */ +class WindowFocusGainEvent : public Event {}; + +/** + * \brief Event triggered to indicate the window lost focus + * + * This event is triggered when the window loses focus, meaning it is no longer the active window + * for user interaction. + */ +class WindowFocusLostEvent : public Event {}; + } // namespace crepe diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h index fcfc080..1b9573a 100644 --- a/src/crepe/api/KeyCodes.h +++ b/src/crepe/api/KeyCodes.h @@ -1,5 +1,9 @@ #pragma once + +#include <unordered_map> + namespace crepe { + //! Enumeration for mouse button inputs, including standard and extended buttons. enum class MouseButton { NONE = 0, //!< No mouse button input. @@ -151,4 +155,6 @@ enum class Keycode { /// \} MENU = 348, //!< Menu key. }; +//! Typedef for keyboard state. +typedef std::unordered_map<Keycode, bool> keyboard_state_t; } // namespace crepe diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index b5e5ff7..7a78019 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -65,6 +65,7 @@ void LoopManager::fixed_update() { this->get_system<InputSystem>().update(); this->event_manager.dispatch_events(); this->get_system<ScriptSystem>().update(); + this->get_system<ParticleSystem>().update(); this->get_system<AISystem>().update(); this->get_system<PhysicsSystem>().update(); this->get_system<CollisionSystem>().update(); diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp index 90b77a0..4f54bbd 100644 --- a/src/crepe/api/ParticleEmitter.cpp +++ b/src/crepe/api/ParticleEmitter.cpp @@ -1,11 +1,14 @@ #include "ParticleEmitter.h" +#include "api/Sprite.h" using namespace crepe; -ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Data & data) +ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite, + const Data & data) : Component(game_object_id), + sprite(sprite), data(data) { for (size_t i = 0; i < this->data.max_particles; i++) { - this->data.particles.emplace_back(); + this->particles.emplace_back(); } } diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h index b83fd61..8ac2e72 100644 --- a/src/crepe/api/ParticleEmitter.h +++ b/src/crepe/api/ParticleEmitter.h @@ -1,7 +1,11 @@ #pragma once +#include <cmath> #include <vector> +#include "system/ParticleSystem.h" +#include "system/RenderSystem.h" + #include "Component.h" #include "Particle.h" #include "types.h" @@ -26,15 +30,18 @@ public: */ struct Boundary { //! boundary width (midpoint is emitter location) - double width = 0.0; + float width = INFINITY; //! boundary height (midpoint is emitter location) - double height = 0.0; + float height = INFINITY; //! boundary offset from particle emitter location vec2 offset; //! reset on exit or stop velocity and set max postion bool reset_on_exit = false; }; + //! sprite reference of displayed sprite + const Sprite & sprite; + /** * \brief Holds parameters that control particle emission. * @@ -42,32 +49,28 @@ public: * and the sprite used for rendering particles. */ struct Data { - //! position of the emitter - vec2 position; + //! offset of the emitter relative to transform + vec2 offset; //! maximum number of particles - const unsigned int max_particles = 0; - //! rate of particle emission per update (Lowest value = 0.001 any lower is ignored) - double emission_rate = 0; + const unsigned int max_particles = 256; + //! rate of particle emission per second + float emission_rate = 50; //! min speed of the particles - double min_speed = 0; + float min_speed = 100; //! min speed of the particles - double max_speed = 0; + float max_speed = 100; //! min angle of particle emission - double min_angle = 0; + float min_angle = 0; //! max angle of particle emission - double max_angle = 0; - //! begin Lifespan of particle (only visual) - double begin_lifespan = 0.0; - //! end Lifespan of particle - double end_lifespan = 0.0; + float max_angle = 0; + //! begin Lifespan of particle in seconds (only visual) + float begin_lifespan = 0.0; + //! end Lifespan of particle in seconds + float end_lifespan = 10.0; //! force over time (physics) vec2 force_over_time; //! particle boundary Boundary boundary; - //! collection of particles - std::vector<Particle> particles; - //! sprite reference - const Sprite & sprite; }; public: @@ -75,11 +78,21 @@ public: * \param game_object_id Identifier for the game object using this emitter. * \param data Configuration data defining particle properties. */ - ParticleEmitter(game_object_id_t game_object_id, const Data & data); + ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite, const Data & data); public: //! Configuration data for particle emission settings. Data data; + +private: + //! Only ParticleSystem can move and read particles + friend ParticleSystem; + //! Only RenderSystem can read particles + friend RenderSystem; + //! Saves time left over from last update event. + float spawn_accumulator = 0; + //! collection of particles + std::vector<Particle> particles; }; } // namespace crepe diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index 753a9e3..583c04f 100644 --- a/src/crepe/api/Script.cpp +++ b/src/crepe/api/Script.cpp @@ -1,7 +1,7 @@ #include <string> +#include "../facade/SDLContext.h" #include "../manager/SceneManager.h" - #include "Script.h" using namespace crepe; @@ -25,3 +25,16 @@ void Script::set_next_scene(const string & name) { } SaveManager & Script::get_save_manager() const { return this->mediator->save_manager; } + +const keyboard_state_t & Script::get_keyboard_state() const { + SDLContext & sdl_context = this->mediator->sdl_context; + return sdl_context.get_keyboard_state(); +} + +bool Script::get_key_state(Keycode key) const noexcept { + try { + return this->get_keyboard_state().at(key); + } catch (...) { + return false; + } +} diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 668e5d1..65306cd 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -2,6 +2,7 @@ #include <vector> +#include "../api/KeyCodes.h" #include "../manager/EventManager.h" #include "../manager/Mediator.h" #include "../system/CollisionSystem.h" @@ -134,7 +135,22 @@ protected: //! Retrieve SaveManager reference SaveManager & get_save_manager() const; - + /** + * \brief Utility function to retrieve the keyboard state + * \see SDLContext::get_keyboard_state + * + * \return current keyboard state map with Keycode as key and bool as value(true = pressed, false = not pressed) + * + */ + const keyboard_state_t & get_keyboard_state() const; + /** + * \brief Utility function to retrieve a single key state. + * \see SDLContext::get_keyboard_state + * + * \return Keycode state (true if pressed, false if not pressed). + * + */ + bool get_key_state(Keycode key) const noexcept; //! \} private: diff --git a/src/crepe/facade/EventData.h b/src/crepe/facade/EventData.h new file mode 100644 index 0000000..a7526b4 --- /dev/null +++ b/src/crepe/facade/EventData.h @@ -0,0 +1,54 @@ +#pragma once +#include "../api/KeyCodes.h" +#include "../types.h" +namespace crepe { +//! EventType enum for passing eventType +enum EventType { + NONE = 0, + MOUSE_DOWN, + MOUSE_UP, + MOUSE_MOVE, + MOUSE_WHEEL, + KEY_UP, + KEY_DOWN, + SHUTDOWN, + WINDOW_MINIMIZE, + WINDOW_MAXIMIZE, + WINDOW_FOCUS_GAIN, + WINDOW_FOCUS_LOST, + WINDOW_MOVE, + WINDOW_RESIZE, + WINDOW_EXPOSE, +}; + +//! Struct for storing key data. +struct KeyData { + Keycode key = Keycode::NONE; + bool key_repeat = false; +}; + +//! Struct for storing mouse data. +struct MouseData { + MouseButton mouse_button = MouseButton::NONE; + ivec2 mouse_position = {-1, -1}; + int scroll_direction = -1; + float scroll_delta = INFINITY; + ivec2 rel_mouse_move = {-1, -1}; +}; + +//! Struct for storing window data. +struct WindowData { + ivec2 move_delta; + ivec2 resize_dimension; +}; + +//! EventData struct for passing event data from facade +struct EventData { + EventType event_type = EventType::NONE; + union { + KeyData key_data; + MouseData mouse_data; + WindowData window_data; + } data; +}; +} // namespace crepe diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 20bb030..7ccc243 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -6,7 +6,6 @@ #include <SDL2/SDL_rect.h> #include <SDL2/SDL_render.h> #include <SDL2/SDL_surface.h> -#include <SDL2/SDL_video.h> #include <array> #include <cmath> #include <cstddef> @@ -75,118 +74,24 @@ SDLContext::~SDLContext() { SDL_Quit(); } -Keycode SDLContext::sdl_to_keycode(SDL_Keycode sdl_key) { - static const std::array<Keycode, SDL_NUM_SCANCODES> LOOKUP_TABLE = [] { - std::array<Keycode, SDL_NUM_SCANCODES> table{}; - table.fill(Keycode::NONE); - - table[SDL_SCANCODE_SPACE] = Keycode::SPACE; - table[SDL_SCANCODE_APOSTROPHE] = Keycode::APOSTROPHE; - table[SDL_SCANCODE_COMMA] = Keycode::COMMA; - table[SDL_SCANCODE_MINUS] = Keycode::MINUS; - table[SDL_SCANCODE_PERIOD] = Keycode::PERIOD; - table[SDL_SCANCODE_SLASH] = Keycode::SLASH; - table[SDL_SCANCODE_0] = Keycode::D0; - table[SDL_SCANCODE_1] = Keycode::D1; - table[SDL_SCANCODE_2] = Keycode::D2; - table[SDL_SCANCODE_3] = Keycode::D3; - table[SDL_SCANCODE_4] = Keycode::D4; - table[SDL_SCANCODE_5] = Keycode::D5; - table[SDL_SCANCODE_6] = Keycode::D6; - table[SDL_SCANCODE_7] = Keycode::D7; - table[SDL_SCANCODE_8] = Keycode::D8; - table[SDL_SCANCODE_9] = Keycode::D9; - table[SDL_SCANCODE_SEMICOLON] = Keycode::SEMICOLON; - table[SDL_SCANCODE_EQUALS] = Keycode::EQUAL; - table[SDL_SCANCODE_A] = Keycode::A; - table[SDL_SCANCODE_B] = Keycode::B; - table[SDL_SCANCODE_C] = Keycode::C; - table[SDL_SCANCODE_D] = Keycode::D; - table[SDL_SCANCODE_E] = Keycode::E; - table[SDL_SCANCODE_F] = Keycode::F; - table[SDL_SCANCODE_G] = Keycode::G; - table[SDL_SCANCODE_H] = Keycode::H; - table[SDL_SCANCODE_I] = Keycode::I; - table[SDL_SCANCODE_J] = Keycode::J; - table[SDL_SCANCODE_K] = Keycode::K; - table[SDL_SCANCODE_L] = Keycode::L; - table[SDL_SCANCODE_M] = Keycode::M; - table[SDL_SCANCODE_N] = Keycode::N; - table[SDL_SCANCODE_O] = Keycode::O; - table[SDL_SCANCODE_P] = Keycode::P; - table[SDL_SCANCODE_Q] = Keycode::Q; - table[SDL_SCANCODE_R] = Keycode::R; - table[SDL_SCANCODE_S] = Keycode::S; - table[SDL_SCANCODE_T] = Keycode::T; - table[SDL_SCANCODE_U] = Keycode::U; - table[SDL_SCANCODE_V] = Keycode::V; - table[SDL_SCANCODE_W] = Keycode::W; - table[SDL_SCANCODE_X] = Keycode::X; - table[SDL_SCANCODE_Y] = Keycode::Y; - table[SDL_SCANCODE_Z] = Keycode::Z; - table[SDL_SCANCODE_LEFTBRACKET] = Keycode::LEFT_BRACKET; - table[SDL_SCANCODE_BACKSLASH] = Keycode::BACKSLASH; - table[SDL_SCANCODE_RIGHTBRACKET] = Keycode::RIGHT_BRACKET; - table[SDL_SCANCODE_GRAVE] = Keycode::GRAVE_ACCENT; - table[SDL_SCANCODE_ESCAPE] = Keycode::ESCAPE; - table[SDL_SCANCODE_RETURN] = Keycode::ENTER; - table[SDL_SCANCODE_TAB] = Keycode::TAB; - table[SDL_SCANCODE_BACKSPACE] = Keycode::BACKSPACE; - table[SDL_SCANCODE_INSERT] = Keycode::INSERT; - table[SDL_SCANCODE_DELETE] = Keycode::DELETE; - table[SDL_SCANCODE_RIGHT] = Keycode::RIGHT; - table[SDL_SCANCODE_LEFT] = Keycode::LEFT; - table[SDL_SCANCODE_DOWN] = Keycode::DOWN; - table[SDL_SCANCODE_UP] = Keycode::UP; - table[SDL_SCANCODE_PAGEUP] = Keycode::PAGE_UP; - table[SDL_SCANCODE_PAGEDOWN] = Keycode::PAGE_DOWN; - table[SDL_SCANCODE_HOME] = Keycode::HOME; - table[SDL_SCANCODE_END] = Keycode::END; - table[SDL_SCANCODE_CAPSLOCK] = Keycode::CAPS_LOCK; - table[SDL_SCANCODE_SCROLLLOCK] = Keycode::SCROLL_LOCK; - table[SDL_SCANCODE_NUMLOCKCLEAR] = Keycode::NUM_LOCK; - table[SDL_SCANCODE_PRINTSCREEN] = Keycode::PRINT_SCREEN; - table[SDL_SCANCODE_PAUSE] = Keycode::PAUSE; - table[SDL_SCANCODE_F1] = Keycode::F1; - table[SDL_SCANCODE_F2] = Keycode::F2; - table[SDL_SCANCODE_F3] = Keycode::F3; - table[SDL_SCANCODE_F4] = Keycode::F4; - table[SDL_SCANCODE_F5] = Keycode::F5; - table[SDL_SCANCODE_F6] = Keycode::F6; - table[SDL_SCANCODE_F7] = Keycode::F7; - table[SDL_SCANCODE_F8] = Keycode::F8; - table[SDL_SCANCODE_F9] = Keycode::F9; - table[SDL_SCANCODE_F10] = Keycode::F10; - table[SDL_SCANCODE_F11] = Keycode::F11; - table[SDL_SCANCODE_F12] = Keycode::F12; - table[SDL_SCANCODE_KP_0] = Keycode::KP0; - table[SDL_SCANCODE_KP_1] = Keycode::KP1; - table[SDL_SCANCODE_KP_2] = Keycode::KP2; - table[SDL_SCANCODE_KP_3] = Keycode::KP3; - table[SDL_SCANCODE_KP_4] = Keycode::KP4; - table[SDL_SCANCODE_KP_5] = Keycode::KP5; - table[SDL_SCANCODE_KP_6] = Keycode::KP6; - table[SDL_SCANCODE_KP_7] = Keycode::KP7; - table[SDL_SCANCODE_KP_8] = Keycode::KP8; - table[SDL_SCANCODE_KP_9] = Keycode::KP9; - table[SDL_SCANCODE_LSHIFT] = Keycode::LEFT_SHIFT; - table[SDL_SCANCODE_LCTRL] = Keycode::LEFT_CONTROL; - table[SDL_SCANCODE_LALT] = Keycode::LEFT_ALT; - table[SDL_SCANCODE_LGUI] = Keycode::LEFT_SUPER; - table[SDL_SCANCODE_RSHIFT] = Keycode::RIGHT_SHIFT; - table[SDL_SCANCODE_RCTRL] = Keycode::RIGHT_CONTROL; - table[SDL_SCANCODE_RALT] = Keycode::RIGHT_ALT; - table[SDL_SCANCODE_RGUI] = Keycode::RIGHT_SUPER; - table[SDL_SCANCODE_MENU] = Keycode::MENU; +Keycode SDLContext::sdl_to_keycode(SDL_Scancode sdl_key) { + if (!LOOKUP_TABLE.contains(sdl_key)) return Keycode::NONE; - return table; - }(); + return LOOKUP_TABLE.at(sdl_key); +} - if (sdl_key < 0 || sdl_key >= SDL_NUM_SCANCODES) { - return Keycode::NONE; - } +const keyboard_state_t & SDLContext::get_keyboard_state() { + SDL_PumpEvents(); + const Uint8 * current_state = SDL_GetKeyboardState(nullptr); + + for (int i = 0; i < SDL_NUM_SCANCODES; ++i) { - return LOOKUP_TABLE[sdl_key]; + Keycode key = sdl_to_keycode(static_cast<SDL_Scancode>(i)); + if (key != Keycode::NONE) { + this->keyboard_state[key] = current_state[i] != 0; + } + } + return this->keyboard_state; } MouseButton SDLContext::sdl_to_mousebutton(Uint8 sdl_button) { @@ -365,8 +270,8 @@ ivec2 SDLContext::get_size(const Texture & ctx) { return size; } -std::vector<SDLContext::EventData> SDLContext::get_events() { - std::vector<SDLContext::EventData> event_list; +std::vector<EventData> SDLContext::get_events() { + std::vector<EventData> event_list; SDL_Event event; const CameraAuxiliaryData & cam = this->cam_aux_data; while (SDL_PollEvent(&event)) { @@ -375,60 +280,130 @@ std::vector<SDLContext::EventData> SDLContext::get_events() { mouse_pos.y = (event.button.y - cam.bar_size.y) / cam.render_scale.y; switch (event.type) { case SDL_QUIT: - event_list.push_back(EventData{ - .event_type = SDLContext::EventType::SHUTDOWN, - }); + event_list.push_back({.event_type = EventType::SHUTDOWN}); break; case SDL_KEYDOWN: event_list.push_back(EventData{ - .event_type = SDLContext::EventType::KEYDOWN, - .key = sdl_to_keycode(event.key.keysym.scancode), - .key_repeat = (event.key.repeat != 0), + .event_type = EventType::KEY_DOWN, + .data = { + .key_data = { + .key = this->sdl_to_keycode(event.key.keysym.scancode), + .key_repeat = event.key.repeat != 0, + }, + }, }); break; + case SDL_KEYUP: event_list.push_back(EventData{ - .event_type = SDLContext::EventType::KEYUP, - .key = sdl_to_keycode(event.key.keysym.scancode), + .event_type = EventType::KEY_UP, + .data = { + .key_data = { + .key = this->sdl_to_keycode(event.key.keysym.scancode), + .key_repeat = event.key.repeat != 0, + }, + }, }); break; + case SDL_MOUSEBUTTONDOWN: event_list.push_back(EventData{ - .event_type = SDLContext::EventType::MOUSEDOWN, - .mouse_button = sdl_to_mousebutton(event.button.button), - .mouse_position = mouse_pos, + .event_type = EventType::MOUSE_DOWN, + .data = { + .mouse_data = { + .mouse_button = this->sdl_to_mousebutton(event.button.button), + .mouse_position = mouse_pos, + }, + }, }); break; - case SDL_MOUSEBUTTONUP: { - int x, y; - SDL_GetMouseState(&x, &y); + case SDL_MOUSEBUTTONUP: event_list.push_back(EventData{ - .event_type = SDLContext::EventType::MOUSEUP, - .mouse_button = sdl_to_mousebutton(event.button.button), - .mouse_position = mouse_pos, + .event_type = EventType::MOUSE_UP, + .data = { + .mouse_data = { + .mouse_button = this->sdl_to_mousebutton(event.button.button), + .mouse_position = mouse_pos, + }, + }, }); - } break; + break; - case SDL_MOUSEMOTION: { - event_list.push_back( - EventData{.event_type = SDLContext::EventType::MOUSEMOVE, - .mouse_position = mouse_pos, - .rel_mouse_move = {event.motion.xrel, event.motion.yrel}}); - } break; + case SDL_MOUSEMOTION: + event_list.push_back(EventData{ + .event_type = EventType::MOUSE_MOVE, + .data = { + .mouse_data = { + .mouse_position = mouse_pos, + .rel_mouse_move = {event.motion.xrel, event.motion.yrel}, + }, + }, + }); + break; - case SDL_MOUSEWHEEL: { + case SDL_MOUSEWHEEL: event_list.push_back(EventData{ - .event_type = SDLContext::EventType::MOUSEWHEEL, - .mouse_position = mouse_pos, - // TODO: why is this needed? - .scroll_direction = event.wheel.y < 0 ? -1 : 1, - .scroll_delta = event.wheel.preciseY, + .event_type = EventType::MOUSE_WHEEL, + .data = { + .mouse_data = { + .mouse_position = mouse_pos, + .scroll_direction = event.wheel.y < 0 ? -1 : 1, + .scroll_delta = event.wheel.preciseY, + }, + }, }); - } break; + break; + case SDL_WINDOWEVENT: + this->handle_window_event(event.window, event_list); + break; } } + return event_list; } + +void SDLContext::handle_window_event(const SDL_WindowEvent & window_event, + std::vector<EventData> & event_list) { + switch (window_event.event) { + case SDL_WINDOWEVENT_EXPOSED: + event_list.push_back(EventData{EventType::WINDOW_EXPOSE}); + break; + case SDL_WINDOWEVENT_RESIZED: + event_list.push_back(EventData{ + .event_type = EventType::WINDOW_RESIZE, + .data = { + .window_data = { + .resize_dimension = {window_event.data1, window_event.data2} + }, + }, + }); + break; + case SDL_WINDOWEVENT_MOVED: + event_list.push_back(EventData{ + .event_type = EventType::WINDOW_MOVE, + .data = { + .window_data = { + .move_delta = {window_event.data1, window_event.data2} + }, + }, + }); + break; + + case SDL_WINDOWEVENT_MINIMIZED: + event_list.push_back(EventData{EventType::WINDOW_MINIMIZE}); + break; + case SDL_WINDOWEVENT_MAXIMIZED: + event_list.push_back(EventData{EventType::WINDOW_MAXIMIZE}); + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + event_list.push_back(EventData{EventType::WINDOW_FOCUS_GAIN}); + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + event_list.push_back(EventData{EventType::WINDOW_FOCUS_LOST}); + break; + } +} + void SDLContext::set_color_texture(const Texture & texture, const Color & color) { SDL_SetTextureColorMod(texture.get_img(), color.r, color.g, color.b); SDL_SetTextureAlphaMod(texture.get_img(), color.a); diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index bcadf87..8f4760e 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -9,17 +9,18 @@ #include <functional> #include <memory> #include <string> +#include <unordered_map> +#include "../types.h" #include "api/Camera.h" #include "api/Color.h" #include "api/KeyCodes.h" #include "api/Sprite.h" #include "api/Transform.h" -#include "types.h" +#include "EventData.h" namespace crepe { - class Texture; class Mediator; @@ -69,29 +70,11 @@ public: }; public: - //! EventType enum for passing eventType - enum EventType { - NONE = 0, - MOUSEDOWN, - MOUSEUP, - MOUSEMOVE, - MOUSEWHEEL, - KEYUP, - KEYDOWN, - SHUTDOWN, - - }; - //! EventData struct for passing event data from facade - struct EventData { - SDLContext::EventType event_type = SDLContext::EventType::NONE; - Keycode key = Keycode::NONE; - bool key_repeat = false; - MouseButton mouse_button = MouseButton::NONE; - ivec2 mouse_position = {-1, -1}; - int scroll_direction = -1; - float scroll_delta = INFINITY; - ivec2 rel_mouse_move = {-1, -1}; - }; + /** + * \brief Gets the singleton instance of SDLContext. + * \return Reference to the SDLContext instance. + */ + static SDLContext & get_instance(); public: SDLContext(const SDLContext &) = delete; @@ -122,18 +105,25 @@ public: * * \return Events that occurred since last call to `get_events()` */ - std::vector<SDLContext::EventData> get_events(); - + std::vector<EventData> get_events(); /** - * \brief Converts an SDL key code to the custom Keycode type. + * \brief Fills event_list with triggered window events * - * This method maps an SDL key code to the corresponding `Keycode` enum value, + * This method checks if any window events are triggered and adds them to the event_list. + * + */ + void handle_window_event(const SDL_WindowEvent & window_event, + std::vector<EventData> & event_list); + /** + * \brief Converts an SDL scan code to the custom Keycode type. + * + * This method maps an SDL scan code to the corresponding `Keycode` enum value, * which is used internally by the system to identify the keys. * - * \param sdl_key The SDL key code to convert. + * \param sdl_key The SDL scan code to convert. * \return The corresponding `Keycode` value or `Keycode::NONE` if the key is unrecognized. */ - Keycode sdl_to_keycode(SDL_Keycode sdl_key); + Keycode sdl_to_keycode(SDL_Scancode sdl_key); /** * \brief Converts an SDL mouse button code to the custom MouseButton type. @@ -145,6 +135,16 @@ public: * \return The corresponding `MouseButton` value or `MouseButton::NONE` if the key is unrecognized */ MouseButton sdl_to_mousebutton(Uint8 sdl_button); + /** + * \brief Gets the current state of the keyboard. + * + * Updates the internal keyboard state by checking the current key states using + * SDL's `SDL_GetKeyboardState()`, and returns a reference to the `keyboard_state_t`. + * + * \return A constant reference to the `keyboard_state_t`, which holds the state + * of each key (true = pressed, false = not pressed). + */ + const keyboard_state_t & get_keyboard_state(); public: /** @@ -240,6 +240,110 @@ private: * - this is defined in this class because get_events() needs this information aswell */ CameraAuxiliaryData cam_aux_data; + +private: + //! variable to store the state of each key (true = pressed, false = not pressed) + keyboard_state_t keyboard_state; + //! lookup table for converting SDL_SCANCODES to Keycodes + const std::unordered_map<SDL_Scancode, Keycode> LOOKUP_TABLE + = {{SDL_SCANCODE_SPACE, Keycode::SPACE}, + {SDL_SCANCODE_APOSTROPHE, Keycode::APOSTROPHE}, + {SDL_SCANCODE_COMMA, Keycode::COMMA}, + {SDL_SCANCODE_MINUS, Keycode::MINUS}, + {SDL_SCANCODE_PERIOD, Keycode::PERIOD}, + {SDL_SCANCODE_SLASH, Keycode::SLASH}, + {SDL_SCANCODE_0, Keycode::D0}, + {SDL_SCANCODE_1, Keycode::D1}, + {SDL_SCANCODE_2, Keycode::D2}, + {SDL_SCANCODE_3, Keycode::D3}, + {SDL_SCANCODE_4, Keycode::D4}, + {SDL_SCANCODE_5, Keycode::D5}, + {SDL_SCANCODE_6, Keycode::D6}, + {SDL_SCANCODE_7, Keycode::D7}, + {SDL_SCANCODE_8, Keycode::D8}, + {SDL_SCANCODE_9, Keycode::D9}, + {SDL_SCANCODE_SEMICOLON, Keycode::SEMICOLON}, + {SDL_SCANCODE_EQUALS, Keycode::EQUAL}, + {SDL_SCANCODE_A, Keycode::A}, + {SDL_SCANCODE_B, Keycode::B}, + {SDL_SCANCODE_C, Keycode::C}, + {SDL_SCANCODE_D, Keycode::D}, + {SDL_SCANCODE_E, Keycode::E}, + {SDL_SCANCODE_F, Keycode::F}, + {SDL_SCANCODE_G, Keycode::G}, + {SDL_SCANCODE_H, Keycode::H}, + {SDL_SCANCODE_I, Keycode::I}, + {SDL_SCANCODE_J, Keycode::J}, + {SDL_SCANCODE_K, Keycode::K}, + {SDL_SCANCODE_L, Keycode::L}, + {SDL_SCANCODE_M, Keycode::M}, + {SDL_SCANCODE_N, Keycode::N}, + {SDL_SCANCODE_O, Keycode::O}, + {SDL_SCANCODE_P, Keycode::P}, + {SDL_SCANCODE_Q, Keycode::Q}, + {SDL_SCANCODE_R, Keycode::R}, + {SDL_SCANCODE_S, Keycode::S}, + {SDL_SCANCODE_T, Keycode::T}, + {SDL_SCANCODE_U, Keycode::U}, + {SDL_SCANCODE_V, Keycode::V}, + {SDL_SCANCODE_W, Keycode::W}, + {SDL_SCANCODE_X, Keycode::X}, + {SDL_SCANCODE_Y, Keycode::Y}, + {SDL_SCANCODE_Z, Keycode::Z}, + {SDL_SCANCODE_LEFTBRACKET, Keycode::LEFT_BRACKET}, + {SDL_SCANCODE_BACKSLASH, Keycode::BACKSLASH}, + {SDL_SCANCODE_RIGHTBRACKET, Keycode::RIGHT_BRACKET}, + {SDL_SCANCODE_GRAVE, Keycode::GRAVE_ACCENT}, + {SDL_SCANCODE_ESCAPE, Keycode::ESCAPE}, + {SDL_SCANCODE_RETURN, Keycode::ENTER}, + {SDL_SCANCODE_TAB, Keycode::TAB}, + {SDL_SCANCODE_BACKSPACE, Keycode::BACKSPACE}, + {SDL_SCANCODE_INSERT, Keycode::INSERT}, + {SDL_SCANCODE_DELETE, Keycode::DELETE}, + {SDL_SCANCODE_RIGHT, Keycode::RIGHT}, + {SDL_SCANCODE_LEFT, Keycode::LEFT}, + {SDL_SCANCODE_DOWN, Keycode::DOWN}, + {SDL_SCANCODE_UP, Keycode::UP}, + {SDL_SCANCODE_PAGEUP, Keycode::PAGE_UP}, + {SDL_SCANCODE_PAGEDOWN, Keycode::PAGE_DOWN}, + {SDL_SCANCODE_HOME, Keycode::HOME}, + {SDL_SCANCODE_END, Keycode::END}, + {SDL_SCANCODE_CAPSLOCK, Keycode::CAPS_LOCK}, + {SDL_SCANCODE_SCROLLLOCK, Keycode::SCROLL_LOCK}, + {SDL_SCANCODE_NUMLOCKCLEAR, Keycode::NUM_LOCK}, + {SDL_SCANCODE_PRINTSCREEN, Keycode::PRINT_SCREEN}, + {SDL_SCANCODE_PAUSE, Keycode::PAUSE}, + {SDL_SCANCODE_F1, Keycode::F1}, + {SDL_SCANCODE_F2, Keycode::F2}, + {SDL_SCANCODE_F3, Keycode::F3}, + {SDL_SCANCODE_F4, Keycode::F4}, + {SDL_SCANCODE_F5, Keycode::F5}, + {SDL_SCANCODE_F6, Keycode::F6}, + {SDL_SCANCODE_F7, Keycode::F7}, + {SDL_SCANCODE_F8, Keycode::F8}, + {SDL_SCANCODE_F9, Keycode::F9}, + {SDL_SCANCODE_F10, Keycode::F10}, + {SDL_SCANCODE_F11, Keycode::F11}, + {SDL_SCANCODE_F12, Keycode::F12}, + {SDL_SCANCODE_KP_0, Keycode::KP0}, + {SDL_SCANCODE_KP_1, Keycode::KP1}, + {SDL_SCANCODE_KP_2, Keycode::KP2}, + {SDL_SCANCODE_KP_3, Keycode::KP3}, + {SDL_SCANCODE_KP_4, Keycode::KP4}, + {SDL_SCANCODE_KP_5, Keycode::KP5}, + {SDL_SCANCODE_KP_6, Keycode::KP6}, + {SDL_SCANCODE_KP_7, Keycode::KP7}, + {SDL_SCANCODE_KP_8, Keycode::KP8}, + {SDL_SCANCODE_KP_9, Keycode::KP9}, + {SDL_SCANCODE_LSHIFT, Keycode::LEFT_SHIFT}, + {SDL_SCANCODE_LCTRL, Keycode::LEFT_CONTROL}, + {SDL_SCANCODE_LALT, Keycode::LEFT_ALT}, + {SDL_SCANCODE_LGUI, Keycode::LEFT_SUPER}, + {SDL_SCANCODE_RSHIFT, Keycode::RIGHT_SHIFT}, + {SDL_SCANCODE_RCTRL, Keycode::RIGHT_CONTROL}, + {SDL_SCANCODE_RALT, Keycode::RIGHT_ALT}, + {SDL_SCANCODE_RGUI, Keycode::RIGHT_SUPER}, + {SDL_SCANCODE_MENU, Keycode::MENU}}; }; } // namespace crepe diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp index a710ae2..fca540f 100644 --- a/src/crepe/system/InputSystem.cpp +++ b/src/crepe/system/InputSystem.cpp @@ -1,7 +1,7 @@ #include "../api/Button.h" +#include "../facade/SDLContext.h" #include "../manager/ComponentManager.h" #include "../manager/EventManager.h" -#include "facade/SDLContext.h" #include "util/Log.h" #include "InputSystem.h" @@ -10,12 +10,13 @@ using namespace crepe; void InputSystem::update() { ComponentManager & mgr = this->mediator.component_manager; - EventManager & event_mgr = this->mediator.event_manager; + SDLContext & context = this->mediator.sdl_context; - std::vector<SDLContext::EventData> event_list = context.get_events(); + std::vector<EventData> event_list = context.get_events(); RefVector<Button> buttons = mgr.get_components_by_type<Button>(); RefVector<Camera> cameras = mgr.get_components_by_type<Camera>(); OptionalRef<Camera> curr_cam_ref; + // Find the active camera for (Camera & cam : cameras) { if (!cam.active) continue; @@ -23,150 +24,185 @@ void InputSystem::update() { break; } if (!curr_cam_ref) return; + Camera & current_cam = curr_cam_ref; RefVector<Transform> transform_vec = mgr.get_components_by_id<Transform>(current_cam.game_object_id); Transform & cam_transform = transform_vec.front().get(); - int camera_origin_x = cam_transform.position.x + current_cam.data.postion_offset.x - - (current_cam.viewport_size.x / 2); - int camera_origin_y = cam_transform.position.y + current_cam.data.postion_offset.y - - (current_cam.viewport_size.y / 2); - - for (const SDLContext::EventData & event : event_list) { - int world_mouse_x = event.mouse_position.x + camera_origin_x; - int world_mouse_y = event.mouse_position.y + camera_origin_y; - // check if the mouse is within the viewport - bool mouse_in_viewport - = !(world_mouse_x < camera_origin_x - || world_mouse_x > camera_origin_x + current_cam.viewport_size.x - || world_mouse_y < camera_origin_y - || world_mouse_y > camera_origin_y + current_cam.viewport_size.y); - - switch (event.event_type) { - case SDLContext::EventType::KEYDOWN: - event_mgr.queue_event<KeyPressEvent>(KeyPressEvent{ - .repeat = event.key_repeat, - .key = event.key, - }); - break; - case SDLContext::EventType::KEYUP: - event_mgr.queue_event<KeyReleaseEvent>(KeyReleaseEvent{ - .key = event.key, - }); - break; - case SDLContext::EventType::MOUSEDOWN: - if (!mouse_in_viewport) { - break; - } - event_mgr.queue_event<MousePressEvent>(MousePressEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .button = event.mouse_button, - }); - this->last_mouse_down_position = {world_mouse_x, world_mouse_y}; - this->last_mouse_button = event.mouse_button; - break; - case SDLContext::EventType::MOUSEUP: { - if (!mouse_in_viewport) { - break; - } - event_mgr.queue_event<MouseReleaseEvent>(MouseReleaseEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .button = event.mouse_button, - }); - //check if its a click by checking the last button down - int delta_x = world_mouse_x - this->last_mouse_down_position.x; - int delta_y = world_mouse_y - this->last_mouse_down_position.y; - - if (this->last_mouse_button == event.mouse_button - && std::abs(delta_x) <= click_tolerance - && std::abs(delta_y) <= click_tolerance) { - event_mgr.queue_event<MouseClickEvent>(MouseClickEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .button = event.mouse_button, - }); - - this->handle_click(event.mouse_button, world_mouse_x, world_mouse_y); - } - } break; - case SDLContext::EventType::MOUSEMOVE: - if (!mouse_in_viewport) { - break; - } - event_mgr.queue_event<MouseMoveEvent>(MouseMoveEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .delta_x = event.rel_mouse_move.x, - .delta_y = event.rel_mouse_move.y, - }); - this->handle_move(event, world_mouse_x, world_mouse_y); - break; - case SDLContext::EventType::MOUSEWHEEL: - event_mgr.queue_event<MouseScrollEvent>(MouseScrollEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .scroll_direction = event.scroll_direction, - .scroll_delta = event.scroll_delta, + + vec2 camera_origin = cam_transform.position + current_cam.data.postion_offset + - (current_cam.viewport_size / 2); + + for (const EventData & event : event_list) { + // Only calculate mouse coordinates for relevant events + if (event.event_type == EventType::MOUSE_DOWN + || event.event_type == EventType::MOUSE_UP + || event.event_type == EventType::MOUSE_MOVE + || event.event_type == EventType::MOUSE_WHEEL) { + this->handle_mouse_event(event, camera_origin, current_cam); + + } else { + this->handle_non_mouse_event(event); + } + } +} + +void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camera_origin, + const Camera & current_cam) { + EventManager & event_mgr = this->mediator.event_manager; + vec2 adjusted_mouse; + adjusted_mouse.x = event.data.mouse_data.mouse_position.x + camera_origin.x; + adjusted_mouse.x = event.data.mouse_data.mouse_position.y + camera_origin.y; + // Check if the mouse is within the viewport + if ((adjusted_mouse.x < camera_origin.x + || adjusted_mouse.x > camera_origin.x + current_cam.viewport_size.x + || adjusted_mouse.y < camera_origin.y + || adjusted_mouse.y > camera_origin.y + current_cam.viewport_size.y)) + return; + + // Handle mouse-specific events + switch (event.event_type) { + case EventType::MOUSE_DOWN: + event_mgr.queue_event<MousePressEvent>({ + .mouse_pos = adjusted_mouse, + .button = event.data.mouse_data.mouse_button, + }); + this->last_mouse_down_position = adjusted_mouse; + this->last_mouse_button = event.data.mouse_data.mouse_button; + break; + + case EventType::MOUSE_UP: { + event_mgr.queue_event<MouseReleaseEvent>({ + .mouse_pos = adjusted_mouse, + .button = event.data.mouse_data.mouse_button, + }); + vec2 delta_move = adjusted_mouse - this->last_mouse_down_position; + int click_tolerance = Config::get_instance().input.click_tolerance; + if (this->last_mouse_button == event.data.mouse_data.mouse_button + && std::abs(delta_move.x) <= click_tolerance + && std::abs(delta_move.y) <= click_tolerance) { + event_mgr.queue_event<MouseClickEvent>({ + .mouse_pos = adjusted_mouse, + .button = event.data.mouse_data.mouse_button, }); - break; - case SDLContext::EventType::SHUTDOWN: - event_mgr.queue_event<ShutDownEvent>(ShutDownEvent{}); - break; - default: - break; + this->handle_click(event.data.mouse_data.mouse_button, adjusted_mouse); + } + break; } + + case EventType::MOUSE_MOVE: + event_mgr.queue_event<MouseMoveEvent>({ + .mouse_pos = adjusted_mouse, + .mouse_delta = event.data.mouse_data.rel_mouse_move, + }); + this->handle_move(event, adjusted_mouse); + break; + + case EventType::MOUSE_WHEEL: + event_mgr.queue_event<MouseScrollEvent>({ + .mouse_pos = adjusted_mouse, + .scroll_direction = event.data.mouse_data.scroll_direction, + .scroll_delta = event.data.mouse_data.scroll_delta, + }); + break; + + default: + break; + } +} + +void InputSystem::handle_non_mouse_event(const EventData & event) { + EventManager & event_mgr = this->mediator.event_manager; + switch (event.event_type) { + case EventType::KEY_DOWN: + + event_mgr.queue_event<KeyPressEvent>( + {.repeat = event.data.key_data.key_repeat, .key = event.data.key_data.key}); + break; + case EventType::KEY_UP: + event_mgr.queue_event<KeyReleaseEvent>({.key = event.data.key_data.key}); + break; + case EventType::SHUTDOWN: + event_mgr.queue_event<ShutDownEvent>({}); + break; + case EventType::WINDOW_EXPOSE: + event_mgr.queue_event<WindowExposeEvent>({}); + break; + case EventType::WINDOW_RESIZE: + event_mgr.queue_event<WindowResizeEvent>( + WindowResizeEvent{.dimensions = event.data.window_data.resize_dimension}); + break; + case EventType::WINDOW_MOVE: + event_mgr.queue_event<WindowMoveEvent>( + {.delta_move = event.data.window_data.move_delta}); + break; + case EventType::WINDOW_MINIMIZE: + event_mgr.queue_event<WindowMinimizeEvent>({}); + break; + case EventType::WINDOW_MAXIMIZE: + event_mgr.queue_event<WindowMaximizeEvent>({}); + break; + case EventType::WINDOW_FOCUS_GAIN: + event_mgr.queue_event<WindowFocusGainEvent>({}); + break; + case EventType::WINDOW_FOCUS_LOST: + event_mgr.queue_event<WindowFocusLostEvent>({}); + break; + default: + break; } } -void InputSystem::handle_move(const SDLContext::EventData & event_data, - const int world_mouse_x, const int world_mouse_y) { + +void InputSystem::handle_move(const EventData & event_data, const vec2 & mouse_pos) { ComponentManager & mgr = this->mediator.component_manager; RefVector<Button> buttons = mgr.get_components_by_type<Button>(); for (Button & button : buttons) { + if (!button.active) continue; RefVector<Transform> transform_vec = mgr.get_components_by_id<Transform>(button.game_object_id); Transform & transform(transform_vec.front().get()); bool was_hovering = button.hover; - if (button.active - && this->is_mouse_inside_button(world_mouse_x, world_mouse_y, button, transform)) { + if (this->is_mouse_inside_button(mouse_pos, button, transform)) { button.hover = true; - if (!was_hovering && button.on_mouse_enter) { + if (!button.on_mouse_enter) continue; + if (!was_hovering) { button.on_mouse_enter(); } } else { button.hover = false; // Trigger the on_exit callback if the hover state just changed to false - if (was_hovering && button.on_mouse_exit) { + if (!button.on_mouse_exit) continue; + if (was_hovering) { button.on_mouse_exit(); } } } } -void InputSystem::handle_click(const MouseButton & mouse_button, const int world_mouse_x, - const int world_mouse_y) { +void InputSystem::handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos) { ComponentManager & mgr = this->mediator.component_manager; RefVector<Button> buttons = mgr.get_components_by_type<Button>(); for (Button & button : buttons) { + if (!button.active) continue; + if (!button.on_click) continue; RefVector<Transform> transform_vec = mgr.get_components_by_id<Transform>(button.game_object_id); Transform & transform = transform_vec.front().get(); - if (button.active - && this->is_mouse_inside_button(world_mouse_x, world_mouse_y, button, transform)) { - this->handle_button_press(button); + if (this->is_mouse_inside_button(mouse_pos, button, transform)) { + + button.on_click(); } } } -bool InputSystem::is_mouse_inside_button(const int mouse_x, const int mouse_y, - const Button & button, const Transform & transform) { +bool InputSystem::is_mouse_inside_button(const vec2 & mouse_pos, const Button & button, + const Transform & transform) { int actual_x = transform.position.x + button.offset.x; int actual_y = transform.position.y + button.offset.y; @@ -174,17 +210,6 @@ bool InputSystem::is_mouse_inside_button(const int mouse_x, const int mouse_y, int half_height = button.dimensions.y / 2; // Check if the mouse is within the button's boundaries - return mouse_x >= actual_x - half_width && mouse_x <= actual_x + half_width - && mouse_y >= actual_y - half_height && mouse_y <= actual_y + half_height; -} - -void InputSystem::handle_button_press(Button & button) { - if (button.is_toggle) { - if (!button.is_pressed && button.on_click) { - button.on_click(); - } - button.is_pressed = !button.is_pressed; - } else if (button.on_click) { - button.on_click(); - } + return mouse_pos.x >= actual_x - half_width && mouse_pos.x <= actual_x + half_width + && mouse_pos.y >= actual_y - half_height && mouse_pos.y <= actual_y + half_height; } diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h index 87e86f8..eefd9fe 100644 --- a/src/crepe/system/InputSystem.h +++ b/src/crepe/system/InputSystem.h @@ -1,6 +1,8 @@ #pragma once -#include "../facade/SDLContext.h" +#include "../api/Config.h" +#include "../facade/EventData.h" + #include "../types.h" #include "../util/OptionalRef.h" @@ -11,7 +13,6 @@ namespace crepe { class Camera; class Button; class Transform; - /** * \brief Handles the processing of input events created by SDLContext * @@ -31,15 +32,30 @@ public: private: //! Stores the last position of the mouse when the button was pressed. - ivec2 last_mouse_down_position; + vec2 last_mouse_down_position; // TODO: specify world/hud space and make regular `vec2` //! Stores the last mouse button pressed. MouseButton last_mouse_button = MouseButton::NONE; - - //! The maximum allowable distance between mouse down and mouse up to register as a click. - const int click_tolerance = 5; - + /** + * \brief Handles mouse-related events. + * \param event The event data for the mouse event. + * \param camera_origin The origin position of the camera in world space. + * \param current_cam The currently active camera. + * + * This method processes mouse events, adjusts the mouse position to world coordinates, + * and triggers the appropriate mouse-specific event handling logic. + */ + void handle_mouse_event(const EventData & event, const vec2 & camera_origin, + const Camera & current_cam); + /** + * \brief Handles non-mouse-related events. + * \param event The event data for the non-mouse event. + * + * This method processes events that do not involve the mouse, such as keyboard events, + * window events, and shutdown events, and triggers the corresponding event actions. + */ + void handle_non_mouse_event(const EventData & event); /** * \brief Handles the mouse click event. * \param mouse_button The mouse button involved in the click. @@ -48,8 +64,7 @@ private: * * This method processes the mouse click event and triggers the corresponding button action. */ - void handle_click(const MouseButton & mouse_button, const int world_mouse_x, - const int world_mouse_y); + void handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos); /** * \brief Handles the mouse movement event. @@ -59,8 +74,7 @@ private: * * This method processes the mouse movement event and updates the button hover state. */ - void handle_move(const SDLContext::EventData & event_data, const int world_mouse_x, - const int world_mouse_y); + void handle_move(const EventData & event_data, const vec2 & mouse_pos); /** * \brief Checks if the mouse position is inside the bounds of the button. @@ -70,8 +84,8 @@ private: * \param transform The transform component of the button. * \return True if the mouse is inside the button, false otherwise. */ - bool is_mouse_inside_button(const int world_mouse_x, const int world_mouse_y, - const Button & button, const Transform & transform); + bool is_mouse_inside_button(const vec2 & mouse_pos, const Button & button, + const Transform & transform); /** * \brief Handles the button press event, calling the on_click callback if necessary. diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp index b14c52f..35a1d41 100644 --- a/src/crepe/system/ParticleSystem.cpp +++ b/src/crepe/system/ParticleSystem.cpp @@ -1,3 +1,4 @@ +#include <chrono> #include <cmath> #include <cstdlib> #include <ctime> @@ -5,6 +6,7 @@ #include "../api/ParticleEmitter.h" #include "../api/Transform.h" #include "../manager/ComponentManager.h" +#include "../manager/LoopTimerManager.h" #include "ParticleSystem.h" @@ -12,7 +14,11 @@ using namespace crepe; void ParticleSystem::update() { // Get all emitters - ComponentManager & mgr = this->mediator.component_manager; + const Mediator & mediator = this->mediator; + LoopTimerManager & loop_timer = mediator.loop_timer; + ComponentManager & mgr = mediator.component_manager; + float dt = loop_timer.get_scaled_fixed_delta_time().count(); + RefVector<ParticleEmitter> emitters = mgr.get_components_by_type<ParticleEmitter>(); for (ParticleEmitter & emitter : emitters) { @@ -21,38 +27,39 @@ void ParticleSystem::update() { = mgr.get_components_by_id<Transform>(emitter.game_object_id).front().get(); // Emit particles based on emission_rate - int updates = calculate_update(this->update_count, emitter.data.emission_rate); - for (size_t i = 0; i < updates; i++) { - emit_particle(emitter, transform); + emitter.spawn_accumulator += emitter.data.emission_rate * dt; + while (emitter.spawn_accumulator >= 1.0) { + this->emit_particle(emitter, transform); + emitter.spawn_accumulator -= 1.0; } // Update all particles - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { if (particle.active) { - particle.update(); + particle.update(dt); } } // Check if within boundary - check_bounds(emitter, transform); + this->check_bounds(emitter, transform); } - - this->update_count = (this->update_count + 1) % this->MAX_UPDATE_COUNT; } void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & transform) { constexpr float DEG_TO_RAD = M_PI / 180.0; - vec2 initial_position = emitter.data.position + transform.position; - float random_angle = generate_random_angle(emitter.data.min_angle, emitter.data.max_angle); + vec2 initial_position = emitter.data.offset + transform.position; + float random_angle + = this->generate_random_angle(emitter.data.min_angle, emitter.data.max_angle); - float random_speed = generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); + float random_speed + = this->generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); float angle_radians = random_angle * DEG_TO_RAD; vec2 velocity = {random_speed * std::cos(angle_radians), random_speed * std::sin(angle_radians)}; - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { if (!particle.active) { particle.reset(emitter.data.end_lifespan, initial_position, velocity, random_angle); @@ -61,66 +68,54 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & } } -int ParticleSystem::calculate_update(int count, double emission) const { - double integer_part = std::floor(emission); - double fractional_part = emission - integer_part; - - if (fractional_part > 0) { - int denominator = static_cast<int>(1.0 / fractional_part); - return (count % denominator == 0) ? 1 : 0; - } - - return static_cast<int>(emission); -} - void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform & transform) { - vec2 offset = emitter.data.boundary.offset + transform.position + emitter.data.position; - double half_width = emitter.data.boundary.width / 2.0; - double half_height = emitter.data.boundary.height / 2.0; + vec2 offset = emitter.data.boundary.offset + transform.position + emitter.data.offset; + float half_width = emitter.data.boundary.width / 2.0; + float half_height = emitter.data.boundary.height / 2.0; - const double LEFT = offset.x - half_width; - const double RIGHT = offset.x + half_width; - const double TOP = offset.y - half_height; - const double BOTTOM = offset.y + half_height; + float left = offset.x - half_width; + float right = offset.x + half_width; + float top = offset.y - half_height; + float bottom = offset.y + half_height; - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { const vec2 & position = particle.position; - bool within_bounds = (position.x >= LEFT && position.x <= RIGHT && position.y >= TOP - && position.y <= BOTTOM); - + bool within_bounds = (position.x >= left && position.x <= right && position.y >= top + && position.y <= bottom); + //if not within bounds do a reset or stop velocity if (!within_bounds) { if (emitter.data.boundary.reset_on_exit) { particle.active = false; } else { particle.velocity = {0, 0}; - if (position.x < LEFT) particle.position.x = LEFT; - else if (position.x > RIGHT) particle.position.x = RIGHT; - if (position.y < TOP) particle.position.y = TOP; - else if (position.y > BOTTOM) particle.position.y = BOTTOM; + if (position.x < left) particle.position.x = left; + else if (position.x > right) particle.position.x = right; + if (position.y < top) particle.position.y = top; + else if (position.y > bottom) particle.position.y = bottom; } } } } -double ParticleSystem::generate_random_angle(double min_angle, double max_angle) const { +float ParticleSystem::generate_random_angle(float min_angle, float max_angle) const { if (min_angle == max_angle) { return min_angle; } else if (min_angle < max_angle) { return min_angle - + static_cast<double>(std::rand() % static_cast<int>(max_angle - min_angle)); + + static_cast<float>(std::rand() % static_cast<int>(max_angle - min_angle)); } else { - double angle_offset = (360 - min_angle) + max_angle; - double random_angle - = min_angle + static_cast<double>(std::rand() % static_cast<int>(angle_offset)); + float angle_offset = (360 - min_angle) + max_angle; + float random_angle + = min_angle + static_cast<float>(std::rand() % static_cast<int>(angle_offset)); return (random_angle >= 360) ? random_angle - 360 : random_angle; } } -double ParticleSystem::generate_random_speed(double min_speed, double max_speed) const { +float ParticleSystem::generate_random_speed(float min_speed, float max_speed) const { if (min_speed == max_speed) { return min_speed; } else { return min_speed - + static_cast<double>(std::rand() % static_cast<int>(max_speed - min_speed)); + + static_cast<float>(std::rand() % static_cast<int>(max_speed - min_speed)); } } diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h index 068f01c..154521d 100644 --- a/src/crepe/system/ParticleSystem.h +++ b/src/crepe/system/ParticleSystem.h @@ -32,16 +32,6 @@ private: void emit_particle(ParticleEmitter & emitter, const Transform & transform); /** - * \brief Calculates the number of times particles should be emitted based on emission rate - * and update count. - * - * \param count Current update count. - * \param emission Emission rate. - * \return The number of particles to emit. - */ - int calculate_update(int count, double emission) const; - - /** * \brief Checks whether particles are within the emitter’s boundary, resets or stops * particles if they exit. * @@ -57,7 +47,7 @@ private: * \param max_angle Maximum emission angle in degrees. * \return Random angle in degrees. */ - double generate_random_angle(double min_angle, double max_angle) const; + float generate_random_angle(float min_angle, float max_angle) const; /** * \brief Generates a random speed for particle emission within the specified range. @@ -66,15 +56,7 @@ private: * \param max_speed Maximum emission speed. * \return Random speed. */ - double generate_random_speed(double min_speed, double max_speed) const; - -private: - //! Counter to count updates to determine how many times emit_particle is - // called. - unsigned int update_count = 0; - //! Determines the lowest amount of emission rate (1000 = 0.001 = 1 particle per 1000 - // updates). - static constexpr unsigned int MAX_UPDATE_COUNT = 100; + float generate_random_speed(float min_speed, float max_speed) const; }; } // namespace crepe diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index afd9548..505433a 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -83,11 +83,11 @@ bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) bool rendering_particles = false; for (const ParticleEmitter & em : emitters) { - if (&em.data.sprite != &sprite) continue; + if (&em.sprite != &sprite) continue; rendering_particles = true; if (!em.active) continue; - for (const Particle & p : em.data.particles) { + for (const Particle & p : em.particles) { if (!p.active) continue; ctx.draw(SDLContext::RenderContext{ |