aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLoek Le Blansch <loek@pipeframe.xyz>2024-11-12 18:18:29 +0100
committerLoek Le Blansch <loek@pipeframe.xyz>2024-11-12 18:18:29 +0100
commit6a83afb0da8abb7a6db068dbf4e885617b71383e (patch)
treea3fbde5cd940a5b2a113ec6ad428ea383245c80a /src
parentc926205fa73bfbc15fd184de512db7092daefe95 (diff)
parentd8ab8df9e61d908348a25d9556ec2e8c19656315 (diff)
Merge branch 'wouter/LoopManager-new' of github.com:lonkaars/crepe
Diffstat (limited to 'src')
-rw-r--r--src/crepe/api/CMakeLists.txt4
-rw-r--r--src/crepe/api/LoopManager.cpp55
-rw-r--r--src/crepe/api/LoopManager.h79
-rw-r--r--src/crepe/api/LoopTimer.cpp86
-rw-r--r--src/crepe/api/LoopTimer.h147
-rw-r--r--src/crepe/facade/SDLContext.cpp1
-rw-r--r--src/crepe/facade/SDLContext.h13
-rw-r--r--src/example/CMakeLists.txt1
-rw-r--r--src/example/gameloop.cpp7
-rw-r--r--src/makefile5
10 files changed, 394 insertions, 4 deletions
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index 87cbb09..85696c4 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -18,6 +18,8 @@ target_sources(crepe PUBLIC
Vector2.cpp
Camera.cpp
Animator.cpp
+ LoopManager.cpp
+ LoopTimer.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -42,4 +44,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
SceneManager.hpp
Camera.h
Animator.h
+ LoopManager.h
+ LoopTimer.h
)
diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp
new file mode 100644
index 0000000..2e9823f
--- /dev/null
+++ b/src/crepe/api/LoopManager.cpp
@@ -0,0 +1,55 @@
+
+#include "../facade/SDLContext.h"
+#include "../system/RenderSystem.h"
+#include "../system/ScriptSystem.h"
+
+#include "LoopManager.h"
+#include "LoopTimer.h"
+
+using namespace crepe;
+
+LoopManager::LoopManager() {}
+void LoopManager::process_input() {
+ SDLContext::get_instance().handle_events(this->game_running);
+}
+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 = LoopTimer::get_instance();
+ timer.start();
+
+ while (game_running) {
+ timer.update();
+
+ while (timer.get_lag() >= timer.get_fixed_delta_time()) {
+ this->process_input();
+ this->fixed_update();
+ timer.advance_fixed_update();
+ }
+
+ this->update();
+ this->render();
+
+ timer.enforce_frame_rate();
+ }
+}
+
+void LoopManager::setup() {
+ this->game_running = true;
+ LoopTimer::get_instance().start();
+ LoopTimer::get_instance().set_fps(60);
+}
+
+void LoopManager::render() {
+ if (this->game_running) {
+ RenderSystem::get_instance().update();
+ }
+}
+
+void LoopManager::update() { LoopTimer & timer = LoopTimer::get_instance(); }
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
new file mode 100644
index 0000000..2f03193
--- /dev/null
+++ b/src/crepe/api/LoopManager.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include <memory>
+
+class RenderSystem;
+class SDLContext;
+class LoopTimer;
+class ScriptSystem;
+class SoundSystem;
+class ParticleSystem;
+class PhysicsSystem;
+class AnimatorSystem;
+class CollisionSystem;
+namespace crepe {
+
+class LoopManager {
+public:
+ void start();
+ LoopManager();
+
+private:
+ /**
+ * \brief Setup function for one-time initialization.
+ *
+ * This function initializes necessary components for the game.
+ */
+ void setup();
+ /**
+ * \brief Main game loop function.
+ *
+ * This function runs the main loop, handling game updates and rendering.
+ */
+ void loop();
+
+ /**
+ * \brief Function for handling input-related system calls.
+ *
+ * Processes user inputs from keyboard and mouse.
+ */
+ void process_input();
+
+ /**
+ * \brief Per-frame update.
+ *
+ * Updates the game state based on the elapsed time since the last frame.
+ */
+ void update();
+
+ /**
+ * \brief Late update which is called after update().
+ *
+ * This function can be used for final adjustments before rendering.
+ */
+ void late_update();
+
+ /**
+ * \brief Fixed update executed at a fixed rate.
+ *
+ * This function updates physics and game logic based on LoopTimer's fixed_delta_time.
+ */
+ void fixed_update();
+ /**
+ * \brief Set game running variable
+ *
+ * \param running running (false = game shutdown, true = game running)
+ */
+ void set_running(bool running);
+ /**
+ * \brief Function for executing render-related systems.
+ *
+ * Renders the current state of the game to the screen.
+ */
+ void render();
+
+ bool game_running = false;
+ //#TODO add system instances
+};
+
+} // namespace crepe
diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp
new file mode 100644
index 0000000..8f09e41
--- /dev/null
+++ b/src/crepe/api/LoopTimer.cpp
@@ -0,0 +1,86 @@
+#include <chrono>
+
+#include "../facade/SDLContext.h"
+#include "../util/log.h"
+
+#include "LoopTimer.h"
+
+using namespace crepe;
+
+LoopTimer::LoopTimer() { dbg_trace(); }
+
+LoopTimer & LoopTimer::get_instance() {
+ static LoopTimer instance;
+ return instance;
+}
+
+void LoopTimer::start() {
+ this->last_frame_time = std::chrono::steady_clock::now();
+ this->elapsed_time = std::chrono::milliseconds(0);
+ this->elapsed_fixed_time = std::chrono::milliseconds(0);
+ this->delta_time = std::chrono::milliseconds(0);
+}
+
+void LoopTimer::update() {
+ auto current_frame_time = std::chrono::steady_clock::now();
+ // Convert to duration in seconds for delta time
+ this->delta_time
+ = std::chrono::duration_cast<std::chrono::duration<double>>(
+ current_frame_time - last_frame_time);
+
+ if (this->delta_time > this->maximum_delta_time) {
+ this->delta_time = this->maximum_delta_time;
+ }
+
+ this->delta_time *= this->game_scale;
+ this->elapsed_time += this->delta_time;
+ this->last_frame_time = current_frame_time;
+}
+
+double LoopTimer::get_delta_time() const { return this->delta_time.count(); }
+
+double LoopTimer::get_current_time() const {
+ return this->elapsed_time.count();
+}
+
+void LoopTimer::advance_fixed_update() {
+ this->elapsed_fixed_time += this->fixed_delta_time;
+}
+
+double LoopTimer::get_fixed_delta_time() const {
+ return this->fixed_delta_time.count();
+}
+
+void LoopTimer::set_fps(int fps) {
+ this->fps = fps;
+ // target time per frame in seconds
+ this->frame_target_time = std::chrono::seconds(1) / fps;
+}
+
+int LoopTimer::get_fps() const { return this->fps; }
+
+void LoopTimer::set_game_scale(double value) { this->game_scale = value; }
+
+double LoopTimer::get_game_scale() const { return this->game_scale; }
+void LoopTimer::enforce_frame_rate() {
+ std::chrono::steady_clock::time_point current_frame_time
+ = std::chrono::steady_clock::now();
+ std::chrono::milliseconds frame_duration
+ = std::chrono::duration_cast<std::chrono::milliseconds>(
+ current_frame_time - this->last_frame_time);
+
+ if (frame_duration < this->frame_target_time) {
+ std::chrono::milliseconds delay_time
+ = std::chrono::duration_cast<std::chrono::milliseconds>(
+ this->frame_target_time - frame_duration);
+ if (delay_time.count() > 0) {
+ SDLContext::get_instance().delay(delay_time.count());
+ }
+ }
+
+ this->last_frame_time = current_frame_time;
+}
+
+double LoopTimer::get_lag() const {
+ return (this->elapsed_time - this->elapsed_fixed_time).count();
+}
diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h
new file mode 100644
index 0000000..85687be
--- /dev/null
+++ b/src/crepe/api/LoopTimer.h
@@ -0,0 +1,147 @@
+#pragma once
+
+#include <chrono>
+#include <cstdint>
+
+namespace crepe {
+
+class LoopTimer {
+public:
+ /**
+ * \brief Get the singleton instance of LoopTimer.
+ *
+ * \return A reference to the LoopTimer instance.
+ */
+ static LoopTimer & get_instance();
+
+ /**
+ * \brief Get the current delta time for the current frame.
+ *
+ * \return Delta time in seconds since the last frame.
+ */
+ double get_delta_time() const;
+
+ /**
+ * \brief Get the current game time.
+ *
+ * \note The current game time may vary from real-world elapsed time.
+ * It is the cumulative sum of each frame's delta time.
+ *
+ * \return Elapsed game time in seconds.
+ */
+ double get_current_time() const;
+
+ /**
+ * \brief Set the target frames per second (FPS).
+ *
+ * \param fps The desired frames rendered per second.
+ */
+ void set_fps(int fps);
+
+ /**
+ * \brief Get the current frames per second (FPS).
+ *
+ * \return Current FPS.
+ */
+ int get_fps() const;
+
+ /**
+ * \brief Get the current game scale.
+ *
+ * \return The current game scale, where 0 = paused, 1 = normal speed, and values > 1 speed up the game.
+ */
+ double get_game_scale() const;
+
+ /**
+ * \brief Set the game scale.
+ *
+ * \param game_scale The desired game scale (0 = pause, 1 = normal speed, > 1 = speed up).
+ */
+ void set_game_scale(double game_scale);
+
+private:
+ friend class LoopManager;
+
+ /**
+ * \brief Start the loop timer.
+ *
+ * Initializes the timer to begin tracking frame times.
+ */
+ void start();
+
+ /**
+ * \brief Enforce the frame rate limit.
+ *
+ * Ensures that the game loop does not exceed the target FPS by delaying
+ * frame updates as necessary.
+ */
+ void enforce_frame_rate();
+
+ /**
+ * \brief Get the fixed delta time for consistent updates.
+ *
+ * Fixed delta time is used for operations that require uniform time steps,
+ * such as physics calculations.
+ *
+ * \return Fixed delta time in seconds.
+ */
+ double get_fixed_delta_time() const;
+
+ /**
+ * \brief Get the accumulated lag in the game loop.
+ *
+ * Lag represents the difference between the target frame time and the
+ * actual frame time, useful for managing fixed update intervals.
+ *
+ * \return Accumulated lag in seconds.
+ */
+ double get_lag() const;
+
+ /**
+ * \brief Construct a new LoopTimer object.
+ *
+ * Private constructor for singleton pattern to restrict instantiation
+ * outside the class.
+ */
+ LoopTimer();
+
+ /**
+ * \brief Update the timer to the current frame.
+ *
+ * Calculates and updates the delta time for the current frame and adds it to
+ * the cumulative game time.
+ */
+ void update();
+
+ /**
+ * \brief Advance the game loop by a fixed update interval.
+ *
+ * This method progresses the game state by a consistent, fixed time step,
+ * allowing for stable updates independent of frame rate fluctuations.
+ */
+ void advance_fixed_update();
+
+private:
+ //! Current frames per second
+ int fps = 50;
+ //! Current game scale
+ double game_scale = 1;
+ //! Maximum delta time in seconds to avoid large jumps
+ std::chrono::duration<double> maximum_delta_time{0.25};
+ //! Delta time for the current frame in seconds
+ std::chrono::duration<double> delta_time{0.0};
+ //! Target time per frame in seconds
+ std::chrono::duration<double> frame_target_time
+ = std::chrono::seconds(1) / fps;
+ //! Fixed delta time for fixed updates in seconds
+ std::chrono::duration<double> fixed_delta_time
+ = std::chrono::seconds(1) / 50;
+ //! Total elapsed game time in seconds
+ std::chrono::duration<double> elapsed_time{0.0};
+ //! Total elapsed time for fixed updates in seconds
+ std::chrono::duration<double> elapsed_fixed_time{0.0};
+ //! Time of the last frame
+ std::chrono::steady_clock::time_point last_frame_time;
+};
+
+} // namespace crepe
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index c4c96e2..39d0d4d 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -191,3 +191,4 @@ int SDLContext::get_height(const Texture & ctx) const {
SDL_QueryTexture(ctx.texture.get(), NULL, NULL, NULL, &h);
return h;
}
+void SDLContext::delay(int ms) const { SDL_Delay(ms); }
diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h
index e358c21..536dec5 100644
--- a/src/crepe/facade/SDLContext.h
+++ b/src/crepe/facade/SDLContext.h
@@ -58,12 +58,23 @@ private:
private:
//! Will only use get_ticks
friend class AnimatorSystem;
-
+ //! Will only use delay
+ friend class LoopTimer;
/**
* \brief Gets the current SDL ticks since the program started.
* \return Current ticks in milliseconds as a constant uint64_t.
*/
uint64_t get_ticks() const;
+ /**
+ * \brief Pauses the execution for a specified duration.
+ *
+ * This function uses SDL's delay function to halt the program execution
+ * for a given number of milliseconds, allowing for frame rate control
+ * or other timing-related functionality.
+ *
+ * \param ms Duration of the delay in milliseconds.
+ */
+ void delay(int ms) const;
private:
/**
diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt
index 36f9d4d..c8a4b85 100644
--- a/src/example/CMakeLists.txt
+++ b/src/example/CMakeLists.txt
@@ -28,4 +28,5 @@ add_example(proxy)
add_example(db)
add_example(ecs)
add_example(scene_manager)
+add_example(gameloop)
diff --git a/src/example/gameloop.cpp b/src/example/gameloop.cpp
new file mode 100644
index 0000000..a676f20
--- /dev/null
+++ b/src/example/gameloop.cpp
@@ -0,0 +1,7 @@
+#include "crepe/api/LoopManager.h"
+using namespace crepe;
+int main() {
+ LoopManager gameloop;
+ gameloop.start();
+ return 1;
+}
diff --git a/src/makefile b/src/makefile
index b9c44c8..bd05b2e 100644
--- a/src/makefile
+++ b/src/makefile
@@ -94,11 +94,10 @@ LOEK += example/script.cpp
LOEK += test/audio.cpp
LOEK += test/dummy.cpp
JARO += test/PhysicsTest.cpp
-
-FMT := $(JARO) #<<< CHANGE THIS TO YOUR NAME FOR STEP 2
+FMT := $(shell git ls-files '*.c' '*.cpp' '*.h' '*.hpp')
format: FORCE
clang-tidy -p build/compile_commands.json --fix-errors $(FMT)
-# FMT += $(shell git ls-files '*.c' '*.cpp' '*.h' '*.hpp')
+# FMT := $(shell git ls-files '*.c' '*.cpp' '*.h' '*.hpp')
# TODO: re-enable linter after all corrections