diff options
Diffstat (limited to 'src')
26 files changed, 559 insertions, 601 deletions
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 118c7ce..35161e4 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -14,10 +14,7 @@ target_sources(crepe PUBLIC Animator.cpp BoxCollider.cpp CircleCollider.cpp - IKeyListener.cpp - IMouseListener.cpp LoopManager.cpp - LoopTimer.cpp Asset.cpp EventHandler.cpp Script.cpp @@ -49,10 +46,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES EventHandler.h EventHandler.hpp Event.h - IKeyListener.h - IMouseListener.h LoopManager.h - LoopTimer.h Asset.h Button.h UIObject.h diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp deleted file mode 100644 index 8642655..0000000 --- a/src/crepe/api/IKeyListener.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "IKeyListener.h" - -using namespace crepe; - -// Constructor with specified channel -IKeyListener::IKeyListener(event_channel_t channel) - : event_manager(EventManager::get_instance()) { - this->press_id = event_manager.subscribe<KeyPressEvent>( - [this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel); - this->release_id = event_manager.subscribe<KeyReleaseEvent>( - [this](const KeyReleaseEvent & event) { return this->on_key_released(event); }, - channel); -} - -// Destructor, unsubscribe events -IKeyListener::~IKeyListener() { - event_manager.unsubscribe(this->press_id); - event_manager.unsubscribe(this->release_id); -} diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h deleted file mode 100644 index 180a0a6..0000000 --- a/src/crepe/api/IKeyListener.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "../manager/EventManager.h" - -#include "Event.h" -#include "EventHandler.h" - -namespace crepe { - -/** - * \class IKeyListener - * \brief Interface for keyboard event handling in the application. - */ -class IKeyListener { -public: - /** - * \brief Constructs an IKeyListener with a specified channel. - * \param channel The channel ID for event handling. - */ - IKeyListener(event_channel_t channel = EventManager::CHANNEL_ALL); - virtual ~IKeyListener(); - IKeyListener(const IKeyListener &) = delete; - IKeyListener & operator=(const IKeyListener &) = delete; - IKeyListener & operator=(IKeyListener &&) = delete; - IKeyListener(IKeyListener &&) = delete; - - /** - * \brief Pure virtual function to handle key press events. - * \param event The key press event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_key_pressed(const KeyPressEvent & event) = 0; - - /** - * \brief Pure virtual function to handle key release events. - * \param event The key release event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_key_released(const KeyReleaseEvent & event) = 0; - -private: - //! Key press event id - subscription_t press_id = -1; - //! Key release event id - subscription_t release_id = -1; - //! EventManager reference - EventManager & event_manager; -}; - -} // namespace crepe diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp deleted file mode 100644 index 989aeb3..0000000 --- a/src/crepe/api/IMouseListener.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "IMouseListener.h" - -using namespace crepe; - -IMouseListener::IMouseListener(event_channel_t channel) - : event_manager(EventManager::get_instance()) { - this->click_id = event_manager.subscribe<MouseClickEvent>( - [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); }, - channel); - - this->press_id = event_manager.subscribe<MousePressEvent>( - [this](const MousePressEvent & event) { return this->on_mouse_pressed(event); }, - channel); - - this->release_id = event_manager.subscribe<MouseReleaseEvent>( - [this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); }, - channel); - - this->move_id = event_manager.subscribe<MouseMoveEvent>( - [this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }, channel); -} - -IMouseListener::~IMouseListener() { - // Unsubscribe event handlers - event_manager.unsubscribe(this->click_id); - event_manager.unsubscribe(this->press_id); - event_manager.unsubscribe(this->release_id); - event_manager.unsubscribe(this->move_id); -} diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h deleted file mode 100644 index e19897d..0000000 --- a/src/crepe/api/IMouseListener.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "../manager/EventManager.h" - -#include "Event.h" -#include "EventHandler.h" - -namespace crepe { - -/** - * \class IMouseListener - * \brief Interface for mouse event handling in the application. - */ -class IMouseListener { -public: - /** - * \brief Constructs an IMouseListener with a specified channel. - * \param channel The channel ID for event handling. - */ - IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL); - virtual ~IMouseListener(); - IMouseListener & operator=(const IMouseListener &) = delete; - IMouseListener(const IMouseListener &) = delete; - IMouseListener & operator=(const IMouseListener &&) = delete; - IMouseListener(IMouseListener &&) = delete; - - /** - * \brief Move assignment operator (deleted). - */ - IMouseListener & operator=(IMouseListener &&) = delete; - - /** - * \brief Handles a mouse click event. - * \param event The mouse click event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_clicked(const MouseClickEvent & event) = 0; - - /** - * \brief Handles a mouse press event. - * \param event The mouse press event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_pressed(const MousePressEvent & event) = 0; - - /** - * \brief Handles a mouse release event. - * \param event The mouse release event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_released(const MouseReleaseEvent & event) = 0; - - /** - * \brief Handles a mouse move event. - * \param event The mouse move event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0; - -private: - //! Mouse click event id - subscription_t click_id = -1; - //! Mouse press event id - subscription_t press_id = -1; - //! Mouse release event id - subscription_t release_id = -1; - //! Mouse move event id - subscription_t move_id = -1; - //! EventManager reference - EventManager & event_manager; -}; - -} //namespace crepe diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index 1f0ba72..4ca9928 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,3 +1,4 @@ +#include "../facade/SDLContext.h" #include "../system/AISystem.h" #include "../system/AnimatorSystem.h" #include "../system/AudioSystem.h" @@ -7,7 +8,9 @@ #include "../system/PhysicsSystem.h" #include "../system/RenderSystem.h" #include "../system/ScriptSystem.h" -#include "manager/EventManager.h" +#include "../manager/EventManager.h" +#include "../manager/LoopTimerManager.h" +#include "../util/Log.h" #include "LoopManager.h" @@ -22,62 +25,63 @@ LoopManager::LoopManager() { this->load_system<RenderSystem>(); this->load_system<ScriptSystem>(); this->load_system<InputSystem>(); + this->event_manager.subscribe<ShutDownEvent>( + [this](const ShutDownEvent & event) { return this->on_shutdown(event); }); this->load_system<AudioSystem>(); this->load_system<AISystem>(); } - -void LoopManager::process_input() { this->get_system<InputSystem>().update(); } - void LoopManager::start() { this->setup(); this->loop(); } -void LoopManager::set_running(bool running) { this->game_running = running; } -void LoopManager::fixed_update() { - // TODO: retrieve EventManager from direct member after singleton refactor - EventManager & ev = this->mediator.event_manager; - ev.dispatch_events(); - this->get_system<ScriptSystem>().update(); - this->get_system<AISystem>().update(); - this->get_system<PhysicsSystem>().update(); - this->get_system<CollisionSystem>().update(); - this->get_system<AudioSystem>().update(); +void LoopManager::setup() { + this->game_running = true; + this->loop_timer.start(); + this->scene_manager.load_next_scene(); } void LoopManager::loop() { - LoopTimer & timer = this->loop_timer; - timer.start(); + try { while (game_running) { - timer.update(); + this->loop_timer.update(); - while (timer.get_lag() >= timer.get_fixed_delta_time()) { - this->process_input(); + while (this->loop_timer.get_lag() >= this->loop_timer.get_fixed_delta_time()) { this->fixed_update(); - timer.advance_fixed_update(); + this->loop_timer.advance_fixed_elapsed_time(); } - this->update(); - this->render(); - - timer.enforce_frame_rate(); + this->frame_update(); + this->loop_timer.enforce_frame_rate(); + } + }catch(const exception & e){ + Log::logf(Log::Level::ERROR, "Exception caught in main loop: %s", e.what()); + this->event_manager.trigger_event<ShutDownEvent>(ShutDownEvent{}); } } -void LoopManager::setup() { - LoopTimer & timer = this->loop_timer; - this->game_running = true; - this->scene_manager.load_next_scene(); - timer.start(); - timer.set_fps(200); +// will be called at a fixed interval +void LoopManager::fixed_update() { + this->get_system<InputSystem>().update(); + this->event_manager.dispatch_events(); + this->get_system<ScriptSystem>().update(); + this->get_system<AISystem>().update(); + this->get_system<PhysicsSystem>().update(); + this->get_system<CollisionSystem>().update(); + this->get_system<AudioSystem>().update(); } -void LoopManager::render() { - if (!this->game_running) return; - +// will be called every frame +void LoopManager::frame_update() { + this->scene_manager.load_next_scene(); this->get_system<AnimatorSystem>().update(); + //render this->get_system<RenderSystem>().update(); } -void LoopManager::update() {} +bool LoopManager::on_shutdown(const ShutDownEvent & e) { + this->game_running = false; + // propagate to possible user ShutDownEvent listeners + return false; +} diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 700afe4..0110695 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -4,15 +4,17 @@ #include "../facade/SDLContext.h" #include "../manager/ComponentManager.h" +#include "../manager/EventManager.h" +#include "../manager/LoopTimerManager.h" +#include "../manager/Mediator.h" #include "../manager/ResourceManager.h" #include "../manager/SaveManager.h" #include "../manager/SceneManager.h" #include "../system/System.h" -#include "LoopTimer.h" +#include "api/Event.h" namespace crepe { - /** * \brief Main game loop manager * @@ -20,8 +22,14 @@ namespace crepe { */ class LoopManager { public: - void start(); LoopManager(); + /** + * \brief Start the gameloop + * + * This is the start of the engine where the setup is called and then the loop keeps running until the game stops running. + * The Game programmer needs to call this function to run the game. This should be done after creating and adding all scenes. + */ + void start(); /** * \brief Add a new concrete scene to the scene manager @@ -46,49 +54,21 @@ private: void loop(); /** - * \brief Function for handling input-related system calls. - * - * Processes user inputs from keyboard and mouse. - */ - void process_input(); - - /** * \brief Per-frame update. * * Updates the game state based on the elapsed time since the last frame. */ - void update(); - - /** - * \brief Late update which is called after update(). - * - * This function can be used for final adjustments before rendering. - */ - void late_update(); + virtual void frame_update(); /** * \brief Fixed update executed at a fixed rate. * * This function updates physics and game logic based on LoopTimer's fixed_delta_time. */ - void fixed_update(); - - /** - * \brief Set game running variable - * - * \param running running (false = game shutdown, true = game running) - */ - void set_running(bool running); - - /** - * \brief Function for executing render-related systems. - * - * Renders the current state of the game to the screen. - */ - void render(); + virtual void fixed_update(); + //! Indicates whether the game is running. bool game_running = false; - private: //! Global context Mediator mediator; @@ -97,6 +77,10 @@ private: ComponentManager component_manager{mediator}; //! Scene manager instance SceneManager scene_manager{mediator}; + //! LoopTimerManager instance + LoopTimerManager loop_timer{mediator}; + //! EventManager instance + EventManager event_manager{mediator}; //! Resource manager instance ResourceManager resource_manager{mediator}; //! Save manager instance @@ -104,15 +88,20 @@ private: //! SDL context \todo no more singletons! SDLContext & sdl_context = SDLContext::get_instance(); - //! Loop timer \todo no more singletons! - LoopTimer & loop_timer = LoopTimer::get_instance(); private: + + /** + * \brief Callback function for ShutDownEvent + * + * This function sets the game_running variable to false, stopping the gameloop and therefor quitting the game. + */ + bool on_shutdown(const ShutDownEvent & e); /** * \brief Collection of System instances * * This map holds System instances indexed by the system's class typeid. It is filled in the - * constructor of \c LoopManager using LoopManager::load_system. + * constructor of LoopManager using LoopManager::load_system. */ std::unordered_map<std::type_index, std::unique_ptr<System>> systems; /** diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp deleted file mode 100644 index 15a0e3a..0000000 --- a/src/crepe/api/LoopTimer.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include <chrono> - -#include "../facade/SDLContext.h" -#include "../util/Log.h" - -#include "LoopTimer.h" - -using namespace crepe; - -LoopTimer::LoopTimer() { dbg_trace(); } - -LoopTimer & LoopTimer::get_instance() { - static LoopTimer instance; - return instance; -} - -void LoopTimer::start() { - this->last_frame_time = std::chrono::steady_clock::now(); - this->elapsed_time = std::chrono::milliseconds(0); - this->elapsed_fixed_time = std::chrono::milliseconds(0); - this->delta_time = std::chrono::milliseconds(0); -} - -void LoopTimer::update() { - auto current_frame_time = std::chrono::steady_clock::now(); - // Convert to duration in seconds for delta time - this->delta_time = std::chrono::duration_cast<std::chrono::duration<double>>( - current_frame_time - last_frame_time); - - if (this->delta_time > this->maximum_delta_time) { - this->delta_time = this->maximum_delta_time; - } - - this->delta_time *= this->game_scale; - this->elapsed_time += this->delta_time; - this->last_frame_time = current_frame_time; -} - -double LoopTimer::get_delta_time() const { return this->delta_time.count(); } - -double LoopTimer::get_current_time() const { return this->elapsed_time.count(); } - -void LoopTimer::advance_fixed_update() { this->elapsed_fixed_time += this->fixed_delta_time; } - -double LoopTimer::get_fixed_delta_time() const { return this->fixed_delta_time.count(); } - -void LoopTimer::set_fps(int fps) { - this->fps = fps; - // target time per frame in seconds - this->frame_target_time = std::chrono::duration<double>(1.0) / fps; -} - -int LoopTimer::get_fps() const { return this->fps; } - -void LoopTimer::set_game_scale(double value) { this->game_scale = value; } - -double LoopTimer::get_game_scale() const { return this->game_scale; } -void LoopTimer::enforce_frame_rate() { - std::chrono::steady_clock::time_point current_frame_time - = std::chrono::steady_clock::now(); - std::chrono::milliseconds frame_duration - = std::chrono::duration_cast<std::chrono::milliseconds>(current_frame_time - - this->last_frame_time); - - if (frame_duration < this->frame_target_time) { - std::chrono::milliseconds delay_time - = std::chrono::duration_cast<std::chrono::milliseconds>(this->frame_target_time - - frame_duration); - if (delay_time.count() > 0) { - SDLContext::get_instance().delay(delay_time.count()); - } - } - - this->last_frame_time = current_frame_time; -} - -double LoopTimer::get_lag() const { - return (this->elapsed_time - this->elapsed_fixed_time).count(); -} diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h deleted file mode 100644 index 9393439..0000000 --- a/src/crepe/api/LoopTimer.h +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -#include <chrono> - -namespace crepe { - -class LoopTimer { -public: - /** - * \brief Get the singleton instance of LoopTimer. - * - * \return A reference to the LoopTimer instance. - */ - static LoopTimer & get_instance(); - - /** - * \brief Get the current delta time for the current frame. - * - * \return Delta time in seconds since the last frame. - */ - double get_delta_time() const; - - /** - * \brief Get the current game time. - * - * \note The current game time may vary from real-world elapsed time. It is the cumulative - * sum of each frame's delta time. - * - * \return Elapsed game time in seconds. - */ - double get_current_time() const; - - /** - * \brief Set the target frames per second (FPS). - * - * \param fps The desired frames rendered per second. - */ - void set_fps(int fps); - - /** - * \brief Get the current frames per second (FPS). - * - * \return Current FPS. - */ - int get_fps() const; - - /** - * \brief Get the current game scale. - * - * \return The current game scale, where 0 = paused, 1 = normal speed, and values > 1 speed - * up the game. - */ - double get_game_scale() const; - - /** - * \brief Set the game scale. - * - * \param game_scale The desired game scale (0 = pause, 1 = normal speed, > 1 = speed up). - */ - void set_game_scale(double game_scale); - -private: - friend class LoopManager; - - /** - * \brief Start the loop timer. - * - * Initializes the timer to begin tracking frame times. - */ - void start(); - - /** - * \brief Enforce the frame rate limit. - * - * Ensures that the game loop does not exceed the target FPS by delaying frame updates as - * necessary. - */ - void enforce_frame_rate(); - - /** - * \brief Get the fixed delta time for consistent updates. - * - * Fixed delta time is used for operations that require uniform time steps, such as physics - * calculations. - * - * \return Fixed delta time in seconds. - */ - double get_fixed_delta_time() const; - - /** - * \brief Get the accumulated lag in the game loop. - * - * Lag represents the difference between the target frame time and the actual frame time, - * useful for managing fixed update intervals. - * - * \return Accumulated lag in seconds. - */ - double get_lag() const; - - /** - * \brief Construct a new LoopTimer object. - * - * Private constructor for singleton pattern to restrict instantiation outside the class. - */ - LoopTimer(); - - /** - * \brief Update the timer to the current frame. - * - * Calculates and updates the delta time for the current frame and adds it to the cumulative - * game time. - */ - void update(); - - /** - * \brief Advance the game loop by a fixed update interval. - * - * This method progresses the game state by a consistent, fixed time step, allowing for - * stable updates independent of frame rate fluctuations. - */ - void advance_fixed_update(); - -private: - //! Current frames per second - int fps = 50; - //! Current game scale - double game_scale = 1; - //! Maximum delta time in seconds to avoid large jumps - std::chrono::duration<double> maximum_delta_time{0.25}; - //! Delta time for the current frame in seconds - std::chrono::duration<double> delta_time{0.0}; - //! Target time per frame in seconds - std::chrono::duration<double> frame_target_time = std::chrono::duration<double>(1.0) / fps; - //! Fixed delta time for fixed updates in seconds - std::chrono::duration<double> fixed_delta_time = std::chrono::duration<double>(1.0) / 50.0; - //! Total elapsed game time in seconds - std::chrono::duration<double> elapsed_time{0.0}; - //! Total elapsed time for fixed updates in seconds - std::chrono::duration<double> elapsed_fixed_time{0.0}; - //! Time of the last frame - std::chrono::steady_clock::time_point last_frame_time; -}; - -} // namespace crepe diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 6becf60..abdfed0 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -340,8 +340,6 @@ SDLContext::CameraValues SDLContext::set_camera(const Camera & cam) { return ret_cam; } -uint64_t SDLContext::get_ticks() const { return SDL_GetTicks64(); } - std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> SDLContext::texture_from_path(const std::string & path) { @@ -372,8 +370,6 @@ ivec2 SDLContext::get_size(const Texture & ctx) { return size; } -void SDLContext::delay(int ms) const { SDL_Delay(ms); } - std::vector<SDLContext::EventData> SDLContext::get_events() { std::vector<SDLContext::EventData> event_list; SDL_Event event; diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index e232511..31646a6 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -140,24 +140,6 @@ private: MouseButton sdl_to_mousebutton(Uint8 sdl_button); private: - //! Will only use delay - friend class LoopTimer; - /** - * \brief Gets the current SDL ticks since the program started. - * \return Current ticks in milliseconds as a constant uint64_t. - */ - uint64_t get_ticks() const; - /** - * \brief Pauses the execution for a specified duration. - * - * This function uses SDL's delay function to halt the program execution for a given number - * of milliseconds, allowing for frame rate control or other timing-related functionality. - * - * \param ms Duration of the delay in milliseconds. - */ - void delay(int ms) const; - -private: /** * \brief Constructs an SDLContext instance. * Initializes SDL, creates a window and renderer. diff --git a/src/crepe/manager/CMakeLists.txt b/src/crepe/manager/CMakeLists.txt index 480c8ee..f73e165 100644 --- a/src/crepe/manager/CMakeLists.txt +++ b/src/crepe/manager/CMakeLists.txt @@ -4,6 +4,7 @@ target_sources(crepe PUBLIC Manager.cpp SaveManager.cpp SceneManager.cpp + LoopTimerManager.cpp ResourceManager.cpp ) @@ -17,6 +18,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES SaveManager.h SceneManager.h SceneManager.hpp + LoopTimerManager.h ResourceManager.h ResourceManager.hpp ) diff --git a/src/crepe/manager/EventManager.cpp b/src/crepe/manager/EventManager.cpp index 20f0dd3..6aa49ee 100644 --- a/src/crepe/manager/EventManager.cpp +++ b/src/crepe/manager/EventManager.cpp @@ -3,11 +3,9 @@ using namespace crepe; using namespace std; -EventManager & EventManager::get_instance() { - static EventManager instance; - return instance; +EventManager::EventManager(Mediator & mediator) : Manager(mediator) { + this->mediator.event_manager = *this; } - void EventManager::dispatch_events() { for (auto & event : this->events_queue) { this->handle_event(event.type, event.channel, *event.event.get()); diff --git a/src/crepe/manager/EventManager.h b/src/crepe/manager/EventManager.h index ba5e98b..639e37f 100644 --- a/src/crepe/manager/EventManager.h +++ b/src/crepe/manager/EventManager.h @@ -8,6 +8,8 @@ #include "../api/Event.h" #include "../api/EventHandler.h" +#include "Manager.h" + namespace crepe { //! Event listener unique ID @@ -22,27 +24,19 @@ typedef size_t subscription_t; typedef size_t event_channel_t; /** - * \class EventManager * \brief Manages event subscriptions, triggers, and queues, enabling decoupled event handling. * * The `EventManager` acts as a centralized event system. It allows for registering callbacks * for specific event types, triggering events synchronously, queueing events for later * processing, and managing subscriptions via unique identifiers. */ -class EventManager { +class EventManager : public Manager { public: static constexpr const event_channel_t CHANNEL_ALL = -1; - /** - * \brief Get the singleton instance of the EventManager. - * - * This method returns the unique instance of the EventManager, creating it if it - * doesn't already exist. Ensures only one instance is active in the program. - * - * \return Reference to the singleton instance of the EventManager. + * \param mediator A reference to a Mediator object used for transfering managers. */ - static EventManager & get_instance(); - + EventManager(Mediator & mediator); /** * \brief Subscribe to a specific event type. * @@ -108,13 +102,6 @@ public: private: /** - * \brief Default constructor for the EventManager. - * - * Constructor is private to enforce the singleton pattern. - */ - EventManager() = default; - - /** * \struct QueueEntry * \brief Represents an entry in the event queue. */ diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp new file mode 100644 index 0000000..a306eb7 --- /dev/null +++ b/src/crepe/manager/LoopTimerManager.cpp @@ -0,0 +1,94 @@ +#include <chrono> +#include <thread> + +#include "../util/Log.h" + +#include "LoopTimerManager.h" + +using namespace crepe; +using namespace std::chrono_literals; +LoopTimerManager::LoopTimerManager(Mediator & mediator) : Manager(mediator) { + this->mediator.loop_timer = *this; + dbg_trace(); +} + +void LoopTimerManager::start() { + this->last_frame_time = std::chrono::steady_clock::now(); + + this->elapsed_time = 0s; + // by starting the elapsed_fixed_time at (0 - fixed_delta_time) in milliseconds it calls a fixed update at the start of the loop. + this->elapsed_fixed_time + = -std::chrono::duration_cast<ElapsedTime_t>(this->fixed_delta_time); + this->delta_time = 0s; +} + +void LoopTimerManager::update() { + TimePoint_t current_frame_time + = std::chrono::steady_clock::now(); + // Convert to duration in seconds for delta time + this->delta_time = current_frame_time - last_frame_time; + + if (this->delta_time > this->maximum_delta_time) { + this->delta_time = this->maximum_delta_time; + } + if (this->delta_time > 0s) { + this->actual_fps = 1.0 / this->delta_time.count(); + } else { + this->actual_fps = 0; + } + this->elapsed_time += std::chrono::duration_cast<ElapsedTime_t>(this->delta_time); + this->last_frame_time = current_frame_time; +} + +Duration_t LoopTimerManager::get_delta_time() const {return this->delta_time * this->time_scale;} + +ElapsedTime_t LoopTimerManager::get_elapsed_time() const { return this->elapsed_time; } + +void LoopTimerManager::advance_fixed_elapsed_time() { + this->elapsed_fixed_time += std::chrono::duration_cast<ElapsedTime_t>(this->fixed_delta_time); +} + +void LoopTimerManager::set_target_framerate(unsigned fps) { + this->target_fps = fps; + //check if fps is lower or equals 0 + if (fps <= 0) return; + // target time per frame in seconds + this->frame_target_time = Duration_t(1s) / this->target_fps; +} + +unsigned LoopTimerManager::get_fps() const { return this->actual_fps; } + +void LoopTimerManager::set_time_scale(double value) { this->time_scale = value; } + +float LoopTimerManager::get_time_scale() const { return this->time_scale; } + +void LoopTimerManager::enforce_frame_rate() { + TimePoint_t current_frame_time + = std::chrono::steady_clock::now(); + Duration_t frame_duration = current_frame_time - this->last_frame_time; + // Check if frame duration is less than the target frame time + if (frame_duration < this->frame_target_time) { + std::chrono::microseconds delay_time + = std::chrono::duration_cast<std::chrono::microseconds>(this->frame_target_time + - frame_duration); + + if (delay_time.count() > 0) { + std::this_thread::sleep_for(delay_time); + } + } +} + +Duration_t LoopTimerManager::get_lag() const { + return (this->elapsed_time - this->elapsed_fixed_time); +} + +Duration_t LoopTimerManager::get_scaled_fixed_delta_time() const { + return this->fixed_delta_time * this->time_scale; +} +void LoopTimerManager::set_fixed_delta_time(float seconds) { + this->fixed_delta_time = Duration_t(seconds); +} + +Duration_t LoopTimerManager::get_fixed_delta_time() const { + return this->fixed_delta_time; +} diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h new file mode 100644 index 0000000..c5f3cb0 --- /dev/null +++ b/src/crepe/manager/LoopTimerManager.h @@ -0,0 +1,173 @@ +#pragma once + +#include <chrono> + +#include "Manager.h" + +namespace crepe { +typedef std::chrono::duration<float> Duration_t; +typedef std::chrono::duration<unsigned long long, std::micro> ElapsedTime_t; +typedef std::chrono::steady_clock::time_point TimePoint_t; +/** + * \brief Manages timing and frame rate for the game loop. + * + * The LoopTimerManager class is responsible for calculating and managing timing functions + * such as delta time, frames per second (FPS), fixed time steps, and time scaling. It ensures + * consistent frame updates and supports game loop operations, such as handling fixed updates + * for physics and other time-sensitive operations. + */ +class LoopTimerManager : public Manager { +public: + /** + * \param mediator A reference to a Mediator object used for transfering managers. + */ + LoopTimerManager(Mediator & mediator); + /** + * \brief Get the current delta time for the current frame. + * + * This value represents the estimated frame duration of the current frame. + * This value can be used in the frame_update to convert pixel based values to time based values. + * + * \return Delta time in seconds since the last frame. + */ + Duration_t get_delta_time() const; + + /** + * \brief Get the current elapsed time (total time passed ) + * + * \note The current game time may vary from real-world elapsed time. It is the cumulative + * sum of each frame's delta time. + * + * \return Elapsed game time in seconds. + */ + ElapsedTime_t get_elapsed_time() const; + + /** + * \brief Set the target frames per second (FPS). + * + * \param fps The desired frames rendered per second. + */ + void set_target_framerate(unsigned fps); + + /** + * \brief Get the current frames per second (FPS). + * + * \return Current FPS. + */ + unsigned get_fps() const; + + /** + * \brief Get the current time scale. + * + * \return The current time scale, where (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up). + * up the game. + */ + float get_time_scale() const; + + /** + * \brief Set the time scale. + * + * time_scale is a value that changes the delta time that can be retrieved using get_delta_time function. + * + * \param time_scale The desired time scale (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up). + */ + void set_time_scale(double time_scale); + + /** + * \brief Get the fixed delta time in seconds without scaling by the time scale. + * + * This value is used in the LoopManager to determine how many times + * the fixed_update should be called within a given interval. + * + * \return The unscaled fixed delta time in seconds. + */ + Duration_t get_fixed_delta_time() const; + + /** + * \brief Set the fixed_delta_time in seconds. + * + * \param seconds fixed_delta_time in seconds. + * + * The fixed_delta_time value is used to determine how many times per second the fixed_update and process_input functions are called. + * + */ + void set_fixed_delta_time(float seconds); + + /** + * \brief Retrieves the scaled fixed delta time in seconds. + * + * The scaled fixed delta time is the timing value used within the `fixed_update` function. + * It is adjusted by the time_scale to account for any changes in the simulation's + * speed. + * + * \return The fixed delta time, scaled by the current time scale, in seconds. + */ + Duration_t get_scaled_fixed_delta_time() const; + +private: + //! Friend relation to use start,enforce_frame_rate,get_lag,update,advance_fixed_update. + friend class LoopManager; + /** + * \brief Start the loop timer. + * + * Initializes the timer to begin tracking frame times. + */ + void start(); + /** + * \brief Enforce the frame rate limit. + * + * Ensures that the game loop does not exceed the target FPS by delaying frame updates as + * necessary. + */ + void enforce_frame_rate(); + /** + * \brief Get the accumulated lag in the game loop. + * + * Lag represents the difference between the target frame time and the actual frame time, + * useful for managing fixed update intervals. + * + * \return Accumulated lag in seconds. + */ + Duration_t get_lag() const; + + /** + * \brief Update the timer to the current frame. + * + * Calculates and updates the delta time for the current frame and adds it to the cumulative + * game time. + */ + void update(); + + /** + * \brief Progress the elapsed fixed time by the fixed delta time interval. + * + * This method advances the game's fixed update loop by adding the fixed_delta_time + * to elapsed_fixed_time, ensuring the fixed update catches up with the elapsed time. + */ + void advance_fixed_elapsed_time(); + +private: + //! Target frames per second. + int target_fps = 60; + //! Actual frames per second. + int actual_fps = 0; + //! Time scale for speeding up or slowing down the game (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up). + float time_scale = 1; + //! Maximum delta time in seconds to avoid large jumps. + Duration_t maximum_delta_time{0.25}; + //! Delta time for the current frame in seconds. + Duration_t delta_time{0.0}; + //! Target time per frame in seconds + Duration_t frame_target_time + = Duration_t(1.0) / target_fps; + //! Fixed delta time for fixed updates in seconds. + Duration_t fixed_delta_time = Duration_t(1.0) / 50.0; + //! Total elapsed game time in microseconds. + ElapsedTime_t elapsed_time{0}; + //! Total elapsed time for fixed updates in microseconds. + ElapsedTime_t elapsed_fixed_time{0}; + //! Time of the last frame. + TimePoint_t last_frame_time; +}; + +} // namespace crepe diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index 35ac181..dfb37dc 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -4,8 +4,8 @@ // TODO: remove these singletons: #include "../facade/SDLContext.h" -#include "EventManager.h" -#include "api/LoopTimer.h" + +#include "SaveManager.h" namespace crepe { @@ -14,6 +14,8 @@ class SceneManager; class SaveManager; class ResourceManager; +class LoopTimerManager; +class EventManager; /** * Struct to pass references to classes that would otherwise need to be singletons down to * other classes within the engine hierarchy. Made to prevent constant changes to subclasses to @@ -29,11 +31,11 @@ class ResourceManager; struct Mediator { OptionalRef<ComponentManager> component_manager; OptionalRef<SceneManager> scene_manager; + OptionalRef<EventManager> event_manager; + OptionalRef<LoopTimerManager> loop_timer; OptionalRef<SaveManager> save_manager; - OptionalRef<EventManager> event_manager = EventManager::get_instance(); OptionalRef<ResourceManager> resource_manager; OptionalRef<SDLContext> sdl_context = SDLContext::get_instance(); - OptionalRef<LoopTimer> timer = LoopTimer::get_instance(); }; } // namespace crepe diff --git a/src/crepe/manager/SceneManager.cpp b/src/crepe/manager/SceneManager.cpp index 50a9fbb..d4ca90b 100644 --- a/src/crepe/manager/SceneManager.cpp +++ b/src/crepe/manager/SceneManager.cpp @@ -32,4 +32,7 @@ void SceneManager::load_next_scene() { // Load the new scene scene->load_scene(); + + //clear the next scene + next_scene.clear(); } diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp index 549c35d..690b45b 100644 --- a/src/crepe/system/AnimatorSystem.cpp +++ b/src/crepe/system/AnimatorSystem.cpp @@ -2,7 +2,7 @@ #include "../api/Animator.h" #include "../manager/ComponentManager.h" -#include "api/LoopTimer.h" +#include "../manager/LoopTimerManager.h" #include "AnimatorSystem.h" @@ -10,10 +10,10 @@ using namespace crepe; void AnimatorSystem::update() { ComponentManager & mgr = this->mediator.component_manager; - LoopTimer & timer = this->mediator.timer; + LoopTimerManager & timer = this->mediator.loop_timer; RefVector<Animator> animations = mgr.get_components_by_type<Animator>(); - double elapsed_time = timer.get_current_time(); + unsigned long long elapsed_time = timer.get_elapsed_time().count(); for (Animator & a : animations) { if (!a.active) continue; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 7196404..11b4ca9 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -15,6 +15,8 @@ target_sources(test_main PUBLIC ValueBrokerTest.cpp DBTest.cpp Vector2Test.cpp + LoopManagerTest.cpp + LoopTimerTest.cpp InputTest.cpp ScriptEventTest.cpp ScriptSceneTest.cpp diff --git a/src/test/CollisionTest.cpp b/src/test/CollisionTest.cpp index dd45eb6..5dbc670 100644 --- a/src/test/CollisionTest.cpp +++ b/src/test/CollisionTest.cpp @@ -50,6 +50,7 @@ public: class CollisionTest : public Test { public: Mediator m; + EventManager event_mgr{m}; ComponentManager mgr{m}; CollisionSystem collision_sys{m}; ScriptSystem script_sys{m}; diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index 4a4872d..82272b5 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -1,56 +1,41 @@ -#include <gmock/gmock.h> -#include <gtest/gtest.h> - #include <crepe/api/Event.h> -#include <crepe/api/IKeyListener.h> -#include <crepe/api/IMouseListener.h> #include <crepe/manager/EventManager.h> - +#include <crepe/manager/Mediator.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> using namespace std; using namespace std::chrono_literals; using namespace crepe; class EventManagerTest : public ::testing::Test { protected: + Mediator mediator; + EventManager event_mgr{mediator}; void SetUp() override { // Clear any existing subscriptions or events before each test - EventManager::get_instance().clear(); + event_mgr.clear(); } void TearDown() override { // Ensure cleanup after each test - EventManager::get_instance().clear(); + event_mgr.clear(); } }; -class MockKeyListener : public IKeyListener { -public: - MOCK_METHOD(bool, on_key_pressed, (const KeyPressEvent & event), (override)); - MOCK_METHOD(bool, on_key_released, (const KeyReleaseEvent & event), (override)); -}; - -class MockMouseListener : public IMouseListener { -public: - MOCK_METHOD(bool, on_mouse_clicked, (const MouseClickEvent & event), (override)); - MOCK_METHOD(bool, on_mouse_pressed, (const MousePressEvent & event), (override)); - MOCK_METHOD(bool, on_mouse_released, (const MouseReleaseEvent & event), (override)); - MOCK_METHOD(bool, on_mouse_moved, (const MouseMoveEvent & event), (override)); -}; - TEST_F(EventManagerTest, EventSubscription) { EventHandler<KeyPressEvent> key_handler = [](const KeyPressEvent & e) { return true; }; // Subscribe to KeyPressEvent - EventManager::get_instance().subscribe<KeyPressEvent>(key_handler, 1); + event_mgr.subscribe<KeyPressEvent>(key_handler, 1); // Verify subscription (not directly verifiable; test by triggering event) - EventManager::get_instance().trigger_event<KeyPressEvent>( + event_mgr.trigger_event<KeyPressEvent>( KeyPressEvent{ .repeat = true, .key = Keycode::A, }, 1); - EventManager::get_instance().trigger_event<KeyPressEvent>( + event_mgr.trigger_event<KeyPressEvent>( KeyPressEvent{ .repeat = true, .key = Keycode::A, @@ -68,13 +53,11 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; - EventManager::get_instance().subscribe<MouseClickEvent>(mouse_handler, - EventManager::CHANNEL_ALL); + event_mgr.subscribe<MouseClickEvent>(mouse_handler, EventManager::CHANNEL_ALL); MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - EventManager::get_instance().trigger_event<MouseClickEvent>(click_event, - EventManager::CHANNEL_ALL); + event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); EXPECT_TRUE(triggered); } @@ -88,19 +71,17 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_one_channel) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; - EventManager::get_instance().subscribe<MouseClickEvent>(mouse_handler, test_channel); + event_mgr.subscribe<MouseClickEvent>(mouse_handler, test_channel); MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - EventManager::get_instance().trigger_event<MouseClickEvent>(click_event, - EventManager::CHANNEL_ALL); + event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); EXPECT_FALSE(triggered); - EventManager::get_instance().trigger_event<MouseClickEvent>(click_event, test_channel); + event_mgr.trigger_event<MouseClickEvent>(click_event, test_channel); } TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { - EventManager & event_manager = EventManager::get_instance(); // Flags to track handler calls bool triggered_true = false; @@ -126,11 +107,11 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { // Test event MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - event_manager.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL); - event_manager.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL); + event_mgr.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL); + event_mgr.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL); // Trigger event - event_manager.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); + event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); // Check that only the true handler was triggered EXPECT_TRUE(triggered_true); @@ -139,12 +120,12 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { // Reset and clear triggered_true = false; triggered_false = false; - event_manager.clear(); - event_manager.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL); - event_manager.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL); + event_mgr.clear(); + event_mgr.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL); + event_mgr.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL); // Trigger event again - event_manager.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); + event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); // Check that both handlers were triggered EXPECT_TRUE(triggered_true); @@ -152,47 +133,37 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { } TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) { - EventManager & event_manager = EventManager::get_instance(); bool triggered1 = false; bool triggered2 = false; int test_channel = 1; - - // Adjusted to use KeyPressEvent with repeat as the first variable - EventHandler<KeyPressEvent> key_handler1 = [&](const KeyPressEvent & e) { + EventHandler<MouseClickEvent> mouse_handler1 = [&](const MouseClickEvent & e) { triggered1 = true; - EXPECT_EQ(e.repeat, false); // Expecting repeat to be false - EXPECT_EQ(e.key, Keycode::A); // Adjust expected key code + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; - - EventHandler<KeyPressEvent> key_handler2 = [&](const KeyPressEvent & e) { + EventHandler<MouseClickEvent> mouse_handler2 = [&](const MouseClickEvent & e) { triggered2 = true; - EXPECT_EQ(e.repeat, false); // Expecting repeat to be false - EXPECT_EQ(e.key, Keycode::A); // Adjust expected key code + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; + event_mgr.subscribe<MouseClickEvent>(mouse_handler1); + event_mgr.subscribe<MouseClickEvent>(mouse_handler2, test_channel); - // Subscribe handlers to KeyPressEvent - event_manager.subscribe<KeyPressEvent>(key_handler1); - event_manager.subscribe<KeyPressEvent>(key_handler2, test_channel); - - // Queue a KeyPressEvent instead of KeyDownEvent - event_manager.queue_event<KeyPressEvent>(KeyPressEvent{ - .repeat = false, .key = Keycode::A}); // Adjust event with repeat flag first - - event_manager.queue_event<KeyPressEvent>( - KeyPressEvent{.repeat = false, - .key = Keycode::A}, // Adjust event for second subscription + event_mgr.queue_event<MouseClickEvent>( + MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); + event_mgr.queue_event<MouseClickEvent>( + MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}, test_channel); - - event_manager.dispatch_events(); - + event_mgr.dispatch_events(); EXPECT_TRUE(triggered1); EXPECT_TRUE(triggered2); } TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { - EventManager & event_manager = EventManager::get_instance(); // Flags to track if handlers are triggered bool triggered1 = false; @@ -215,15 +186,15 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { return false; // Allows propagation }; // Subscribe handlers - subscription_t handler1_id = event_manager.subscribe<MouseClickEvent>(mouse_handler1); - subscription_t handler2_id = event_manager.subscribe<MouseClickEvent>(mouse_handler2); + subscription_t handler1_id = event_mgr.subscribe<MouseClickEvent>(mouse_handler1); + subscription_t handler2_id = event_mgr.subscribe<MouseClickEvent>(mouse_handler2); // Queue events - event_manager.queue_event<MouseClickEvent>( + event_mgr.queue_event<MouseClickEvent>( MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); // Dispatch events - both handlers should be triggered - event_manager.dispatch_events(); + event_mgr.dispatch_events(); EXPECT_TRUE(triggered1); // Handler 1 should be triggered EXPECT_TRUE(triggered2); // Handler 2 should be triggered @@ -232,14 +203,14 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { triggered2 = false; // Unsubscribe handler1 - event_manager.unsubscribe(handler1_id); + event_mgr.unsubscribe(handler1_id); // Queue the same event again - event_manager.queue_event<MouseClickEvent>( + event_mgr.queue_event<MouseClickEvent>( MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); // Dispatch events - only handler 2 should be triggered, handler 1 should NOT - event_manager.dispatch_events(); + event_mgr.dispatch_events(); EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered EXPECT_TRUE(triggered2); // Handler 2 should be triggered @@ -247,14 +218,14 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { triggered2 = false; // Unsubscribe handler2 - event_manager.unsubscribe(handler2_id); + event_mgr.unsubscribe(handler2_id); // Queue the event again - event_manager.queue_event<MouseClickEvent>( + event_mgr.queue_event<MouseClickEvent>( MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); // Dispatch events - no handler should be triggered - event_manager.dispatch_events(); + event_mgr.dispatch_events(); EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered EXPECT_FALSE(triggered2); // Handler 2 should NOT be triggered } diff --git a/src/test/InputTest.cpp b/src/test/InputTest.cpp index 73eaab3..4826dc2 100644 --- a/src/test/InputTest.cpp +++ b/src/test/InputTest.cpp @@ -26,8 +26,7 @@ public: ComponentManager mgr{mediator}; InputSystem input_system{mediator}; - - EventManager & event_manager = EventManager::get_instance(); + EventManager event_manager{mediator}; //GameObject camera; protected: diff --git a/src/test/LoopManagerTest.cpp b/src/test/LoopManagerTest.cpp new file mode 100644 index 0000000..4e0ecdc --- /dev/null +++ b/src/test/LoopManagerTest.cpp @@ -0,0 +1,79 @@ +#include <chrono> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <thread> +#define private public +#define protected public +#include <crepe/api/LoopManager.h> +#include <crepe/manager/EventManager.h> +#include <crepe/manager/LoopTimerManager.h> +using namespace std::chrono; +using namespace crepe; + +class LoopManagerTest : public ::testing::Test { +protected: + class TestGameLoop : public crepe::LoopManager { + public: + MOCK_METHOD(void, fixed_update, (), (override)); + MOCK_METHOD(void, frame_update, (), (override)); + }; + + TestGameLoop test_loop; + void SetUp() override {} +}; + +TEST_F(LoopManagerTest, FixedUpdate) { + // Arrange + test_loop.loop_timer.set_target_framerate(60); + + // Set expectations for the mock calls + EXPECT_CALL(test_loop, frame_update).Times(::testing::Exactly(60)); + EXPECT_CALL(test_loop, fixed_update).Times(::testing::Exactly(50)); + + // Start the loop in a separate thread + std::thread loop_thread([&]() { test_loop.start(); }); + + // Let the loop run for exactly 1 second + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Stop the game loop + test_loop.game_running = false; + // Wait for the loop thread to finish + loop_thread.join(); + + // Test finished +} +TEST_F(LoopManagerTest, ScaledFixedUpdate) { + // Arrange + test_loop.loop_timer.set_target_framerate(60); + + // Set expectations for the mock calls + EXPECT_CALL(test_loop, frame_update).Times(::testing::Exactly(60)); + EXPECT_CALL(test_loop, fixed_update).Times(::testing::Exactly(50)); + + // Start the loop in a separate thread + std::thread loop_thread([&]() { test_loop.start(); }); + + // Let the loop run for exactly 1 second + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Stop the game loop + test_loop.game_running = false; + // Wait for the loop thread to finish + loop_thread.join(); + + // Test finished +} +TEST_F(LoopManagerTest, ShutDown) { + // Arrange + test_loop.loop_timer.set_target_framerate(60); + + EXPECT_CALL(test_loop, frame_update).Times(::testing::AtLeast(1)); + EXPECT_CALL(test_loop, fixed_update).Times(::testing::AtLeast(1)); + // Start the loop in a separate thread + std::thread loop_thread([&]() { test_loop.start(); }); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + test_loop.event_manager.trigger_event<ShutDownEvent>(ShutDownEvent{}); + // Wait for the loop thread to finish + loop_thread.join(); +} diff --git a/src/test/LoopTimerTest.cpp b/src/test/LoopTimerTest.cpp new file mode 100644 index 0000000..1216e5e --- /dev/null +++ b/src/test/LoopTimerTest.cpp @@ -0,0 +1,75 @@ +#include <chrono> +#include <gtest/gtest.h> +#include <thread> +#define private public +#define protected public +#include <crepe/manager/LoopTimerManager.h> +#include <crepe/manager/Mediator.h> +using namespace std::chrono; +using namespace crepe; + +class LoopTimerTest : public ::testing::Test { +protected: + Mediator mediator; + LoopTimerManager loop_timer{mediator}; + + void SetUp() override { loop_timer.start(); } +}; +TEST_F(LoopTimerTest, EnforcesTargetFrameRate) { + // Set the target FPS to 60 (which gives a target time per frame of ~16.67 ms) + loop_timer.set_target_framerate(60); + + auto start_time = steady_clock::now(); + loop_timer.enforce_frame_rate(); + + auto elapsed_time = steady_clock::now() - start_time; + auto elapsed_ms = duration_cast<milliseconds>(elapsed_time).count(); + + // For 60 FPS, the target frame time is around 16.67ms + ASSERT_NEAR(elapsed_ms, 16.7, 1); +} +TEST_F(LoopTimerTest, SetTargetFps) { + // Set the target FPS to 120 + loop_timer.set_target_framerate(120); + + // Calculate the expected frame time (~8.33ms per frame) + Duration_t expected_frame_time = std::chrono::duration<float>(1.0 / 120.0); + + ASSERT_NEAR(loop_timer.frame_target_time.count(), expected_frame_time.count(), 0.001); +} +TEST_F(LoopTimerTest, DeltaTimeCalculation) { + // Set the target FPS to 60 (16.67 ms per frame) + loop_timer.set_target_framerate(60); + + auto start_time = steady_clock::now(); + loop_timer.update(); + auto end_time = steady_clock::now(); + + // Check the delta time + Duration_t delta_time = loop_timer.get_delta_time(); + + auto elapsed_time = duration_cast<seconds>(end_time - start_time).count(); + + // Assert that delta_time is close to the elapsed time + ASSERT_NEAR(delta_time.count(), elapsed_time, 1); +} + +TEST_F(LoopTimerTest, getCurrentTime) { + // Set the target FPS to 60 (16.67 ms per frame) + loop_timer.set_target_framerate(60); + + auto start_time = steady_clock::now(); + + // Sleep + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + loop_timer.update(); + + auto end_time = steady_clock::now(); + + // Get the elapsed time in seconds as a double + auto elapsed_time = std::chrono::duration_cast<ElapsedTime_t>(end_time - start_time).count(); + + + ASSERT_NEAR(loop_timer.get_elapsed_time().count(), elapsed_time, 5); +} diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h index 309e016..31fa7c9 100644 --- a/src/test/ScriptTest.h +++ b/src/test/ScriptTest.h @@ -6,8 +6,8 @@ #include <crepe/api/BehaviorScript.h> #include <crepe/api/Script.h> #include <crepe/manager/ComponentManager.h> +#include <crepe/manager/EventManager.h> #include <crepe/system/ScriptSystem.h> - class ScriptTest : public testing::Test { protected: crepe::Mediator mediator; @@ -16,6 +16,7 @@ protected: public: crepe::ComponentManager component_manager{mediator}; crepe::ScriptSystem system{mediator}; + crepe::EventManager event_mgr{mediator}; crepe::GameObject entity = component_manager.new_object(OBJ_NAME); class MyScript : public crepe::Script { |