diff options
| -rw-r--r-- | src/crepe/api/LoopManager.cpp | 45 | ||||
| -rw-r--r-- | src/crepe/api/LoopManager.h | 18 | ||||
| -rw-r--r-- | src/crepe/manager/LoopTimerManager.cpp | 56 | ||||
| -rw-r--r-- | src/crepe/manager/LoopTimerManager.h | 64 | ||||
| -rw-r--r-- | src/crepe/manager/Mediator.h | 1 | ||||
| -rw-r--r-- | src/crepe/system/AnimatorSystem.cpp | 2 | ||||
| -rw-r--r-- | src/test/LoopManagerTest.cpp | 12 | ||||
| -rw-r--r-- | src/test/LoopTimerTest.cpp | 20 | 
8 files changed, 99 insertions, 119 deletions
| diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index 46a7635..a1da8be 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -7,7 +7,8 @@  #include "../system/PhysicsSystem.h"  #include "../system/RenderSystem.h"  #include "../system/ScriptSystem.h" -#include "manager/EventManager.h" +#include "../manager/EventManager.h" +#include "../util/Log.h"  #include "LoopManager.h" @@ -25,56 +26,56 @@ LoopManager::LoopManager() {  	this->event_manager.subscribe<ShutDownEvent>(  		[this](const ShutDownEvent & event) { return this->on_shutdown(event); });  } - -void LoopManager::process_input() { -	this->get_system<InputSystem>().update(); -	this->event_manager.dispatch_events(); -} -  void LoopManager::start() {  	this->setup();  	this->loop();  } -void LoopManager::fixed_update() { -	this->get_system<ScriptSystem>().update(); -	this->get_system<PhysicsSystem>().update(); -	this->get_system<CollisionSystem>().update(); +void LoopManager::setup() { +	this->game_running = true; +	this->loop_timer.start(); +	this->scene_manager.load_next_scene();  }  void LoopManager::loop() { +	try {  	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();  			this->loop_timer.advance_fixed_elapsed_time();  		}  		this->frame_update(); -		this->render();  		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() { -	this->game_running = true; -	this->loop_timer.start(); -	this->scene_manager.load_next_scene(); +// 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<PhysicsSystem>().update(); +	this->get_system<CollisionSystem>().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();  }  bool LoopManager::on_shutdown(const ShutDownEvent & e) {  	this->game_running = false; +	// propagate to possible user ShutDownEvent listeners  	return false;  } - -void LoopManager::frame_update() { this->scene_manager.load_next_scene(); } diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index f94cea1..2319d65 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -25,7 +25,7 @@ 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. +	 * The Game programmer needs to call this function to run the game. This should be done after creating and adding all scenes.  	 */  	void start(); @@ -52,13 +52,6 @@ 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. @@ -71,15 +64,9 @@ private:  	 * This function updates physics and game logic based on LoopTimer's fixed_delta_time.  	 */  	virtual void fixed_update(); -	/** -	 * \brief Function for executing render-related systems. -	 * -	 * Renders the current state of the game to the screen. -	 */ -	virtual void render(); +	//! Indicates whether the game is running.  	bool game_running = false; -  private:  	//! Global context  	Mediator mediator; @@ -97,6 +84,7 @@ private:  	SDLContext & sdl_context = SDLContext::get_instance();  private: +	  	/**  	 * \brief Callback function for ShutDownEvent  	 * diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp index 8725c33..a306eb7 100644 --- a/src/crepe/manager/LoopTimerManager.cpp +++ b/src/crepe/manager/LoopTimerManager.cpp @@ -6,7 +6,7 @@  #include "LoopTimerManager.h"  using namespace crepe; - +using namespace std::chrono_literals;  LoopTimerManager::LoopTimerManager(Mediator & mediator) : Manager(mediator) {  	this->mediator.loop_timer = *this;  	dbg_trace(); @@ -15,62 +15,57 @@ LoopTimerManager::LoopTimerManager(Mediator & mediator) : Manager(mediator) {  void LoopTimerManager::start() {  	this->last_frame_time = std::chrono::steady_clock::now(); -	this->elapsed_time = std::chrono::milliseconds(0); +	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<std::chrono::milliseconds>(fixed_delta_time); -	this->delta_time = std::chrono::milliseconds(0); +		= -std::chrono::duration_cast<ElapsedTime_t>(this->fixed_delta_time); +	this->delta_time = 0s;  }  void LoopTimerManager::update() { -	std::chrono::steady_clock::time_point current_frame_time +	TimePoint_t 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); +	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.count() > 0.0) { +	if (this->delta_time > 0s) {  		this->actual_fps = 1.0 / this->delta_time.count();  	} else {  		this->actual_fps = 0;  	} - -	this->elapsed_time += this->delta_time; +	this->elapsed_time += std::chrono::duration_cast<ElapsedTime_t>(this->delta_time);  	this->last_frame_time = current_frame_time;  } -double LoopTimerManager::get_delta_time() const { -	return this->delta_time.count() * this->time_scale; -} +Duration_t LoopTimerManager::get_delta_time() const {return this->delta_time * this->time_scale;} -double LoopTimerManager::get_current_time() const { return this->elapsed_time.count(); } +ElapsedTime_t LoopTimerManager::get_elapsed_time() const { return this->elapsed_time; }  void LoopTimerManager::advance_fixed_elapsed_time() { -	this->elapsed_fixed_time += this->fixed_delta_time; +	this->elapsed_fixed_time += std::chrono::duration_cast<ElapsedTime_t>(this->fixed_delta_time);  } -void LoopTimerManager::set_target_fps(int fps) { +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 = std::chrono::duration<double>(1.0) / this->target_fps; +	this->frame_target_time = Duration_t(1s) / this->target_fps;  } -int LoopTimerManager::get_fps() const { return this->actual_fps; } +unsigned LoopTimerManager::get_fps() const { return this->actual_fps; }  void LoopTimerManager::set_time_scale(double value) { this->time_scale = value; } -double LoopTimerManager::get_time_scale() const { return this->time_scale; } +float LoopTimerManager::get_time_scale() const { return this->time_scale; }  void LoopTimerManager::enforce_frame_rate() { -	std::chrono::steady_clock::time_point current_frame_time +	TimePoint_t current_frame_time  		= std::chrono::steady_clock::now(); -	std::chrono::duration<double> frame_duration = current_frame_time - this->last_frame_time; - +	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 @@ -83,16 +78,17 @@ void LoopTimerManager::enforce_frame_rate() {  	}  } -double LoopTimerManager::get_lag() const { -	return (this->elapsed_time - this->elapsed_fixed_time).count(); +Duration_t LoopTimerManager::get_lag() const { +	return (this->elapsed_time - this->elapsed_fixed_time);  } -double LoopTimerManager::get_scaled_fixed_delta_time() const { -	return this->fixed_delta_time.count() * this->time_scale; + +Duration_t LoopTimerManager::get_scaled_fixed_delta_time() const { +	return this->fixed_delta_time * this->time_scale;  } -void LoopTimerManager::set_fixed_delta_time(double seconds) { -	this->fixed_delta_time = std::chrono::duration<double>(seconds); +void LoopTimerManager::set_fixed_delta_time(float seconds) { +	this->fixed_delta_time = Duration_t(seconds);  } -double LoopTimerManager::get_fixed_delta_time() const { -	return this->fixed_delta_time.count(); +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 index ec44d52..c5f3cb0 100644 --- a/src/crepe/manager/LoopTimerManager.h +++ b/src/crepe/manager/LoopTimerManager.h @@ -5,7 +5,9 @@  #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.   *  @@ -28,31 +30,31 @@ public:  	 *   	 * \return Delta time in seconds since the last frame.  	 */ -	double get_delta_time() const; +	Duration_t get_delta_time() const;  	/** -	 * \brief Get the current game time. +	 * \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.  	 */ -	double get_current_time() const; +	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_fps(int fps); +	void set_target_framerate(unsigned fps);  	/**  	 * \brief Get the current frames per second (FPS).  	 *  	 * \return Current FPS.  	 */ -	int get_fps() const; +	unsigned get_fps() const;  	/**  	 * \brief Get the current time scale. @@ -60,7 +62,7 @@ public:  	 * \return The current time scale, where (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up).  	 * up the game.  	 */ -	double get_time_scale() const; +	float get_time_scale() const;  	/**  	 * \brief Set the time scale. @@ -76,11 +78,10 @@ public:  	 *  	 * This value is used in the LoopManager to determine how many times   	 * the fixed_update should be called within a given interval. -	 * This value is also the timing value which is used in the fixed_loop to convert pixels to time.  	 *  	 * \return The unscaled fixed delta time in seconds.  	 */ -	double get_fixed_delta_time() const; +	Duration_t get_fixed_delta_time() const;  	/**  	 * \brief Set the fixed_delta_time in seconds. @@ -88,9 +89,9 @@ public:  	 * \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. -	 * This value is also the timing value which is used in the fixed_loop to convert pixels to time. +	 *   	 */ -	void set_fixed_delta_time(double seconds); +	void set_fixed_delta_time(float seconds);  	/**  	 * \brief Retrieves the scaled fixed delta time in seconds. @@ -101,12 +102,11 @@ public:  	 *  	 * \return The fixed delta time, scaled by the current time scale, in seconds.  	 */ -	double get_scaled_fixed_delta_time() const; +	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.  	 * @@ -128,7 +128,7 @@ private:  	 *  	 * \return Accumulated lag in seconds.  	 */ -	double get_lag() const; +	Duration_t get_lag() const;  	/**  	 * \brief Update the timer to the current frame. @@ -147,27 +147,27 @@ private:  	void advance_fixed_elapsed_time();  private: -	//! Target frames per second +	//! Target frames per second.  	int target_fps = 60; -	//! Actual frames per second +	//! 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) -	double time_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}; +	//! 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 -	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 -	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; +	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 eb8a7a5..ad51cc6 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -11,6 +11,7 @@ namespace crepe {  class ComponentManager;  class SceneManager; +  class LoopTimerManager;  class EventManager;  /** diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp index 499f618..690b45b 100644 --- a/src/crepe/system/AnimatorSystem.cpp +++ b/src/crepe/system/AnimatorSystem.cpp @@ -13,7 +13,7 @@ void AnimatorSystem::update() {  	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/LoopManagerTest.cpp b/src/test/LoopManagerTest.cpp index c44ebda..4e0ecdc 100644 --- a/src/test/LoopManagerTest.cpp +++ b/src/test/LoopManagerTest.cpp @@ -16,7 +16,6 @@ protected:  	public:  		MOCK_METHOD(void, fixed_update, (), (override));  		MOCK_METHOD(void, frame_update, (), (override)); -		MOCK_METHOD(void, render, (), (override));  	};  	TestGameLoop test_loop; @@ -25,10 +24,9 @@ protected:  TEST_F(LoopManagerTest, FixedUpdate) {  	// Arrange -	test_loop.loop_timer.set_target_fps(60); +	test_loop.loop_timer.set_target_framerate(60);  	// Set expectations for the mock calls -	EXPECT_CALL(test_loop, render).Times(::testing::Exactly(60));  	EXPECT_CALL(test_loop, frame_update).Times(::testing::Exactly(60));  	EXPECT_CALL(test_loop, fixed_update).Times(::testing::Exactly(50)); @@ -47,10 +45,9 @@ TEST_F(LoopManagerTest, FixedUpdate) {  }  TEST_F(LoopManagerTest, ScaledFixedUpdate) {  	// Arrange -	test_loop.loop_timer.set_target_fps(60); +	test_loop.loop_timer.set_target_framerate(60);  	// Set expectations for the mock calls -	EXPECT_CALL(test_loop, render).Times(::testing::Exactly(60));  	EXPECT_CALL(test_loop, frame_update).Times(::testing::Exactly(60));  	EXPECT_CALL(test_loop, fixed_update).Times(::testing::Exactly(50)); @@ -69,9 +66,8 @@ TEST_F(LoopManagerTest, ScaledFixedUpdate) {  }  TEST_F(LoopManagerTest, ShutDown) {  	// Arrange -	test_loop.loop_timer.set_target_fps(60); +	test_loop.loop_timer.set_target_framerate(60); -	EXPECT_CALL(test_loop, render).Times(::testing::AtLeast(1));  	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 @@ -80,6 +76,4 @@ TEST_F(LoopManagerTest, ShutDown) {  	test_loop.event_manager.trigger_event<ShutDownEvent>(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 c6655d9..1216e5e 100644 --- a/src/test/LoopTimerTest.cpp +++ b/src/test/LoopTimerTest.cpp @@ -17,7 +17,7 @@ protected:  };  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); +	loop_timer.set_target_framerate(60);  	auto start_time = steady_clock::now();  	loop_timer.enforce_frame_rate(); @@ -30,33 +30,33 @@ TEST_F(LoopTimerTest, EnforcesTargetFrameRate) {  }  TEST_F(LoopTimerTest, SetTargetFps) {  	// Set the target FPS to 120 -	loop_timer.set_target_fps(120); +	loop_timer.set_target_framerate(120);  	// Calculate the expected frame time (~8.33ms per frame) -	auto expected_frame_time = std::chrono::duration<double>(1.0 / 120.0); +	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_fps(60); +	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 -	double delta_time = loop_timer.get_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, elapsed_time, 1); +	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_fps(60); +	loop_timer.set_target_framerate(60);  	auto start_time = steady_clock::now(); @@ -68,8 +68,8 @@ TEST_F(LoopTimerTest, getCurrentTime) {  	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(); +	auto elapsed_time = std::chrono::duration_cast<ElapsedTime_t>(end_time - start_time).count(); -	ASSERT_NEAR(loop_timer.get_current_time(), elapsed_time, 0.001); + +	ASSERT_NEAR(loop_timer.get_elapsed_time().count(), elapsed_time, 5);  } |