aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format1
-rw-r--r--.gitignore2
-rw-r--r--contributing.md93
-rw-r--r--mwe/ecs-homemade/src/Components.cpp4
-rw-r--r--mwe/ecs-homemade/src/GameObjectMax.cpp6
-rw-r--r--mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp3
-rw-r--r--mwe/ecs-memory-efficient/src/Components.cpp4
-rw-r--r--mwe/ecs-memory-efficient/src/GameObjectMax.cpp6
-rw-r--r--mwe/events/include/customTypes.h4
-rw-r--r--mwe/events/include/eventHandler.h3
-rw-r--r--mwe/events/src/event.cpp31
-rw-r--r--mwe/events/src/main.cpp4
-rw-r--r--mwe/events/src/uiObject.cpp13
-rw-r--r--mwe/gameloop/src/gameObject.cpp7
-rw-r--r--src/crepe/ValueBroker.hpp3
-rw-r--r--src/crepe/api/Animator.cpp5
-rw-r--r--src/crepe/api/AssetManager.h35
-rw-r--r--src/crepe/api/CMakeLists.txt4
-rw-r--r--src/crepe/api/Camera.cpp3
-rw-r--r--src/crepe/api/CircleCollider.h3
-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/api/Metadata.cpp4
-rw-r--r--src/crepe/api/Rigidbody.cpp3
-rw-r--r--src/crepe/api/Sprite.cpp5
-rw-r--r--src/crepe/api/Transform.cpp5
-rw-r--r--src/crepe/facade/SDLContext.cpp3
-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/example/rendering.cpp11
-rw-r--r--src/makefile101
34 files changed, 612 insertions, 142 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
diff --git a/.gitignore b/.gitignore
index 180afb8..8bfb819 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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 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/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/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 722ffea..3a5b543 100644
--- a/src/example/CMakeLists.txt
+++ b/src/example/CMakeLists.txt
@@ -29,4 +29,5 @@ add_example(db)
add_example(ecs)
add_example(scene_manager)
add_example(particles)
+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 356179e..5f80204 100644
--- a/src/makefile
+++ b/src/makefile
@@ -1,105 +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
-JARO += crepe/Particle.cpp
-JARO += 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
-JARO += crepe/api/ParticleEmitter.cpp
-JARO += 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
-JARO += crepe/system/ParticleSystem.cpp
-JARO += 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
-JARO += example/particles.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
-JARO += test/ParticleTest.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')
-# TODO: re-enable linter after all corrections
-