From 020cdfddcd06e162515deee4941ce91f3a945ee6 Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Sun, 24 Nov 2024 16:38:58 +0100 Subject: fps counter functionality --- src/crepe/api/LoopTimer.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/crepe/api/LoopTimer.h') diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h index 9393439..eea60bb 100644 --- a/src/crepe/api/LoopTimer.h +++ b/src/crepe/api/LoopTimer.h @@ -35,7 +35,7 @@ public: * * \param fps The desired frames rendered per second. */ - void set_fps(int fps); + void set_target_fps(int fps); /** * \brief Get the current frames per second (FPS). @@ -121,8 +121,10 @@ private: void advance_fixed_update(); private: - //! Current frames per second - int fps = 50; + //! Target frames per second + int target_fps = 50; + //! Actual frames per second + int actual_fps = 0; //! Current game scale double game_scale = 1; //! Maximum delta time in seconds to avoid large jumps @@ -130,7 +132,7 @@ private: //! Delta time for the current frame in seconds std::chrono::duration delta_time{0.0}; //! Target time per frame in seconds - std::chrono::duration frame_target_time = std::chrono::duration(1.0) / fps; + std::chrono::duration frame_target_time = std::chrono::duration(1.0) / target_fps; //! Fixed delta time for fixed updates in seconds std::chrono::duration fixed_delta_time = std::chrono::duration(1.0) / 50.0; //! Total elapsed game time in seconds @@ -139,6 +141,7 @@ private: std::chrono::duration elapsed_fixed_time{0.0}; //! Time of the last frame std::chrono::steady_clock::time_point last_frame_time; + }; } // namespace crepe -- cgit v1.2.3 From 9f4f2fa8eac190ccb407c3f911ac5978cb4c3e3a Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Sun, 24 Nov 2024 19:36:32 +0100 Subject: start of tests --- src/crepe/api/LoopTimer.h | 1 - src/test/gameLoopTest.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/test/gameLoopTest.cpp (limited to 'src/crepe/api/LoopTimer.h') diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h index eea60bb..0a48e20 100644 --- a/src/crepe/api/LoopTimer.h +++ b/src/crepe/api/LoopTimer.h @@ -68,7 +68,6 @@ private: * Initializes the timer to begin tracking frame times. */ void start(); - /** * \brief Enforce the frame rate limit. * diff --git a/src/test/gameLoopTest.cpp b/src/test/gameLoopTest.cpp new file mode 100644 index 0000000..af80b27 --- /dev/null +++ b/src/test/gameLoopTest.cpp @@ -0,0 +1,34 @@ +#define private public +#define protected public +#include "api/LoopManager.h" +#include "api/LoopTimer.h" +#include +#include + +using namespace std; +using namespace std::chrono_literals; +using namespace crepe; + +class GameLoopTest : public ::testing::Test { +public: +LoopManager loop_manager; +LoopTimer loop_timer = LoopTimer::get_instance(); +protected: + void SetUp() override { + loop_timer.start(); + loop_manager.start(); + } + + void TearDown() override { + + } +}; +TEST_F(GameLoopTest, TestDeltaTime) { + auto start_time = std::chrono::steady_clock::now(); + + loop_timer.update(); + double delta_time = loop_timer.get_delta_time(); + + auto elapsed_time = std::chrono::steady_clock::now() - start_time; + EXPECT_LE(delta_time, std::chrono::duration(elapsed_time).count()); // delta_time should match or be slightly less +} -- cgit v1.2.3 From c7c4cc0e3b1a3152256bc8ebf6494c19519538db Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Wed, 4 Dec 2024 10:26:32 +0100 Subject: looptimer no singleton --- src/crepe/api/LoopManager.cpp | 31 +++++++++++++++++++------------ src/crepe/api/LoopManager.h | 4 +++- src/crepe/api/LoopTimer.cpp | 37 ++++++++++++++++++------------------- src/crepe/api/LoopTimer.h | 15 ++------------- 4 files changed, 42 insertions(+), 45 deletions(-) (limited to 'src/crepe/api/LoopTimer.h') diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index 7edf4d1..4a6d2cd 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -6,20 +6,25 @@ #include "../system/PhysicsSystem.h" #include "../system/RenderSystem.h" #include "../system/ScriptSystem.h" - +#include "../api/EventManager.h" #include "LoopManager.h" #include "LoopTimer.h" - +#include using namespace crepe; using namespace std; LoopManager::LoopManager() { + this->loop_timer = make_unique(); this->load_system(); this->load_system(); this->load_system(); this->load_system(); this->load_system(); this->load_system(); + EventManager::get_instance().subscribe([this](const ShutDownEvent& event) { + return this->on_shutdown(event); + }); + } void LoopManager::process_input() { @@ -35,29 +40,27 @@ void LoopManager::set_running(bool running) { this->game_running = running; } void LoopManager::fixed_update() {} void LoopManager::loop() { - LoopTimer & timer = LoopTimer::get_instance(); - timer.start(); + this->loop_timer->start(); while (game_running) { - timer.update(); + this->loop_timer->update(); - while (timer.get_lag() >= timer.get_fixed_delta_time()) { + while (this->loop_timer->get_lag() >= this->loop_timer->get_fixed_delta_time()) { this->process_input(); this->fixed_update(); - timer.advance_fixed_update(); + this->loop_timer->advance_fixed_update(); } this->update(); this->render(); - - timer.enforce_frame_rate(); + this->loop_timer->enforce_frame_rate(); } } void LoopManager::setup() { this->game_running = true; - LoopTimer::get_instance().start(); - LoopTimer::get_instance().set_fps(200); + this->loop_timer->start(); + this->loop_timer->set_fps(60); } void LoopManager::render() { @@ -65,5 +68,9 @@ void LoopManager::render() { this->get_system().update(); } } +bool LoopManager::on_shutdown(const ShutDownEvent & e){ + this->game_running = false; + return false; +} -void LoopManager::update() { LoopTimer & timer = LoopTimer::get_instance(); } +void LoopManager::update() {} diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 13e6dac..ff1ff55 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -7,7 +7,7 @@ #include "api/SceneManager.h" namespace crepe { - +class LoopTimer; /** * \brief Main game loop manager * @@ -91,6 +91,8 @@ private: SceneManager scene_manager{component_manager}; private: + std::unique_ptr loop_timer; + bool on_shutdown(const ShutDownEvent & e); /** * \brief Collection of System instances * diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp index 15a0e3a..d0a19d7 100644 --- a/src/crepe/api/LoopTimer.cpp +++ b/src/crepe/api/LoopTimer.cpp @@ -9,10 +9,6 @@ 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(); @@ -56,22 +52,25 @@ 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(current_frame_time - - this->last_frame_time); - - if (frame_duration < this->frame_target_time) { - std::chrono::milliseconds delay_time - = std::chrono::duration_cast(this->frame_target_time - - frame_duration); - if (delay_time.count() > 0) { - SDLContext::get_instance().delay(delay_time.count()); - } - } + auto current_frame_time = std::chrono::steady_clock::now(); + auto frame_duration = current_frame_time - this->last_frame_time; - this->last_frame_time = current_frame_time; + if (frame_duration < this->frame_target_time) { + auto remaining_time = this->frame_target_time - frame_duration; + + // Sleep for most of the remaining time using SDLContext + if (remaining_time > std::chrono::microseconds(2000)) { // 2ms threshold + SDLContext::get_instance().delay( + std::chrono::duration_cast(remaining_time).count()); + } + + // Busy-wait for the last tiny remaining duration + while (std::chrono::steady_clock::now() - current_frame_time < remaining_time) { + // Busy wait + } + } + + this->last_frame_time = std::chrono::steady_clock::now(); // Update frame time } double LoopTimer::get_lag() const { diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h index 9393439..b20203d 100644 --- a/src/crepe/api/LoopTimer.h +++ b/src/crepe/api/LoopTimer.h @@ -6,13 +6,7 @@ namespace crepe { class LoopTimer { public: - /** - * \brief Get the singleton instance of LoopTimer. - * - * \return A reference to the LoopTimer instance. - */ - static LoopTimer & get_instance(); - + LoopTimer(); /** * \brief Get the current delta time for the current frame. * @@ -97,12 +91,7 @@ private: */ 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. -- cgit v1.2.3 From d78ba1aafe83b4d5cb64ea696089e7517691cd6f Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Thu, 5 Dec 2024 11:06:40 +0100 Subject: make format --- src/crepe/api/LoopManager.cpp | 12 +++--- src/crepe/api/LoopManager.h | 5 +-- src/crepe/api/LoopTimer.cpp | 27 +++++++------- src/crepe/api/LoopTimer.h | 6 +-- src/test/LoopManagerTest.cpp | 46 ++++++++++++----------- src/test/LoopTimerTest.cpp | 87 ++++++++++++++++++++----------------------- 6 files changed, 87 insertions(+), 96 deletions(-) (limited to 'src/crepe/api/LoopTimer.h') diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index 040cb93..5879d79 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,12 +1,12 @@ #include "../facade/SDLContext.h" +#include "../manager/EventManager.h" #include "../system/AnimatorSystem.h" #include "../system/CollisionSystem.h" #include "../system/ParticleSystem.h" #include "../system/PhysicsSystem.h" #include "../system/RenderSystem.h" #include "../system/ScriptSystem.h" -#include "../manager/EventManager.h" #include "LoopManager.h" @@ -20,9 +20,8 @@ LoopManager::LoopManager() { this->load_system(); this->load_system(); this->load_system(); - EventManager::get_instance().subscribe([this](const ShutDownEvent& event) { - return this->on_shutdown(event); - }); + EventManager::get_instance().subscribe( + [this](const ShutDownEvent & event) { return this->on_shutdown(event); }); this->loop_timer = make_unique(); this->mediator.loop_timer = *loop_timer; } @@ -41,7 +40,7 @@ void LoopManager::loop() { while (game_running) { this->loop_timer->update(); - + while (this->loop_timer->get_lag() >= this->loop_timer->get_fixed_delta_time()) { this->process_input(); this->fixed_update(); @@ -55,7 +54,6 @@ void LoopManager::loop() { } void LoopManager::setup() { - this->game_running = true; this->loop_timer->start(); @@ -67,7 +65,7 @@ void LoopManager::render() { this->get_system().update(); } -bool LoopManager::on_shutdown(const ShutDownEvent & e){ +bool LoopManager::on_shutdown(const ShutDownEvent & e) { this->game_running = false; return false; } diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index c50f5aa..6a212eb 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -5,10 +5,8 @@ #include "../facade/SDLContext.h" #include "../manager/ComponentManager.h" #include "../manager/SceneManager.h" -#include "../manager/SceneManager.h" #include "../system/System.h" - #include "api/Event.h" #include "api/LoopTimer.h" @@ -28,7 +26,6 @@ public: * Developers need to call this function to run the game. */ void start(); - /** * \brief Add a new concrete scene to the scene manager @@ -101,8 +98,8 @@ private: SDLContext & sdl_context = SDLContext::get_instance(); //! loop timer instance std::unique_ptr loop_timer; + private: - /** * \brief Callback function for ShutDownEvent * diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp index eedb5ee..8fb7ce8 100644 --- a/src/crepe/api/LoopTimer.cpp +++ b/src/crepe/api/LoopTimer.cpp @@ -10,13 +10,13 @@ using namespace crepe; LoopTimer::LoopTimer() { dbg_trace(); } - void LoopTimer::start() { this->last_frame_time = std::chrono::steady_clock::now(); - + this->elapsed_time = std::chrono::milliseconds(0); // 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(fixed_delta_time); + this->elapsed_fixed_time + = -std::chrono::duration_cast(fixed_delta_time); this->delta_time = std::chrono::milliseconds(0); } @@ -30,7 +30,7 @@ void LoopTimer::update() { this->delta_time = this->maximum_delta_time; } this->actual_fps = 1.0 / this->delta_time.count(); - + this->delta_time *= this->game_scale; this->elapsed_time += this->delta_time; this->last_frame_time = current_frame_time; @@ -56,17 +56,18 @@ 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() { - auto current_frame_time = std::chrono::steady_clock::now(); - auto frame_duration = current_frame_time - this->last_frame_time; + auto current_frame_time = std::chrono::steady_clock::now(); + auto 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) { - auto delay_time = std::chrono::duration_cast(this->frame_target_time - frame_duration); + // Check if frame duration is less than the target frame time + if (frame_duration < this->frame_target_time) { + auto delay_time = std::chrono::duration_cast( + this->frame_target_time - frame_duration); - if (delay_time.count() > 0) { - std::this_thread::sleep_for(delay_time); - } - } + if (delay_time.count() > 0) { + std::this_thread::sleep_for(delay_time); + } + } } double LoopTimer::get_lag() const { diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h index e348628..c4294d7 100644 --- a/src/crepe/api/LoopTimer.h +++ b/src/crepe/api/LoopTimer.h @@ -90,8 +90,6 @@ private: */ double get_lag() const; - - /** * \brief Update the timer to the current frame. * @@ -120,7 +118,8 @@ private: //! Delta time for the current frame in seconds std::chrono::duration delta_time{0.0}; //! Target time per frame in seconds - std::chrono::duration frame_target_time = std::chrono::duration(1.0) / target_fps; + std::chrono::duration frame_target_time + = std::chrono::duration(1.0) / target_fps; //! Fixed delta time for fixed updates in seconds std::chrono::duration fixed_delta_time = std::chrono::duration(1.0) / 50.0; //! Total elapsed game time in seconds @@ -129,7 +128,6 @@ private: std::chrono::duration 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/test/LoopManagerTest.cpp b/src/test/LoopManagerTest.cpp index 7937649..5897906 100644 --- a/src/test/LoopManagerTest.cpp +++ b/src/test/LoopManagerTest.cpp @@ -1,43 +1,45 @@ -#include #include +#include #include #define private public #define protected public -#include "api/LoopTimer.h" #include "api/LoopManager.h" +#include "api/LoopTimer.h" using namespace std::chrono; using namespace crepe; class LoopManagerTest : public ::testing::Test { protected: - LoopManager loop_manager; + LoopManager loop_manager; - void SetUp() override { - // Setting up loop manager and start the loop - loop_manager.loop_timer->set_target_fps(60); - } + void SetUp() override { + // Setting up loop manager and start the loop + loop_manager.loop_timer->set_target_fps(60); + } }; //Test to check if exactly 5 fixed updates are done every second (50Hz) TEST_F(LoopManagerTest, FixedUpdate) { - loop_manager.loop_timer->fixed_delta_time = std::chrono::milliseconds(20); + loop_manager.loop_timer->fixed_delta_time = std::chrono::milliseconds(20); loop_manager.loop_timer->set_target_fps(50); - int fixed_update_count = 0; + int fixed_update_count = 0; loop_manager.loop_timer->start(); - // We want to simulate the game loop for about 1 second - auto start_time = steady_clock::now(); + // We want to simulate the game loop for about 1 second + auto start_time = steady_clock::now(); - // Simulate the game loop for 1 second - while (duration_cast(steady_clock::now() - start_time) < std::chrono::milliseconds(1000)) { + // Simulate the game loop for 1 second + while (duration_cast(steady_clock::now() - start_time) + < std::chrono::milliseconds(1000)) { loop_manager.loop_timer->update(); - // Simulate processing fixed updates while there's lag to advance - while (loop_manager.loop_timer->get_lag() >= loop_manager.loop_timer->get_fixed_delta_time()) { - fixed_update_count++; - loop_manager.loop_timer->advance_fixed_update(); - } - - loop_manager.loop_timer->enforce_frame_rate(); - } - ASSERT_EQ(fixed_update_count, 50); + // Simulate processing fixed updates while there's lag to advance + while (loop_manager.loop_timer->get_lag() + >= loop_manager.loop_timer->get_fixed_delta_time()) { + fixed_update_count++; + loop_manager.loop_timer->advance_fixed_update(); + } + + loop_manager.loop_timer->enforce_frame_rate(); + } + ASSERT_EQ(fixed_update_count, 50); } diff --git a/src/test/LoopTimerTest.cpp b/src/test/LoopTimerTest.cpp index 6e3f118..9068c72 100644 --- a/src/test/LoopTimerTest.cpp +++ b/src/test/LoopTimerTest.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #define private public #define protected public @@ -10,72 +10,67 @@ using namespace crepe; class LoopTimerTest : public ::testing::Test { protected: - LoopTimer loop_timer; + LoopTimer loop_timer; - void SetUp() override { - loop_timer.start(); - } + 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_fps(60); + // Set the target FPS to 60 (which gives a target time per frame of ~16.67 ms) + loop_timer.set_target_fps(60); - auto start_time = steady_clock::now(); - loop_timer.enforce_frame_rate(); + auto start_time = steady_clock::now(); + loop_timer.enforce_frame_rate(); - auto elapsed_time = steady_clock::now() - start_time; - auto elapsed_ms = duration_cast(elapsed_time).count(); + auto elapsed_time = steady_clock::now() - start_time; + auto elapsed_ms = duration_cast(elapsed_time).count(); - // For 60 FPS, the target frame time is around 16.67ms - ASSERT_GE(elapsed_ms, 16); // Make sure it's at least 16 ms (could be slightly more) - ASSERT_LE(elapsed_ms, 18); // Ensure it's not too much longer + // For 60 FPS, the target frame time is around 16.67ms + ASSERT_GE(elapsed_ms, 16); // Make sure it's at least 16 ms (could be slightly more) + ASSERT_LE(elapsed_ms, 18); // Ensure it's not too much longer } TEST_F(LoopTimerTest, SetTargetFps) { - // Set the target FPS to 120 - loop_timer.set_target_fps(120); - - // Calculate the expected frame time (~8.33ms per frame) - auto expected_frame_time = std::chrono::duration(1.0 / 120.0); + // Set the target FPS to 120 + loop_timer.set_target_fps(120); - ASSERT_NEAR(loop_timer.frame_target_time.count(), expected_frame_time.count(), 0.001); + // Calculate the expected frame time (~8.33ms per frame) + auto expected_frame_time = std::chrono::duration(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_fps(60); + // Set the target FPS to 60 (16.67 ms per frame) + loop_timer.set_target_fps(60); + + auto start_time = steady_clock::now(); + loop_timer.update(); + auto end_time = steady_clock::now(); - auto start_time = steady_clock::now(); - loop_timer.update(); - auto end_time = steady_clock::now(); + // Check the delta time + double delta_time = loop_timer.get_delta_time(); - // Check the delta time - double delta_time = loop_timer.get_delta_time(); + auto elapsed_time = duration_cast(end_time - start_time).count(); - auto elapsed_time = duration_cast(end_time - start_time).count(); - - // Assert that delta_time is close to the elapsed time - ASSERT_GE(delta_time, elapsed_time / 1000.0); - ASSERT_LE(delta_time, (elapsed_time + 2) / 1000.0); + // Assert that delta_time is close to the elapsed time + ASSERT_GE(delta_time, elapsed_time / 1000.0); + ASSERT_LE(delta_time, (elapsed_time + 2) / 1000.0); } TEST_F(LoopTimerTest, getCurrentTime) { - // Set the target FPS to 60 (16.67 ms per frame) - loop_timer.set_target_fps(60); + // Set the target FPS to 60 (16.67 ms per frame) + loop_timer.set_target_fps(60); - auto start_time = steady_clock::now(); + auto start_time = steady_clock::now(); - // Sleep for 500 milliseconds - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + // Sleep for 500 milliseconds + std::this_thread::sleep_for(std::chrono::milliseconds(100)); - loop_timer.update(); + loop_timer.update(); - auto end_time = steady_clock::now(); + auto end_time = steady_clock::now(); - // Get the elapsed time in seconds as a double - auto elapsed_time = duration_cast>(end_time - start_time).count(); + // Get the elapsed time in seconds as a double + auto elapsed_time + = duration_cast>(end_time - start_time).count(); - ASSERT_NEAR(loop_timer.get_current_time(), elapsed_time, 0.001); - - + ASSERT_NEAR(loop_timer.get_current_time(), elapsed_time, 0.001); } - - -- cgit v1.2.3 From a73ff31b67faa7e6a922cfb5598f56f80bc01d62 Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Sat, 7 Dec 2024 14:19:16 +0100 Subject: added loopTimer and eventManager to mediator and removed the singletons --- src/crepe/api/CMakeLists.txt | 6 -- src/crepe/api/IKeyListener.cpp | 19 ----- src/crepe/api/IKeyListener.h | 50 ------------- src/crepe/api/IMouseListener.cpp | 29 ------- src/crepe/api/IMouseListener.h | 73 ------------------ src/crepe/api/LoopManager.cpp | 18 ++--- src/crepe/api/LoopManager.h | 9 ++- src/crepe/api/LoopTimer.cpp | 75 ------------------- src/crepe/api/LoopTimer.h | 133 --------------------------------- src/crepe/manager/CMakeLists.txt | 2 + src/crepe/manager/EventManager.cpp | 6 +- src/crepe/manager/EventManager.h | 24 ++---- src/crepe/manager/LoopTimerManager.cpp | 77 +++++++++++++++++++ src/crepe/manager/LoopTimerManager.h | 133 +++++++++++++++++++++++++++++++++ src/crepe/manager/Mediator.h | 8 +- src/test/EventTest.cpp | 88 +++++++++------------- src/test/LoopManagerTest.cpp | 29 ++++--- src/test/LoopTimerTest.cpp | 17 ++--- src/test/ScriptTest.h | 4 +- 19 files changed, 303 insertions(+), 497 deletions(-) delete mode 100644 src/crepe/api/IKeyListener.cpp delete mode 100644 src/crepe/api/IKeyListener.h delete mode 100644 src/crepe/api/IMouseListener.cpp delete mode 100644 src/crepe/api/IMouseListener.h delete mode 100644 src/crepe/api/LoopTimer.cpp delete mode 100644 src/crepe/api/LoopTimer.h create mode 100644 src/crepe/manager/LoopTimerManager.cpp create mode 100644 src/crepe/manager/LoopTimerManager.h (limited to 'src/crepe/api/LoopTimer.h') diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 7da9dca..60d9dc5 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -13,10 +13,7 @@ target_sources(crepe PUBLIC Metadata.cpp Camera.cpp Animator.cpp - IKeyListener.cpp - IMouseListener.cpp LoopManager.cpp - LoopTimer.cpp Asset.cpp EventHandler.cpp Script.cpp @@ -45,9 +42,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES EventHandler.h EventHandler.hpp Event.h - IKeyListener.h - IMouseListener.h LoopManager.h - LoopTimer.h Asset.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( - [this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel); - this->release_id = event_manager.subscribe( - [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 6ded107..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( - [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); }, - channel); - - this->press_id = event_manager.subscribe( - [this](const MousePressEvent & event) { return this->on_mouse_pressed(event); }, - channel); - - this->release_id = event_manager.subscribe( - [this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); }, - channel); - - this->move_id = event_manager.subscribe( - [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 9e4fdf7..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 454afe8..69cbfaf 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,3 +1,4 @@ +#include #include "../facade/SDLContext.h" #include "../manager/EventManager.h" @@ -20,10 +21,8 @@ LoopManager::LoopManager() { this->load_system(); this->load_system(); this->load_system(); - EventManager::get_instance().subscribe( + this->event_manager.subscribe( [this](const ShutDownEvent & event) { return this->on_shutdown(event); }); - this->loop_timer = make_unique(); - this->mediator.loop_timer = *loop_timer; } void LoopManager::process_input() { @@ -38,27 +37,28 @@ void LoopManager::start() { void LoopManager::fixed_update() {} void LoopManager::loop() { - this->loop_timer->start(); + while (game_running) { - this->loop_timer->update(); + this->loop_timer.update(); - while (this->loop_timer->get_lag() >= this->loop_timer->get_fixed_delta_time()) { + while (this->loop_timer.get_lag() >= this->loop_timer.get_fixed_delta_time()) { this->process_input(); + event_manager.dispatch_events(); this->fixed_update(); - this->loop_timer->advance_fixed_update(); + this->loop_timer.advance_fixed_update(); } this->update(); this->render(); - this->loop_timer->enforce_frame_rate(); + this->loop_timer.enforce_frame_rate(); } } void LoopManager::setup() { this->game_running = true; - this->loop_timer->start(); + this->loop_timer.start(); } void LoopManager::render() { diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 2161dff..00f5409 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -5,10 +5,11 @@ #include "../facade/SDLContext.h" #include "../manager/ComponentManager.h" #include "../manager/SceneManager.h" +#include "../manager/EventManager.h" +#include "../manager/LoopTimerManager.h" #include "../system/System.h" #include "api/Event.h" -#include "api/LoopTimer.h" namespace crepe { /** @@ -96,8 +97,10 @@ private: //! SDL context \todo no more singletons! SDLContext & sdl_context = SDLContext::get_instance(); - //! loop timer instance - std::unique_ptr loop_timer; + //! LoopTimer instance + LoopTimerManager loop_timer{mediator}; + //! EventManager instance + EventManager event_manager{mediator}; private: /** diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp deleted file mode 100644 index 8fb7ce8..0000000 --- a/src/crepe/api/LoopTimer.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include - -#include "../facade/SDLContext.h" -#include "../util/Log.h" - -#include "LoopTimer.h" - -using namespace crepe; - -LoopTimer::LoopTimer() { dbg_trace(); } - -void LoopTimer::start() { - this->last_frame_time = std::chrono::steady_clock::now(); - - this->elapsed_time = std::chrono::milliseconds(0); - // 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(fixed_delta_time); - 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>( - current_frame_time - last_frame_time); - - if (this->delta_time > this->maximum_delta_time) { - this->delta_time = this->maximum_delta_time; - } - this->actual_fps = 1.0 / this->delta_time.count(); - - 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_target_fps(int fps) { - this->target_fps = fps; - // target time per frame in seconds - this->frame_target_time = std::chrono::duration(1.0) / this->target_fps; -} - -int LoopTimer::get_fps() const { return this->actual_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() { - auto current_frame_time = std::chrono::steady_clock::now(); - auto 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) { - auto delay_time = std::chrono::duration_cast( - this->frame_target_time - frame_duration); - - if (delay_time.count() > 0) { - std::this_thread::sleep_for(delay_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 c4294d7..0000000 --- a/src/crepe/api/LoopTimer.h +++ /dev/null @@ -1,133 +0,0 @@ -#pragma once - -#include - -namespace crepe { - -class LoopTimer { -public: - LoopTimer(); - /** - * \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_target_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 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: - //! Target frames per second - int target_fps = 50; - //! Actual frames per second - int actual_fps = 0; - //! Current game scale - double game_scale = 1; - //! Maximum delta time in seconds to avoid large jumps - std::chrono::duration maximum_delta_time{0.25}; - //! Delta time for the current frame in seconds - std::chrono::duration delta_time{0.0}; - //! Target time per frame in seconds - std::chrono::duration frame_target_time - = std::chrono::duration(1.0) / target_fps; - //! Fixed delta time for fixed updates in seconds - std::chrono::duration fixed_delta_time = std::chrono::duration(1.0) / 50.0; - //! Total elapsed game time in seconds - std::chrono::duration elapsed_time{0.0}; - //! Total elapsed time for fixed updates in seconds - std::chrono::duration 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/manager/CMakeLists.txt b/src/crepe/manager/CMakeLists.txt index 517b8a2..29d6df0 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 ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -16,5 +17,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES SaveManager.h SceneManager.h SceneManager.hpp + LoopTimerManager.h ) diff --git a/src/crepe/manager/EventManager.cpp b/src/crepe/manager/EventManager.cpp index 20f0dd3..9b0fa95 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 d634f54..5f8b107 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,16 @@ 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. - */ - static EventManager & get_instance(); - + EventManager(Mediator & mediator); /** * \brief Subscribe to a specific event type. * @@ -107,12 +98,7 @@ public: void clear(); private: - /** - * \brief Default constructor for the EventManager. - * - * Constructor is private to enforce the singleton pattern. - */ - EventManager() = default; + /** * \struct QueueEntry diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp new file mode 100644 index 0000000..8156c6d --- /dev/null +++ b/src/crepe/manager/LoopTimerManager.cpp @@ -0,0 +1,77 @@ +#include +#include + +#include "../facade/SDLContext.h" +#include "../util/Log.h" + +#include "LoopTimerManager.h" + +using namespace crepe; + +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 = std::chrono::milliseconds(0); + // 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(fixed_delta_time); + this->delta_time = std::chrono::milliseconds(0); +} + +void LoopTimerManager::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>( + current_frame_time - last_frame_time); + + if (this->delta_time > this->maximum_delta_time) { + this->delta_time = this->maximum_delta_time; + } + this->actual_fps = 1.0 / this->delta_time.count(); + + this->elapsed_time += this->delta_time; + this->last_frame_time = current_frame_time; +} + +double LoopTimerManager::get_delta_time() const { return this->delta_time.count() * this->game_scale; } + +double LoopTimerManager::get_current_time() const { return this->elapsed_time.count(); } + +void LoopTimerManager::advance_fixed_update() { this->elapsed_fixed_time += this->fixed_delta_time; } + +double LoopTimerManager::get_fixed_delta_time() const { return this->fixed_delta_time.count(); } + +void LoopTimerManager::set_target_fps(int fps) { + this->target_fps = fps; + // target time per frame in seconds + this->frame_target_time = std::chrono::duration(1.0) / this->target_fps; +} + +int LoopTimerManager::get_fps() const { return this->actual_fps; } + +void LoopTimerManager::set_time_scale(double value) { this->game_scale = value; } + +double LoopTimerManager::get_time_scale() const { return this->game_scale; } +void LoopTimerManager::enforce_frame_rate() { + auto current_frame_time = std::chrono::steady_clock::now(); + auto 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) { + auto delay_time = std::chrono::duration_cast( + this->frame_target_time - frame_duration); + + if (delay_time.count() > 0) { + std::this_thread::sleep_for(delay_time); + } + } +} + +double LoopTimerManager::get_lag() const { + return (this->elapsed_time - this->elapsed_fixed_time).count(); +} diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h new file mode 100644 index 0000000..dba0f66 --- /dev/null +++ b/src/crepe/manager/LoopTimerManager.h @@ -0,0 +1,133 @@ +#pragma once + +#include +#include "../manager/Manager.h" +namespace crepe { + +class LoopTimerManager : public Manager { +public: + LoopTimerManager(Mediator & mediator); + /** + * \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_target_fps(int fps); + + /** + * \brief Get the current frames per second (FPS). + * + * \return Current FPS. + */ + int get_fps() const; + + /** + * \brief Get the current time scale. + * + * \return The current time scale, where 0 = paused, 1 = normal speed, and values > 1 speed + * up the game. + */ + double get_time_scale() const; + + /** + * \brief Set the time scale. + * + * \param game_scale The desired time scale (0 = pause, 1 = normal speed, > 1 = speed up). + */ + void set_time_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 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: + //! Target frames per second + int target_fps = 50; + //! Actual frames per second + int actual_fps = 0; + //! Current game scale + double game_scale = 1; + //! Maximum delta time in seconds to avoid large jumps + std::chrono::duration maximum_delta_time{0.25}; + //! Delta time for the current frame in seconds + std::chrono::duration delta_time{0.0}; + //! Target time per frame in seconds + std::chrono::duration frame_target_time + = std::chrono::duration(1.0) / target_fps; + //! Fixed delta time for fixed updates in seconds + std::chrono::duration fixed_delta_time = std::chrono::duration(1.0) / 50.0; + //! Total elapsed game time in seconds + std::chrono::duration elapsed_time{0.0}; + //! Total elapsed time for fixed updates in seconds + std::chrono::duration 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/manager/Mediator.h b/src/crepe/manager/Mediator.h index cd96614..c72af8e 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -3,14 +3,14 @@ #include "../util/OptionalRef.h" // TODO: remove these singletons: -#include "EventManager.h" #include "SaveManager.h" namespace crepe { class ComponentManager; class SceneManager; -class LoopTimer; +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 @@ -27,8 +27,8 @@ struct Mediator { OptionalRef component_manager; OptionalRef scene_manager; OptionalRef save_manager = SaveManager::get_instance(); - OptionalRef event_manager = EventManager::get_instance(); - OptionalRef loop_timer; + OptionalRef event_manager; + OptionalRef loop_timer; }; } // namespace crepe diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index dccd554..8479998 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -1,56 +1,41 @@ #include #include - #include -#include -#include #include - +#include 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 key_handler = [](const KeyPressEvent & e) { return true; }; // Subscribe to KeyPressEvent - EventManager::get_instance().subscribe(key_handler, 1); + event_mgr.subscribe(key_handler, 1); // Verify subscription (not directly verifiable; test by triggering event) - EventManager::get_instance().trigger_event( + event_mgr.trigger_event( KeyPressEvent{ .repeat = true, .key = Keycode::A, }, 1); - EventManager::get_instance().trigger_event( + event_mgr.trigger_event( KeyPressEvent{ .repeat = true, .key = Keycode::A, @@ -68,12 +53,12 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; - EventManager::get_instance().subscribe(mouse_handler, + event_mgr.subscribe(mouse_handler, EventManager::CHANNEL_ALL); MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - EventManager::get_instance().trigger_event(click_event, + event_mgr.trigger_event(click_event, EventManager::CHANNEL_ALL); EXPECT_TRUE(triggered); @@ -88,19 +73,18 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_one_channel) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; - EventManager::get_instance().subscribe(mouse_handler, test_channel); + event_mgr.subscribe(mouse_handler, test_channel); MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - EventManager::get_instance().trigger_event(click_event, + event_mgr.trigger_event(click_event, EventManager::CHANNEL_ALL); EXPECT_FALSE(triggered); - EventManager::get_instance().trigger_event(click_event, test_channel); + event_mgr.trigger_event(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 +110,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(mouse_handler_true, EventManager::CHANNEL_ALL); - event_manager.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); + event_mgr.subscribe(mouse_handler_true, EventManager::CHANNEL_ALL); + event_mgr.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); // Trigger event - event_manager.trigger_event(click_event, EventManager::CHANNEL_ALL); + event_mgr.trigger_event(click_event, EventManager::CHANNEL_ALL); // Check that only the true handler was triggered EXPECT_TRUE(triggered_true); @@ -139,12 +123,12 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { // Reset and clear triggered_true = false; triggered_false = false; - event_manager.clear(); - event_manager.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); - event_manager.subscribe(mouse_handler_true, EventManager::CHANNEL_ALL); + event_mgr.clear(); + event_mgr.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); + event_mgr.subscribe(mouse_handler_true, EventManager::CHANNEL_ALL); // Trigger event again - event_manager.trigger_event(click_event, EventManager::CHANNEL_ALL); + event_mgr.trigger_event(click_event, EventManager::CHANNEL_ALL); // Check that both handlers were triggered EXPECT_TRUE(triggered_true); @@ -152,7 +136,6 @@ 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; @@ -170,21 +153,20 @@ TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; - event_manager.subscribe(mouse_handler1); - event_manager.subscribe(mouse_handler2, test_channel); + event_mgr.subscribe(mouse_handler1); + event_mgr.subscribe(mouse_handler2, test_channel); - event_manager.queue_event( + event_mgr.queue_event( MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); - event_manager.queue_event( + event_mgr.queue_event( 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; @@ -207,15 +189,15 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { return false; // Allows propagation }; // Subscribe handlers - subscription_t handler1_id = event_manager.subscribe(mouse_handler1); - subscription_t handler2_id = event_manager.subscribe(mouse_handler2); + subscription_t handler1_id = event_mgr.subscribe(mouse_handler1); + subscription_t handler2_id = event_mgr.subscribe(mouse_handler2); // Queue events - event_manager.queue_event( + event_mgr.queue_event( 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 @@ -224,14 +206,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( + event_mgr.queue_event( 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 @@ -239,14 +221,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( + event_mgr.queue_event( 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/LoopManagerTest.cpp b/src/test/LoopManagerTest.cpp index 503eb1f..57f7a2e 100644 --- a/src/test/LoopManagerTest.cpp +++ b/src/test/LoopManagerTest.cpp @@ -2,13 +2,11 @@ #include #include #include -#include #define private public #define protected public -#include "api/LoopManager.h" -#include "api/LoopTimer.h" -#include "manager/EventManager.h" -#include "api/Event.h" +#include +#include +#include using namespace std::chrono; using namespace crepe; @@ -24,19 +22,18 @@ protected: TestGameLoop test_loop; // LoopManager test_loop; void SetUp() override { - test_loop.loop_timer->set_target_fps(10); } }; TEST_F(LoopManagerTest, FixedUpdate) { // Arrange - test_loop.loop_timer->set_target_fps(60); + test_loop.loop_timer.set_target_fps(60); // Set expectations for the mock calls EXPECT_CALL(test_loop, render).Times(::testing::Exactly(60)); EXPECT_CALL(test_loop, update).Times(::testing::Exactly(60)); - EXPECT_CALL(test_loop, fixed_update).Times(::testing::AtLeast(50)); + EXPECT_CALL(test_loop, fixed_update).Times(::testing::Exactly(50)); // Start the loop in a separate thread std::thread loop_thread([&]() { test_loop.start(); }); @@ -46,12 +43,26 @@ TEST_F(LoopManagerTest, FixedUpdate) { // 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_fps(60); + EXPECT_CALL(test_loop, render).Times(::testing::AtLeast(1)); + EXPECT_CALL(test_loop, 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{}); + // Wait for the loop thread to finish + loop_thread.join(); + + // Test finished +} diff --git a/src/test/LoopTimerTest.cpp b/src/test/LoopTimerTest.cpp index 7652093..09b4e00 100644 --- a/src/test/LoopTimerTest.cpp +++ b/src/test/LoopTimerTest.cpp @@ -1,16 +1,17 @@ #include -#include #include +#include #define private public #define protected public -#include "api/LoopTimer.h" - +#include +#include using namespace std::chrono; using namespace crepe; class LoopTimerTest : public ::testing::Test { protected: - LoopTimer loop_timer; + Mediator mediator; + LoopTimerManager loop_timer{mediator}; void SetUp() override { loop_timer.start(); } }; @@ -25,8 +26,7 @@ TEST_F(LoopTimerTest, EnforcesTargetFrameRate) { auto elapsed_ms = duration_cast(elapsed_time).count(); // For 60 FPS, the target frame time is around 16.67ms - ASSERT_GE(elapsed_ms, 16); // Make sure it's at least 16 ms (could be slightly more) - ASSERT_LE(elapsed_ms, 18); // Ensure it's not too much longer + ASSERT_NEAR(elapsed_ms,16.7,1); } TEST_F(LoopTimerTest, SetTargetFps) { // Set the target FPS to 120 @@ -48,11 +48,10 @@ TEST_F(LoopTimerTest, DeltaTimeCalculation) { // Check the delta time double delta_time = loop_timer.get_delta_time(); - auto elapsed_time = duration_cast(end_time - start_time).count(); + auto elapsed_time = duration_cast(end_time - start_time).count(); // Assert that delta_time is close to the elapsed time - ASSERT_GE(delta_time, elapsed_time / 1000.0); - ASSERT_LE(delta_time, (elapsed_time + 2) / 1000.0); + ASSERT_NEAR(delta_time, elapsed_time, 1); } TEST_F(LoopTimerTest, getCurrentTime) { diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h index 1bbfdd3..ee68c23 100644 --- a/src/test/ScriptTest.h +++ b/src/test/ScriptTest.h @@ -7,7 +7,7 @@ #include #include #include - +#include class ScriptTest : public testing::Test { protected: crepe::Mediator mediator; @@ -15,7 +15,7 @@ protected: public: crepe::ComponentManager component_manager{mediator}; crepe::ScriptSystem system{mediator}; - + crepe::EventManager event_mgr{mediator}; class MyScript : public crepe::Script { // NOTE: explicitly stating `public:` is not required on actual scripts -- cgit v1.2.3