diff options
author | WBoerenkamps <wrj.boerenkamps@student.avans.nl> | 2024-11-13 15:44:46 +0100 |
---|---|---|
committer | WBoerenkamps <wrj.boerenkamps@student.avans.nl> | 2024-11-13 15:44:46 +0100 |
commit | 2d07c0dc6bec17ea9f60d4c22669456f6aad52d7 (patch) | |
tree | 629d03f93390fec099840b563922fc99387f2278 | |
parent | 9ce53b197953e66189febeaa434255b848647993 (diff) | |
parent | f2509e89c02894ebd3ad992324eb300103621d26 (diff) |
Merge branch 'master' of https://github.com/lonkaars/crepe into wouter/events
35 files changed, 616 insertions, 159 deletions
diff --git a/.clang-format b/.clang-format index 3ae6c28..8ce4033 100644 --- a/.clang-format +++ b/.clang-format @@ -23,6 +23,7 @@ ReflowComments: false AlignEscapedNewlines: DontAlign BreakBeforeBinaryOperators: All AlwaysBreakTemplateDeclarations: Yes +PackConstructorInitializers: CurrentLine ... # vim: ft=yaml @@ -15,4 +15,4 @@ compile_commands.json CTestTestfile.cmake _deps CMakeUserPresets.json - +compile.sh diff --git a/contributing.md b/contributing.md index 8799057..3090727 100644 --- a/contributing.md +++ b/contributing.md @@ -54,8 +54,6 @@ that you can click on to open them. - Implementation details (if applicable) - Header files (`.h`) contain the following types of comments: - [Usage documentation](#documentation) (required) - > [!NOTE] - > Constructors/destructors aren't required to have a `\brief` description - Implementation details (if they affect the header) - Design/data structure decisions (if applicable) - <details><summary> @@ -436,7 +434,8 @@ that you can click on to open them. ``` </td></tr></table></details> - <details><summary> - Variables that are being moved always use the fully qualified <code>std::move</code> + Variables that are being moved always use the fully qualified + <code>std::move</code> </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td> ```cpp @@ -652,6 +651,33 @@ that you can click on to open them. bool bar = 0; ``` </td></tr></table></details> +- <details><summary> + The reason for <code>friend</code> relations are documented + </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td> + + ```cpp + //! ComponentManager calls the private constructor of this class + friend class ComponentManager; + ``` + </td><td> + + ```cpp + friend class ComponentManager; + ``` + </td></tr></table></details> +- <details><summary> + Do not <i>pick</i> fixed-width integer types (unless required) + </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td> + + ```cpp + unsigned long long foo(); + ``` + </td><td> + + ```cpp + uint64_t foo(); + ``` + </td></tr></table></details> ## CMakeLists-specific @@ -702,6 +728,67 @@ that you can click on to open them. void foo(); ``` </td></tr></table></details> +- <details><summary> + The default constructor and destructor aren't required to have a + <code>\brief</code> description + </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td> + + ```cpp + Foo(); + virtual ~Foo(); + ``` + </td><td> + + ```cpp + //! Create instance of Foo + Foo(); + //! Destroy instance of Foo + virtual ~Foo(); + ``` + </td></tr></table></details> +- <details><summary> + Parameter direction shouldn't be specified using Doxygen + </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td> + + ```cpp + /** + * \param bar Reference to Bar + */ + void foo(const Bar & bar); + ``` + </td><td> + + ```cpp + /** + * \param[in] bar Reference to Bar + */ + void foo(const Bar & bar); + ``` + </td></tr></table></details> +- <details><summary> + Deleted functions shouldn't have Doxygen comments + </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td> + + ```cpp + // singleton + Foo(const Foo &) = delete; + Foo(Foo &&) = delete; + Foo & operator=(const Foo &) = delete; + Foo & operator=(Foo &&) = delete; + ``` + </td><td> + + ```cpp + //! Deleted copy constructor + Foo(const Foo &) = delete; + //! Deleted move constructor + Foo(Foo &&) = delete; + //! Deleted copy assignment operator + Foo & operator=(const Foo &) = delete; + //! Deleted move assignment operator + Foo & operator=(Foo &&) = delete; + ``` + </td></tr></table></details> # Libraries diff --git a/mwe/ecs-homemade/src/Components.cpp b/mwe/ecs-homemade/src/Components.cpp index de8753e..0d62bd5 100644 --- a/mwe/ecs-homemade/src/Components.cpp +++ b/mwe/ecs-homemade/src/Components.cpp @@ -6,7 +6,9 @@ Component::Component() : mActive(true) {} Sprite::Sprite(std::string path) : mPath(path) {} Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) - : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {} + : mMass(mass), + mGravityScale(gravityScale), + mBodyType(bodyType) {} Colider::Colider(int size) : mSize(size) {} diff --git a/mwe/ecs-homemade/src/GameObjectMax.cpp b/mwe/ecs-homemade/src/GameObjectMax.cpp index b0c5af7..753c8e2 100644 --- a/mwe/ecs-homemade/src/GameObjectMax.cpp +++ b/mwe/ecs-homemade/src/GameObjectMax.cpp @@ -4,4 +4,8 @@ GameObject::GameObject(std::uint32_t id, std::string name, std::string tag, int layer) - : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {} + : mId(id), + mName(name), + mTag(tag), + mActive(true), + mLayer(layer) {} diff --git a/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp b/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp index 408d5aa..ff8fde4 100644 --- a/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp +++ b/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp @@ -1,5 +1,6 @@ template <typename T> -ContiguousContainer<T>::ContiguousContainer() : mSize(0), mCapacity(10) { +ContiguousContainer<T>::ContiguousContainer() : mSize(0), + mCapacity(10) { // Allocate memory for 10 objects initially mData = static_cast<T *>(malloc(mCapacity * sizeof(T))); if (!mData) { diff --git a/mwe/ecs-memory-efficient/src/Components.cpp b/mwe/ecs-memory-efficient/src/Components.cpp index c8347b3..2ec8609 100644 --- a/mwe/ecs-memory-efficient/src/Components.cpp +++ b/mwe/ecs-memory-efficient/src/Components.cpp @@ -6,6 +6,8 @@ Component::Component() : mActive(true) {} Sprite::Sprite(std::string path) : mPath(path) {} Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) - : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {} + : mMass(mass), + mGravityScale(gravityScale), + mBodyType(bodyType) {} Colider::Colider(int size) : mSize(size) {} diff --git a/mwe/ecs-memory-efficient/src/GameObjectMax.cpp b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp index b0c5af7..753c8e2 100644 --- a/mwe/ecs-memory-efficient/src/GameObjectMax.cpp +++ b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp @@ -4,4 +4,8 @@ GameObject::GameObject(std::uint32_t id, std::string name, std::string tag, int layer) - : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {} + : mId(id), + mName(name), + mTag(tag), + mActive(true), + mLayer(layer) {} diff --git a/mwe/events/include/customTypes.h b/mwe/events/include/customTypes.h index a5d8dc9..415b989 100644 --- a/mwe/events/include/customTypes.h +++ b/mwe/events/include/customTypes.h @@ -33,6 +33,8 @@ struct Collision { // Constructor to initialize a Collision Collision(int idA, int idB, const Vector2 & point, const Vector2 & normal, float depth) - : objectIdA(idA), objectIdB(idB), contactPoint(point), + : objectIdA(idA), + objectIdB(idB), + contactPoint(point), contactNormal(normal) {} }; diff --git a/mwe/events/include/eventHandler.h b/mwe/events/include/eventHandler.h index aa8f63b..3a83b15 100644 --- a/mwe/events/include/eventHandler.h +++ b/mwe/events/include/eventHandler.h @@ -24,7 +24,8 @@ class EventHandlerWrapper : public IEventHandlerWrapper { public: explicit EventHandlerWrapper(const EventHandler<EventType> & handler, const bool destroyOnSuccess = false) - : m_handler(handler), m_handlerType(m_handler.target_type().name()), + : m_handler(handler), + m_handlerType(m_handler.target_type().name()), m_destroyOnSuccess(destroyOnSuccess) { // std::cout << m_handlerType << std::endl; } diff --git a/mwe/events/src/event.cpp b/mwe/events/src/event.cpp index 0c9f3ed..8ffa0b1 100644 --- a/mwe/events/src/event.cpp +++ b/mwe/events/src/event.cpp @@ -23,7 +23,9 @@ void Event::markHandled() { isHandled = true; } // KeyPressedEvent class methods KeyPressedEvent::KeyPressedEvent(int keycode) - : Event("KeyPressedEvent"), key(keycode), repeatCount(0) {} + : Event("KeyPressedEvent"), + key(keycode), + repeatCount(0) {} Keycode KeyPressedEvent::getKeyCode() const { return key; } @@ -31,13 +33,16 @@ int KeyPressedEvent::getRepeatCount() const { return repeatCount; } // KeyReleasedEvent class methods KeyReleasedEvent::KeyReleasedEvent(int keycode) - : Event("KeyReleasedEvent"), key(keycode) {} + : Event("KeyReleasedEvent"), + key(keycode) {} Keycode KeyReleasedEvent::getKeyCode() const { return key; } // MousePressedEvent class methods MousePressedEvent::MousePressedEvent(int mouseX, int mouseY) - : Event("MousePressedEvent"), mouseX(mouseX), mouseY(mouseY) {} + : Event("MousePressedEvent"), + mouseX(mouseX), + mouseY(mouseY) {} std::pair<int, int> MousePressedEvent::getMousePosition() const { return {mouseX, mouseY}; @@ -45,26 +50,36 @@ std::pair<int, int> MousePressedEvent::getMousePosition() const { //Collision event CollisionEvent::CollisionEvent(Collision collision) - : collisionData(collision), Event("CollisionEvent") {} + : collisionData(collision), + Event("CollisionEvent") {} Collision CollisionEvent::getCollisionData() const { return this->collisionData; } TextSubmitEvent::TextSubmitEvent(std::string text) - : text(text), Event("TextSubmitEvent") {} + : text(text), + Event("TextSubmitEvent") {} std::string TextSubmitEvent::getText() const { return this->text; } MouseReleasedEvent::MouseReleasedEvent(int x, int y, MouseButton button) - : mouseX(x), mouseY(y), button(button), Event("MouseReleased") {} + : mouseX(x), + mouseY(y), + button(button), + Event("MouseReleased") {} std::pair<int, int> MouseReleasedEvent::getMousePosition() const { return {mouseX, mouseY}; } MouseClickEvent::MouseClickEvent(int x, int y, MouseButton button) - : mouseX(x), mouseY(y), button(button), Event("MouseClickEvent") {} + : mouseX(x), + mouseY(y), + button(button), + Event("MouseClickEvent") {} MouseMovedEvent::MouseMovedEvent(int x, int y) - : mouseX(x), mouseY(y), Event("MouseMovedEvent") {} + : mouseX(x), + mouseY(y), + Event("MouseMovedEvent") {} std::pair<int, int> MouseClickEvent::getMousePosition() const { return {mouseX, mouseY}; } diff --git a/mwe/events/src/main.cpp b/mwe/events/src/main.cpp index d49cf74..f4e7390 100644 --- a/mwe/events/src/main.cpp +++ b/mwe/events/src/main.cpp @@ -11,7 +11,9 @@ class PlayerDamagedEvent : public Event { public: PlayerDamagedEvent(int damage, int playerID) - : Event("PlayerDamaged"), damage(damage), playerID(playerID) {} + : Event("PlayerDamaged"), + damage(damage), + playerID(playerID) {} REGISTER_EVENT_TYPE(PlayerDamagedEvent); diff --git a/mwe/events/src/uiObject.cpp b/mwe/events/src/uiObject.cpp index 8405469..947d1a2 100644 --- a/mwe/events/src/uiObject.cpp +++ b/mwe/events/src/uiObject.cpp @@ -7,7 +7,9 @@ UIObject::UIObject(int width, int height) : width(width), height(height) {} Button::Button(int width, int height) : UIObject(width, height) {} Text::Text(int width, int height) - : UIObject(width, height), size(12), font(nullptr), + : UIObject(width, height), + size(12), + font(nullptr), color{255, 255, 255} { // Default size and color alignment.horizontal = Alignment::Horizontal::CENTER; alignment.vertical = Alignment::Vertical::MIDDLE; @@ -15,8 +17,13 @@ Text::Text(int width, int height) } TextInput::TextInput(int width, int height) - : UIObject(width, height), textBuffer(""), placeholder(""), isActive(false), - textColor{255, 255, 255}, backgroundColor{0, 0, 0}, maxLength(100), + : UIObject(width, height), + textBuffer(""), + placeholder(""), + isActive(false), + textColor{255, 255, 255}, + backgroundColor{0, 0, 0}, + maxLength(100), font(nullptr) { alignment.horizontal = Alignment::Horizontal::LEFT; alignment.vertical = Alignment::Vertical::TOP; diff --git a/mwe/gameloop/src/gameObject.cpp b/mwe/gameloop/src/gameObject.cpp index 78217c4..b33dc78 100644 --- a/mwe/gameloop/src/gameObject.cpp +++ b/mwe/gameloop/src/gameObject.cpp @@ -26,5 +26,10 @@ void GameObject::setVelY(float value) { velY = value; } GameObject::GameObject(std::string name, float x, float y, float width, float height, float velX, float velY) - : name(name), x(x), y(y), width(width), height(height), velX(velX), + : name(name), + x(x), + y(y), + width(width), + height(height), + velX(velX), velY(velY) {} diff --git a/src/crepe/ValueBroker.hpp b/src/crepe/ValueBroker.hpp index 927142f..5c3bed9 100644 --- a/src/crepe/ValueBroker.hpp +++ b/src/crepe/ValueBroker.hpp @@ -6,7 +6,8 @@ namespace crepe { template <typename T> ValueBroker<T>::ValueBroker(const setter_t & setter, const getter_t & getter) - : setter(setter), getter(getter) {} + : setter(setter), + getter(getter) {} template <typename T> const T & ValueBroker<T>::get() { diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp index 8b396af..58fee2a 100644 --- a/src/crepe/api/Animator.cpp +++ b/src/crepe/api/Animator.cpp @@ -10,7 +10,10 @@ using namespace crepe; Animator::Animator(uint32_t id, Sprite & ss, int row, int col, int col_animator) - : Component(id), spritesheet(ss), row(row), col(col) { + : Component(id), + spritesheet(ss), + row(row), + col(col) { dbg_trace(); animator_rect = spritesheet.sprite_rect; diff --git a/src/crepe/api/AssetManager.h b/src/crepe/api/AssetManager.h index fefbed9..dbfaef3 100644 --- a/src/crepe/api/AssetManager.h +++ b/src/crepe/api/AssetManager.h @@ -7,9 +7,19 @@ namespace crepe { +/** + * \brief The AssetManager is responsible for storing and managing assets over + * multiple scenes. + * + * The AssetManager ensures that assets are loaded once and can be accessed + * across different scenes. It caches assets to avoid reloading them every time + * a scene is loaded. Assets are retained in memory until the AssetManager is + * destroyed, at which point the cached assets are cleared. + */ class AssetManager { private: + //! A cache that holds all the assets, accessible by their file path, over multiple scenes. std::unordered_map<std::string, std::any> asset_cache; private: @@ -22,12 +32,31 @@ public: AssetManager & operator=(const AssetManager &) = delete; AssetManager & operator=(AssetManager &&) = delete; + /** + * \brief Retrieves the singleton instance of the AssetManager. + * + * \return A reference to the single instance of the AssetManager. + */ static AssetManager & get_instance(); public: - template <typename asset> - std::shared_ptr<asset> cache(const std::string & file_path, - bool reload = false); + /** + * \brief Caches an asset by loading it from the given file path. + * + * \param file_path The path to the asset file to load. + * \param reload If true, the asset will be reloaded from the file, even if + * it is already cached. + * \tparam T The type of asset to cache (e.g., texture, sound, etc.). + * + * \return A shared pointer to the cached asset. + * + * This template function caches the asset at the given file path. If the + * asset is already cached and `reload` is false, the existing cached version + * will be returned. Otherwise, the asset will be reloaded and added to the + * cache. + */ + template <typename T> + std::shared_ptr<T> cache(const std::string & file_path, bool reload = false); }; } // namespace crepe diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 4e8f8a0..87cbb09 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -18,10 +18,6 @@ target_sources(crepe PUBLIC Vector2.cpp Camera.cpp Animator.cpp - EventManager.cpp - EventHandler.cpp - IKeyListener.cpp - IMouseListener.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -46,9 +42,4 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES SceneManager.hpp Camera.h Animator.h - EventManager.h - EventHandler.h - Event.h - IKeyListener.h - IMouseListener.h ) diff --git a/src/crepe/api/Camera.cpp b/src/crepe/api/Camera.cpp index 820a6a8..6355a03 100644 --- a/src/crepe/api/Camera.cpp +++ b/src/crepe/api/Camera.cpp @@ -10,7 +10,8 @@ using namespace crepe; Camera::Camera(uint32_t id, const Color & bg_color) - : Component(id), bg_color(bg_color) { + : Component(id), + bg_color(bg_color) { dbg_trace(); } diff --git a/src/crepe/api/CircleCollider.h b/src/crepe/api/CircleCollider.h index caa7e43..e77a592 100644 --- a/src/crepe/api/CircleCollider.h +++ b/src/crepe/api/CircleCollider.h @@ -6,7 +6,8 @@ namespace crepe { class CircleCollider : public Collider { public: CircleCollider(game_object_id_t game_object_id, int radius) - : Collider(game_object_id), radius(radius) {} + : Collider(game_object_id), + radius(radius) {} int radius; }; 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/api/Metadata.cpp b/src/crepe/api/Metadata.cpp index 76f11d7..d421de5 100644 --- a/src/crepe/api/Metadata.cpp +++ b/src/crepe/api/Metadata.cpp @@ -4,4 +4,6 @@ using namespace crepe; using namespace std; Metadata::Metadata(game_object_id_t id, const string & name, const string & tag) - : Component(id), name(name), tag(tag) {} + : Component(id), + name(name), + tag(tag) {} diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp index 3b2e2f2..0bc2197 100644 --- a/src/crepe/api/ParticleEmitter.cpp +++ b/src/crepe/api/ParticleEmitter.cpp @@ -11,9 +11,14 @@ ParticleEmitter::ParticleEmitter(game_object_id_t id, uint32_t max_particles, uint32_t speed_offset, uint32_t angle, uint32_t angleOffset, float begin_lifespan, float end_lifespan) - : Component(id), max_particles(max_particles), emission_rate(emission_rate), - speed(speed), speed_offset(speed_offset), position{0, 0}, - begin_lifespan(begin_lifespan), end_lifespan(end_lifespan) { + : Component(id), + max_particles(max_particles), + emission_rate(emission_rate), + speed(speed), + speed_offset(speed_offset), + position{0, 0}, + begin_lifespan(begin_lifespan), + end_lifespan(end_lifespan) { std::srand( static_cast<uint32_t>(std::time(nullptr))); // initialize random seed std::cout << "Create emitter" << std::endl; diff --git a/src/crepe/api/Rigidbody.cpp b/src/crepe/api/Rigidbody.cpp index cbf1325..3bf1c5b 100644 --- a/src/crepe/api/Rigidbody.cpp +++ b/src/crepe/api/Rigidbody.cpp @@ -3,7 +3,8 @@ using namespace crepe; crepe::Rigidbody::Rigidbody(uint32_t game_object_id, const Data & data) - : Component(game_object_id), data(data) {} + : Component(game_object_id), + data(data) {} void crepe::Rigidbody::add_force_linear(const Vector2 & force) { this->data.linear_velocity += force; diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp index f9cd761..6f0433f 100644 --- a/src/crepe/api/Sprite.cpp +++ b/src/crepe/api/Sprite.cpp @@ -12,7 +12,10 @@ using namespace crepe; Sprite::Sprite(game_object_id_t id, const shared_ptr<Texture> image, const Color & color, const FlipSettings & flip) - : Component(id), color(color), flip(flip), sprite_image(image) { + : Component(id), + color(color), + flip(flip), + sprite_image(image) { dbg_trace(); this->sprite_rect.w = sprite_image->get_width(); diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp index a244bc5..e401120 100644 --- a/src/crepe/api/Transform.cpp +++ b/src/crepe/api/Transform.cpp @@ -6,6 +6,9 @@ using namespace crepe; Transform::Transform(game_object_id_t id, const Vector2 & point, double rotation, double scale) - : Component(id), position(point), rotation(rotation), scale(scale) { + : Component(id), + position(point), + rotation(rotation), + scale(scale) { dbg_trace(); } diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index c4c96e2..46230b4 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -159,7 +159,7 @@ SDLContext::texture_from_path(const std::string & path) { SDL_Surface * tmp = IMG_Load(path.c_str()); if (tmp == nullptr) { - throw Exception("surface cannot be load from %s", path.c_str()); + tmp = IMG_Load("../asset/texture/ERROR.png"); } std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> @@ -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 da6ceba..bf12ff3 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -29,3 +29,5 @@ add_example(db) add_example(ecs) add_example(scene_manager) add_example(events) +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/example/rendering.cpp b/src/example/rendering.cpp index d554a8a..e02f6a3 100644 --- a/src/example/rendering.cpp +++ b/src/example/rendering.cpp @@ -1,3 +1,4 @@ +#include "api/Camera.h" #include <crepe/ComponentManager.h> #include <crepe/api/GameObject.h> #include <crepe/system/RenderSystem.h> @@ -23,26 +24,26 @@ int main() { auto obj1 = GameObject(1, "name", "tag", Vector2{500, 0}, 1, 0.1); auto obj2 = GameObject(2, "name", "tag", Vector2{800, 0}, 1, 0.1); - auto & mgr = AssetManager::get_instance(); // Normal adding components { Color color(0, 0, 0, 0); obj.add_component<Sprite>( make_shared<Texture>("../asset/texture/img.png"), color, - FlipSettings{true, true}); + FlipSettings{false, false}); + obj.add_component<Camera>(Color::get_red()); } - { Color color(0, 0, 0, 0); - auto img = mgr.cache<Texture>("../asset/texture/second.png"); - obj1.add_component<Sprite>(img, color, FlipSettings{true, true}); + obj1.add_component<Sprite>(make_shared<Texture>("../asset/texture/second.png"), color, FlipSettings{true, true}); } + /* { Color color(0, 0, 0, 0); auto img = mgr.cache<Texture>("../asset/texture/second.png"); obj2.add_component<Sprite>(img, color, FlipSettings{true, true}); } + */ auto & sys = crepe::RenderSystem::get_instance(); auto start = std::chrono::steady_clock::now(); diff --git a/src/makefile b/src/makefile index f6907f6..5f80204 100644 --- a/src/makefile +++ b/src/makefile @@ -1,111 +1,6 @@ .PHONY: FORCE -# STEPS FOR BIG CLEANUP -# -# 1. Change TODO to your name (in capitals) for each file in the list below -# that is yours (or you are going to fix) -# 2. Update the name between parentheses below this list (see comment) to your -# name -# 3. Create a git commit at this point (ensure `git status` reports "working -# tree clean") -# 4. Run `make format` in the REPOSITORY ROOT DIRECTORY (NOT HERE), and start -# fixing reported errors or miscorrections manually until everything works -# again. -# 5. Once everything is working again, create another git commit, and create a -# pull request. Make sure to ask someone to review the code standards for -# each ENTIRE FILE in this pull request. - -LOEK += crepe/Asset.cpp -LOEK += crepe/Asset.h -TODO += crepe/Collider.cpp -TODO += crepe/Collider.h -MAX += crepe/Component.cpp -MAX += crepe/Component.h -MAX += crepe/ComponentManager.cpp -MAX += crepe/ComponentManager.h -MAX += crepe/ComponentManager.hpp -MAX += crepe/api/Metadata.cpp -MAX += crepe/api/Metadata.h -TODO += crepe/Particle.cpp -TODO += crepe/Particle.h -TODO += crepe/Position.h -TODO += crepe/api/AssetManager.cpp -TODO += crepe/api/AssetManager.h -TODO += crepe/api/AssetManager.hpp -LOEK += crepe/api/AudioSource.cpp -LOEK += crepe/api/AudioSource.h -LOEK += crepe/api/BehaviorScript.cpp -LOEK += crepe/api/BehaviorScript.h -LOEK += crepe/api/BehaviorScript.hpp -TODO += crepe/api/CircleCollider.h -TODO += crepe/api/Color.cpp -TODO += crepe/api/Color.h -LOEK += crepe/api/Config.h -MAX += crepe/api/GameObject.cpp -MAX += crepe/api/GameObject.h -MAX += crepe/api/GameObject.hpp -TODO += crepe/api/ParticleEmitter.cpp -TODO += crepe/api/ParticleEmitter.h -TODO += crepe/api/Vector2.h -TODO += crepe/api/Vector2.cpp -JARO += crepe/api/Rigidbody.cpp -JARO += crepe/api/Rigidbody.h -LOEK += crepe/api/Script.cpp -LOEK += crepe/api/Script.h -LOEK += crepe/api/Script.hpp -TODO += crepe/api/Sprite.cpp -TODO += crepe/api/Sprite.h -TODO += crepe/api/Texture.cpp -TODO += crepe/api/Texture.h -MAX += crepe/api/Transform.cpp -MAX += crepe/api/Transform.h -TODO += crepe/facade/SDLContext.cpp -TODO += crepe/facade/SDLContext.h -LOEK += crepe/facade/Sound.cpp -LOEK += crepe/facade/Sound.h -LOEK += crepe/facade/SoundContext.cpp -LOEK += crepe/facade/SoundContext.h -TODO += crepe/system/CollisionSystem.cpp -TODO += crepe/system/CollisionSystem.h -TODO += crepe/system/ParticleSystem.cpp -TODO += crepe/system/ParticleSystem.h -JARO += crepe/system/PhysicsSystem.cpp -JARO += crepe/system/PhysicsSystem.h -TODO += crepe/system/RenderSystem.cpp -TODO += crepe/system/RenderSystem.h -LOEK += crepe/system/ScriptSystem.cpp -LOEK += crepe/system/ScriptSystem.h -LOEK += crepe/system/System.h -LOEK += crepe/util/LogColor.cpp -LOEK += crepe/util/LogColor.h -LOEK += crepe/util/fmt.cpp -LOEK += crepe/util/fmt.h -LOEK += crepe/util/log.cpp -LOEK += crepe/util/log.h -TODO += example/asset_manager.cpp -LOEK += example/audio_internal.cpp -TODO += example/components_internal.cpp -MAX += example/ecs.cpp -LOEK += example/log.cpp -TODO += example/particle.cpp -JARO += example/physics.cpp -TODO += example/rendering.cpp -LOEK += example/script.cpp -LOEK += test/audio.cpp -LOEK += test/dummy.cpp -JARO += test/PhysicsTest.cpp -WOUTER += crepe/api/Event.h -WOUTER += crepe/api/EventHandler.cpp -WOUTER += crepe/api/EventHandler.h -WOUTER += crepe/api/EventManager.cpp -WOUTER += crepe/api/EventManager.h -WOUTER += crepe/api/IKeyListener.cpp -WOUTER += crepe/api/IKeyListener.h -WOUTER += crepe/api/IMouseListener.cpp -WOUTER += crepe/api/IMouseListener.h FMT := $(shell git ls-files '*.c' '*.cpp' '*.h' '*.hpp') format: FORCE clang-tidy -p build/compile_commands.json --fix-errors $(FMT) -# TODO: re-enable linter after all corrections - |