aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/crepe/api/LoopManager.cpp36
-rw-r--r--src/crepe/api/LoopManager.h29
-rw-r--r--src/crepe/api/LoopTimer.cpp45
-rw-r--r--src/crepe/api/LoopTimer.h27
-rw-r--r--src/crepe/manager/Mediator.h3
-rw-r--r--src/example/CMakeLists.txt2
-rw-r--r--src/example/gameloop.cpp7
-rw-r--r--src/test/CMakeLists.txt5
-rw-r--r--src/test/LoopManagerTest.cpp44
-rw-r--r--src/test/LoopTimerTest.cpp81
10 files changed, 195 insertions, 84 deletions
diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp
index 731cfb7..040cb93 100644
--- a/src/crepe/api/LoopManager.cpp
+++ b/src/crepe/api/LoopManager.cpp
@@ -1,9 +1,12 @@
+#include "../facade/SDLContext.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"
@@ -11,15 +14,17 @@ using namespace crepe;
using namespace std;
LoopManager::LoopManager() {
- this->mediator.component_manager = this->component_manager;
- this->mediator.scene_manager = this->scene_manager;
-
this->load_system<AnimatorSystem>();
this->load_system<CollisionSystem>();
this->load_system<ParticleSystem>();
this->load_system<PhysicsSystem>();
this->load_system<RenderSystem>();
this->load_system<ScriptSystem>();
+ EventManager::get_instance().subscribe<ShutDownEvent>([this](const ShutDownEvent& event) {
+ return this->on_shutdown(event);
+ });
+ this->loop_timer = make_unique<LoopTimer>();
+ this->mediator.loop_timer = *loop_timer;
}
void LoopManager::process_input() { this->sdl_context.handle_events(this->game_running); }
@@ -28,36 +33,33 @@ void LoopManager::start() {
this->setup();
this->loop();
}
-void LoopManager::set_running(bool running) { this->game_running = running; }
void LoopManager::fixed_update() {}
void LoopManager::loop() {
- LoopTimer & timer = this->loop_timer;
- timer.start();
+ this->loop_timer->start();
while (game_running) {
- timer.update();
-
- while (timer.get_lag() >= timer.get_fixed_delta_time()) {
+ this->loop_timer->update();
+
+ 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() {
- LoopTimer & timer = this->loop_timer;
+
this->game_running = true;
- timer.start();
- timer.set_fps(200);
+ this->loop_timer->start();
+ this->loop_timer->set_target_fps(200);
}
void LoopManager::render() {
@@ -65,5 +67,9 @@ void LoopManager::render() {
this->get_system<RenderSystem>().update();
}
+bool LoopManager::on_shutdown(const ShutDownEvent & e){
+ this->game_running = false;
+ return false;
+}
void LoopManager::update() {}
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
index d8910a0..17bddd1 100644
--- a/src/crepe/api/LoopManager.h
+++ b/src/crepe/api/LoopManager.h
@@ -6,11 +6,12 @@
#include "../manager/ComponentManager.h"
#include "../manager/SceneManager.h"
#include "../system/System.h"
+#include "manager/SceneManager.h"
-#include "LoopTimer.h"
+#include "api/Event.h"
+#include "api/LoopTimer.h"
namespace crepe {
-
/**
* \brief Main game loop manager
*
@@ -18,6 +19,12 @@ namespace crepe {
*/
class LoopManager {
public:
+ /**
+ * \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.
+ * Developers need to call this function to run the game.
+ */
void start();
LoopManager();
@@ -70,14 +77,6 @@ private:
* 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.
*
@@ -98,15 +97,17 @@ 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();
-
+ //! loop timer instance
+ std::unique_ptr<LoopTimer> loop_timer;
private:
+
+ //! callback function for shutdown event
+ 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
index 15a0e3a..07f0f75 100644
--- a/src/crepe/api/LoopTimer.cpp
+++ b/src/crepe/api/LoopTimer.cpp
@@ -1,4 +1,5 @@
#include <chrono>
+#include <thread>
#include "../facade/SDLContext.h"
#include "../util/Log.h"
@@ -9,15 +10,13 @@ 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);
+ // 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<std::chrono::milliseconds>(fixed_delta_time);
this->delta_time = std::chrono::milliseconds(0);
}
@@ -30,7 +29,8 @@ void LoopTimer::update() {
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;
@@ -44,34 +44,29 @@ void LoopTimer::advance_fixed_update() { this->elapsed_fixed_time += this->fixed
double LoopTimer::get_fixed_delta_time() const { return this->fixed_delta_time.count(); }
-void LoopTimer::set_fps(int fps) {
- this->fps = fps;
+void LoopTimer::set_target_fps(int fps) {
+ this->target_fps = fps;
// target time per frame in seconds
- this->frame_target_time = std::chrono::duration<double>(1.0) / fps;
+ this->frame_target_time = std::chrono::duration<double>(1.0) / target_fps;
}
-int LoopTimer::get_fps() const { return this->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() {
- 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());
- }
- }
+ 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;
+ // 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<std::chrono::microseconds>(this->frame_target_time - frame_duration);
+
+ 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 9393439..e348628 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.
*
@@ -35,7 +29,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).
@@ -68,7 +62,6 @@ private:
* Initializes the timer to begin tracking frame times.
*/
void start();
-
/**
* \brief Enforce the frame rate limit.
*
@@ -97,12 +90,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.
@@ -121,8 +109,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 +120,7 @@ private:
//! 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;
+ std::chrono::duration<double> frame_target_time = std::chrono::duration<double>(1.0) / target_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
@@ -139,6 +129,7 @@ private:
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/manager/Mediator.h b/src/crepe/manager/Mediator.h
index 71bd1c9..cd96614 100644
--- a/src/crepe/manager/Mediator.h
+++ b/src/crepe/manager/Mediator.h
@@ -10,7 +10,7 @@ namespace crepe {
class ComponentManager;
class SceneManager;
-
+class LoopTimer;
/**
* 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
@@ -28,6 +28,7 @@ struct Mediator {
OptionalRef<SceneManager> scene_manager;
OptionalRef<SaveManager> save_manager = SaveManager::get_instance();
OptionalRef<EventManager> event_manager = EventManager::get_instance();
+ OptionalRef<LoopTimer> loop_timer;
};
} // namespace crepe
diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt
index 560e2bc..6f92d45 100644
--- a/src/example/CMakeLists.txt
+++ b/src/example/CMakeLists.txt
@@ -19,5 +19,3 @@ endfunction()
add_example(asset_manager)
add_example(savemgr)
add_example(rendering_particle)
-add_example(gameloop)
-
diff --git a/src/example/gameloop.cpp b/src/example/gameloop.cpp
deleted file mode 100644
index a676f20..0000000
--- a/src/example/gameloop.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "crepe/api/LoopManager.h"
-using namespace crepe;
-int main() {
- LoopManager gameloop;
- gameloop.start();
- return 1;
-}
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index d3e27b0..232c763 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -12,6 +12,7 @@ target_sources(test_main PUBLIC
ValueBrokerTest.cpp
DBTest.cpp
Vector2Test.cpp
- ScriptEventTest.cpp
- ScriptSceneTest.cpp
+ LoopManagerTest.cpp
+ LoopTimerTest.cpp
+
)
diff --git a/src/test/LoopManagerTest.cpp b/src/test/LoopManagerTest.cpp
new file mode 100644
index 0000000..af6cb1c
--- /dev/null
+++ b/src/test/LoopManagerTest.cpp
@@ -0,0 +1,44 @@
+#include <gtest/gtest.h>
+#include <chrono>
+#include <thread>
+#define private public
+#define protected public
+#include "api/LoopTimer.h"
+#include "api/LoopManager.h"
+
+using namespace std::chrono;
+using namespace crepe;
+
+class LoopManagerTest : public ::testing::Test {
+protected:
+ LoopManager loop_manager;
+
+ 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->set_target_fps(50);
+ 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();
+
+ // Simulate the game loop for 1 second
+ while (duration_cast<milliseconds>(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();
+ }
+ // gameloop is 99 because it first takes 20 millisecond to build the lag to execute the fixed loop
+ ASSERT_EQ(fixed_update_count, 50);
+}
diff --git a/src/test/LoopTimerTest.cpp b/src/test/LoopTimerTest.cpp
new file mode 100644
index 0000000..6e3f118
--- /dev/null
+++ b/src/test/LoopTimerTest.cpp
@@ -0,0 +1,81 @@
+#include <gtest/gtest.h>
+#include <chrono>
+#include <thread>
+#define private public
+#define protected public
+#include "api/LoopTimer.h"
+
+using namespace std::chrono;
+using namespace crepe;
+
+class LoopTimerTest : public ::testing::Test {
+protected:
+ LoopTimer loop_timer;
+
+ 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);
+
+ 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_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<double>(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);
+
+ 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();
+
+ auto elapsed_time = duration_cast<milliseconds>(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);
+}
+
+TEST_F(LoopTimerTest, getCurrentTime) {
+ // Set the target FPS to 60 (16.67 ms per frame)
+ loop_timer.set_target_fps(60);
+
+ auto start_time = steady_clock::now();
+
+ // Sleep for 500 milliseconds
+ 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 = duration_cast<std::chrono::duration<double>>(end_time - start_time).count();
+
+ ASSERT_NEAR(loop_timer.get_current_time(), elapsed_time, 0.001);
+
+
+}
+
+