aboutsummaryrefslogtreecommitdiff
path: root/src/crepe
diff options
context:
space:
mode:
Diffstat (limited to 'src/crepe')
-rw-r--r--src/crepe/Particle.cpp5
-rw-r--r--src/crepe/Particle.h4
-rw-r--r--src/crepe/api/AI.cpp33
-rw-r--r--src/crepe/api/AI.h12
-rw-r--r--src/crepe/api/Animator.cpp8
-rw-r--r--src/crepe/api/Animator.h13
-rw-r--r--src/crepe/api/Asset.cpp2
-rw-r--r--src/crepe/api/AudioSource.h2
-rw-r--r--src/crepe/api/BoxCollider.cpp5
-rw-r--r--src/crepe/api/BoxCollider.h5
-rw-r--r--src/crepe/api/Button.cpp7
-rw-r--r--src/crepe/api/Button.h18
-rw-r--r--src/crepe/api/Camera.cpp5
-rw-r--r--src/crepe/api/Camera.h6
-rw-r--r--src/crepe/api/CircleCollider.cpp5
-rw-r--r--src/crepe/api/CircleCollider.h5
-rw-r--r--src/crepe/api/Color.cpp18
-rw-r--r--src/crepe/api/Color.h2
-rw-r--r--src/crepe/api/Config.h2
-rw-r--r--src/crepe/api/Engine.cpp13
-rw-r--r--src/crepe/api/Engine.h18
-rw-r--r--src/crepe/api/Event.h43
-rw-r--r--src/crepe/api/GameObject.cpp12
-rw-r--r--src/crepe/api/GameObject.h6
-rw-r--r--src/crepe/api/ParticleEmitter.cpp7
-rw-r--r--src/crepe/api/Rigidbody.h44
-rw-r--r--src/crepe/api/Scene.cpp6
-rw-r--r--src/crepe/api/Scene.h9
-rw-r--r--src/crepe/api/Script.cpp16
-rw-r--r--src/crepe/api/Script.h67
-rw-r--r--src/crepe/api/Script.hpp11
-rw-r--r--src/crepe/api/Sprite.cpp13
-rw-r--r--src/crepe/api/Sprite.h20
-rw-r--r--src/crepe/api/Text.cpp21
-rw-r--r--src/crepe/api/Text.h32
-rw-r--r--src/crepe/api/Transform.cpp2
-rw-r--r--src/crepe/api/UIObject.h2
-rw-r--r--src/crepe/api/Vector2.h3
-rw-r--r--src/crepe/api/Vector2.hpp5
-rw-r--r--src/crepe/facade/FontFacade.cpp5
-rw-r--r--src/crepe/facade/SDLContext.cpp130
-rw-r--r--src/crepe/facade/SDLContext.h30
-rw-r--r--src/crepe/facade/Texture.cpp6
-rw-r--r--src/crepe/manager/ComponentManager.cpp22
-rw-r--r--src/crepe/manager/ComponentManager.h7
-rw-r--r--src/crepe/manager/ComponentManager.hpp20
-rw-r--r--src/crepe/manager/EventManager.h6
-rw-r--r--src/crepe/manager/EventManager.hpp21
-rw-r--r--src/crepe/manager/LoopTimerManager.cpp6
-rw-r--r--src/crepe/manager/LoopTimerManager.h12
-rw-r--r--src/crepe/manager/ResourceManager.hpp13
-rw-r--r--src/crepe/manager/SceneManager.cpp10
-rw-r--r--src/crepe/manager/SystemManager.cpp14
-rw-r--r--src/crepe/manager/SystemManager.h8
-rw-r--r--src/crepe/manager/SystemManager.hpp11
-rw-r--r--src/crepe/system/AISystem.cpp21
-rw-r--r--src/crepe/system/AnimatorSystem.cpp30
-rw-r--r--src/crepe/system/AudioSystem.cpp2
-rw-r--r--src/crepe/system/CollisionSystem.cpp887
-rw-r--r--src/crepe/system/CollisionSystem.h306
-rw-r--r--src/crepe/system/InputSystem.cpp94
-rw-r--r--src/crepe/system/InputSystem.h33
-rw-r--r--src/crepe/system/ParticleSystem.cpp19
-rw-r--r--src/crepe/system/RenderSystem.cpp70
-rw-r--r--src/crepe/system/RenderSystem.h30
-rw-r--r--src/crepe/system/ScriptSystem.cpp19
-rw-r--r--src/crepe/system/ScriptSystem.h23
-rw-r--r--src/crepe/util/AbsolutePosition.cpp20
-rw-r--r--src/crepe/util/AbsolutePosition.h29
-rw-r--r--src/crepe/util/CMakeLists.txt2
-rw-r--r--src/crepe/util/dbg.h11
71 files changed, 1427 insertions, 997 deletions
diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp
index b340826..27fa97f 100644
--- a/src/crepe/Particle.cpp
+++ b/src/crepe/Particle.cpp
@@ -2,8 +2,9 @@
using namespace crepe;
-void Particle::reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity,
- float angle) {
+void Particle::reset(
+ unsigned int lifespan, const vec2 & position, const vec2 & velocity, float angle
+) {
// Initialize the particle state
this->time_in_life = 0;
this->lifespan = lifespan;
diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h
index ee0cd66..c013de5 100644
--- a/src/crepe/Particle.h
+++ b/src/crepe/Particle.h
@@ -41,8 +41,8 @@ public:
* \param velocity The initial velocity of the particle.
* \param angle The angle of the particle's trajectory or orientation.
*/
- void reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity,
- float angle);
+ void
+ reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity, float angle);
/**
* \brief Updates the particle's state.
*
diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp
index 2195249..2fedaf4 100644
--- a/src/crepe/api/AI.cpp
+++ b/src/crepe/api/AI.cpp
@@ -8,8 +8,9 @@ namespace crepe {
AI::AI(game_object_id_t id, float max_force) : Component(id), max_force(max_force) {}
-void AI::make_circle_path(float radius, const vec2 & center, float start_angle,
- bool clockwise) {
+void AI::make_circle_path(
+ float radius, const vec2 & center, float start_angle, bool clockwise
+) {
if (radius <= 0) {
throw std::runtime_error("Radius must be greater than 0");
}
@@ -25,19 +26,25 @@ void AI::make_circle_path(float radius, const vec2 & center, float start_angle,
if (clockwise) {
for (float i = start_angle; i < 2 * M_PI + start_angle; i += step) {
- path.push_back(vec2{static_cast<float>(center.x + radius * cos(i)),
- static_cast<float>(center.y + radius * sin(i))});
+ path.push_back(vec2 {
+ static_cast<float>(center.x + radius * cos(i)),
+ static_cast<float>(center.y + radius * sin(i))
+ });
}
} else {
for (float i = start_angle; i > start_angle - 2 * M_PI; i -= step) {
- path.push_back(vec2{static_cast<float>(center.x + radius * cos(i)),
- static_cast<float>(center.y + radius * sin(i))});
+ path.push_back(vec2 {
+ static_cast<float>(center.x + radius * cos(i)),
+ static_cast<float>(center.y + radius * sin(i))
+ });
}
}
}
-void AI::make_oval_path(float radius_x, float radius_y, const vec2 & center, float start_angle,
- bool clockwise, float rotation) {
+void AI::make_oval_path(
+ float radius_x, float radius_y, const vec2 & center, float start_angle, bool clockwise,
+ float rotation
+) {
if (radius_x <= 0 && radius_y <= 0) {
throw std::runtime_error("Radius must be greater than 0");
}
@@ -73,14 +80,16 @@ void AI::make_oval_path(float radius_x, float radius_y, const vec2 & center, flo
if (clockwise) {
for (float i = start_angle; i < 2 * M_PI + start_angle; i += step) {
- vec2 point = {static_cast<float>(center.x + radius_x * cos(i)),
- static_cast<float>(center.y + radius_y * sin(i))};
+ vec2 point
+ = {static_cast<float>(center.x + radius_x * cos(i)),
+ static_cast<float>(center.y + radius_y * sin(i))};
path.push_back(rotate_point(point, center));
}
} else {
for (float i = start_angle; i > start_angle - 2 * M_PI; i -= step) {
- vec2 point = {static_cast<float>(center.x + radius_x * cos(i)),
- static_cast<float>(center.y + radius_y * sin(i))};
+ vec2 point
+ = {static_cast<float>(center.x + radius_x * cos(i)),
+ static_cast<float>(center.y + radius_y * sin(i))};
path.push_back(rotate_point(point, center));
}
}
diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h
index c780a91..bee11b3 100644
--- a/src/crepe/api/AI.h
+++ b/src/crepe/api/AI.h
@@ -70,8 +70,10 @@ public:
* \param start_angle The start angle of the circle (in radians)
* \param clockwise The direction of the circle
*/
- void make_circle_path(float radius, const vec2 & center = {0, 0}, float start_angle = 0,
- bool clockwise = true);
+ void make_circle_path(
+ float radius, const vec2 & center = {0, 0}, float start_angle = 0,
+ bool clockwise = true
+ );
/**
* \brief Make an oval path (for the path following behavior)
*
@@ -84,8 +86,10 @@ public:
* \param clockwise The direction of the oval
* \param rotation The rotation of the oval (in radians)
*/
- void make_oval_path(float radius_x, float radius_y, const vec2 & center = {0, 0},
- float start_angle = 0, bool clockwise = true, float rotation = 0);
+ void make_oval_path(
+ float radius_x, float radius_y, const vec2 & center = {0, 0}, float start_angle = 0,
+ bool clockwise = true, float rotation = 0
+ );
public:
//! The maximum force that can be applied to the entity (higher values will make the entity adjust faster)
diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp
index 203cef3..c558d86 100644
--- a/src/crepe/api/Animator.cpp
+++ b/src/crepe/api/Animator.cpp
@@ -7,8 +7,10 @@
using namespace crepe;
-Animator::Animator(game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size,
- const uvec2 & grid_size, const Animator::Data & data)
+Animator::Animator(
+ game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size,
+ const uvec2 & grid_size, const Animator::Data & data
+)
: Component(id),
spritesheet(spritesheet),
grid_size(grid_size),
@@ -52,6 +54,6 @@ void Animator::set_anim(int col) {
void Animator::next_anim() {
Animator::Data & ctx = this->data;
- ctx.row = ctx.row++ % this->grid_size.x;
+ ctx.row = ++ctx.row % this->grid_size.x;
this->spritesheet.mask.x = ctx.row * this->spritesheet.mask.w;
}
diff --git a/src/crepe/api/Animator.h b/src/crepe/api/Animator.h
index 5918800..95539d3 100644
--- a/src/crepe/api/Animator.h
+++ b/src/crepe/api/Animator.h
@@ -1,5 +1,6 @@
#pragma once
+#include "../manager/LoopTimerManager.h"
#include "../types.h"
#include "Component.h"
@@ -83,8 +84,10 @@ public:
* This constructor sets up the Animator with the given parameters, and initializes the
* animation system.
*/
- Animator(game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size,
- const uvec2 & grid_size, const Animator::Data & data);
+ Animator(
+ game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size,
+ const uvec2 & grid_size, const Animator::Data & data
+ );
~Animator(); // dbg_trace
public:
@@ -97,6 +100,12 @@ private:
//! The maximum number of rows and columns inside the spritesheet
const uvec2 grid_size;
+ // the time elapsed from a frame duration
+ duration_t elapsed_time = {};
+
+ // frame counter
+ unsigned int frame = 0;
+
//! Uses the spritesheet
friend AnimatorSystem;
};
diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp
index e148367..bab82e7 100644
--- a/src/crepe/api/Asset.cpp
+++ b/src/crepe/api/Asset.cpp
@@ -50,5 +50,5 @@ string Asset::whereami() const noexcept {
bool Asset::operator==(const Asset & other) const noexcept { return this->src == other.src; }
size_t std::hash<const Asset>::operator()(const Asset & asset) const noexcept {
- return std::hash<string>{}(asset.get_path());
+ return std::hash<string> {}(asset.get_path());
};
diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h
index b20e490..eaa56e8 100644
--- a/src/crepe/api/AudioSource.h
+++ b/src/crepe/api/AudioSource.h
@@ -68,7 +68,7 @@ private:
typeof(loop) last_loop = loop;
//! \}
//! This source's voice handle
- SoundHandle voice{};
+ SoundHandle voice {};
};
} // namespace crepe
diff --git a/src/crepe/api/BoxCollider.cpp b/src/crepe/api/BoxCollider.cpp
index a893d41..f6b358d 100644
--- a/src/crepe/api/BoxCollider.cpp
+++ b/src/crepe/api/BoxCollider.cpp
@@ -4,7 +4,8 @@
using namespace crepe;
-BoxCollider::BoxCollider(game_object_id_t game_object_id, const vec2 & dimensions,
- const vec2 & offset)
+BoxCollider::BoxCollider(
+ game_object_id_t game_object_id, const vec2 & dimensions, const vec2 & offset
+)
: Collider(game_object_id, offset),
dimensions(dimensions) {}
diff --git a/src/crepe/api/BoxCollider.h b/src/crepe/api/BoxCollider.h
index d643e7f..229b90f 100644
--- a/src/crepe/api/BoxCollider.h
+++ b/src/crepe/api/BoxCollider.h
@@ -13,8 +13,9 @@ namespace crepe {
*/
class BoxCollider : public Collider {
public:
- BoxCollider(game_object_id_t game_object_id, const vec2 & dimensions,
- const vec2 & offset = {0, 0});
+ BoxCollider(
+ game_object_id_t game_object_id, const vec2 & dimensions, const vec2 & offset = {0, 0}
+ );
//! Width and height of the box collider
vec2 dimensions;
diff --git a/src/crepe/api/Button.cpp b/src/crepe/api/Button.cpp
index 40153c9..8eadd89 100644
--- a/src/crepe/api/Button.cpp
+++ b/src/crepe/api/Button.cpp
@@ -2,7 +2,10 @@
namespace crepe {
-Button::Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset)
- : UIObject(id, dimensions, offset) {}
+Button::Button(
+ game_object_id_t id, const vec2 & dimensions, const Data & data, const vec2 & offset
+)
+ : UIObject(id, dimensions, offset),
+ data(data) {}
} // namespace crepe
diff --git a/src/crepe/api/Button.h b/src/crepe/api/Button.h
index d42527e..e986c04 100644
--- a/src/crepe/api/Button.h
+++ b/src/crepe/api/Button.h
@@ -1,8 +1,7 @@
#pragma once
-#include <functional>
+#include "../types.h"
-#include "Event.h"
#include "UIObject.h"
namespace crepe {
@@ -21,14 +20,24 @@ namespace crepe {
*/
class Button : public UIObject {
public:
+ struct Data {
+ //! variable indicating if transform is relative to camera(false) or world(true)
+ bool world_space = false;
+ };
+
+public:
/**
* \brief Constructs a Button with the specified game object ID and dimensions.
*
* \param id The unique ID of the game object associated with this button.
* \param dimensions The width and height of the UIObject
* \param offset The offset relative this GameObjects Transform
+ * \param data additional data the button has
*/
- Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset);
+ Button(
+ game_object_id_t id, const vec2 & dimensions, const Data & data,
+ const vec2 & offset = {0, 0}
+ );
/**
* \brief Get the maximum number of instances for this component
*
@@ -38,6 +47,9 @@ public:
*/
virtual int get_instances_max() const { return 1; }
+public:
+ Data data;
+
private:
//! friend relation hover variable
friend class InputSystem;
diff --git a/src/crepe/api/Camera.cpp b/src/crepe/api/Camera.cpp
index 19a3296..b1466b5 100644
--- a/src/crepe/api/Camera.cpp
+++ b/src/crepe/api/Camera.cpp
@@ -6,8 +6,9 @@
using namespace crepe;
-Camera::Camera(game_object_id_t id, const ivec2 & screen, const vec2 & viewport_size,
- const Data & data)
+Camera::Camera(
+ game_object_id_t id, const ivec2 & screen, const vec2 & viewport_size, const Data & data
+)
: Component(id),
screen(screen),
viewport_size(viewport_size),
diff --git a/src/crepe/api/Camera.h b/src/crepe/api/Camera.h
index 54d9a73..3191b04 100644
--- a/src/crepe/api/Camera.h
+++ b/src/crepe/api/Camera.h
@@ -44,8 +44,10 @@ public:
* \param viewport_size is the view of the world in game units
* \param data the camera component data
*/
- Camera(game_object_id_t id, const ivec2 & screen, const vec2 & viewport_size,
- const Camera::Data & data);
+ Camera(
+ game_object_id_t id, const ivec2 & screen, const vec2 & viewport_size,
+ const Camera::Data & data
+ );
~Camera(); // dbg_trace only
public:
diff --git a/src/crepe/api/CircleCollider.cpp b/src/crepe/api/CircleCollider.cpp
index 90ab5e7..e72800c 100644
--- a/src/crepe/api/CircleCollider.cpp
+++ b/src/crepe/api/CircleCollider.cpp
@@ -2,7 +2,8 @@
using namespace crepe;
-CircleCollider::CircleCollider(game_object_id_t game_object_id, float radius,
- const vec2 & offset)
+CircleCollider::CircleCollider(
+ game_object_id_t game_object_id, float radius, const vec2 & offset
+)
: Collider(game_object_id, offset),
radius(radius) {}
diff --git a/src/crepe/api/CircleCollider.h b/src/crepe/api/CircleCollider.h
index 22da836..e6ad4fa 100644
--- a/src/crepe/api/CircleCollider.h
+++ b/src/crepe/api/CircleCollider.h
@@ -13,8 +13,9 @@ namespace crepe {
*/
class CircleCollider : public Collider {
public:
- CircleCollider(game_object_id_t game_object_id, float radius,
- const vec2 & offset = {0, 0});
+ CircleCollider(
+ game_object_id_t game_object_id, float radius, const vec2 & offset = {0, 0}
+ );
//! Radius of the circle collider.
float radius;
diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp
index 29bd77a..d0e3b35 100644
--- a/src/crepe/api/Color.cpp
+++ b/src/crepe/api/Color.cpp
@@ -2,11 +2,13 @@
using namespace crepe;
-const Color Color::WHITE{0xff, 0xff, 0xff};
-const Color Color::RED{0xff, 0x00, 0x00};
-const Color Color::GREEN{0x00, 0xff, 0x00};
-const Color Color::BLUE{0x00, 0x00, 0xff};
-const Color Color::BLACK{0x00, 0x00, 0x00};
-const Color Color::CYAN{0x00, 0xff, 0xff};
-const Color Color::YELLOW{0xff, 0xff, 0x00};
-const Color Color::MAGENTA{0xff, 0x00, 0xff};
+const Color Color::WHITE {0xff, 0xff, 0xff};
+const Color Color::RED {0xff, 0x00, 0x00};
+const Color Color::GREEN {0x00, 0xff, 0x00};
+const Color Color::BLUE {0x00, 0x00, 0xff};
+const Color Color::BLACK {0x00, 0x00, 0x00};
+const Color Color::CYAN {0x00, 0xff, 0xff};
+const Color Color::YELLOW {0xff, 0xff, 0x00};
+const Color Color::MAGENTA {0xff, 0x00, 0xff};
+const Color Color::GREY {0x80, 0x80, 0x80};
+const Color Color::GOLD {249, 205, 91};
diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h
index 84edb5c..dbfd0ed 100644
--- a/src/crepe/api/Color.h
+++ b/src/crepe/api/Color.h
@@ -18,6 +18,8 @@ struct Color {
static const Color MAGENTA;
static const Color YELLOW;
static const Color BLACK;
+ static const Color GREY;
+ static const Color GOLD;
};
} // namespace crepe
diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h
index 6b9e3ca..9c226a3 100644
--- a/src/crepe/api/Config.h
+++ b/src/crepe/api/Config.h
@@ -85,7 +85,7 @@ struct Config final {
* This config option is the font size at which all fonts will be loaded initially.
*
*/
- unsigned int size = 16;
+ unsigned int size = 500;
} font;
//! Configuration for click tolerance.
struct {
diff --git a/src/crepe/api/Engine.cpp b/src/crepe/api/Engine.cpp
index 2e9d35a..0bbe51f 100644
--- a/src/crepe/api/Engine.cpp
+++ b/src/crepe/api/Engine.cpp
@@ -46,17 +46,22 @@ void Engine::loop() {
try {
systems.fixed_update();
} catch (const exception & e) {
- Log::logf(Log::Level::WARNING,
- "Uncaught exception in fixed update function: {}\n", e.what());
+ Log::logf(
+ Log::Level::WARNING, "Uncaught exception in fixed update function: {}\n",
+ e.what()
+ );
}
timer.advance_fixed_elapsed_time();
}
try {
systems.frame_update();
+ this->scene_manager.load_next_scene();
} catch (const exception & e) {
- Log::logf(Log::Level::WARNING, "Uncaught exception in frame update function: {}\n",
- e.what());
+ Log::logf(
+ Log::Level::WARNING, "Uncaught exception in frame update function: {}\n",
+ e.what()
+ );
}
timer.enforce_frame_rate();
}
diff --git a/src/crepe/api/Engine.h b/src/crepe/api/Engine.h
index 700a0cd..452a856 100644
--- a/src/crepe/api/Engine.h
+++ b/src/crepe/api/Engine.h
@@ -54,26 +54,26 @@ private:
Mediator mediator;
//! SystemManager
- SystemManager system_manager{mediator};
+ SystemManager system_manager {mediator};
//! SDLContext instance
- SDLContext sdl_context{mediator};
+ SDLContext sdl_context {mediator};
//! Resource manager instance
- ResourceManager resource_manager{mediator};
+ ResourceManager resource_manager {mediator};
//! Component manager instance
- ComponentManager component_manager{mediator};
+ ComponentManager component_manager {mediator};
//! Scene manager instance
- SceneManager scene_manager{mediator};
+ SceneManager scene_manager {mediator};
//! LoopTimerManager instance
- LoopTimerManager loop_timer{mediator};
+ LoopTimerManager loop_timer {mediator};
//! EventManager instance
- EventManager event_manager{mediator};
+ EventManager event_manager {mediator};
//! Save manager instance
- SaveManager save_manager{mediator};
+ SaveManager save_manager {mediator};
//! ReplayManager instance
- ReplayManager replay_manager{mediator};
+ ReplayManager replay_manager {mediator};
};
} // namespace crepe
diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h
index 8e38280..7d4df21 100644
--- a/src/crepe/api/Event.h
+++ b/src/crepe/api/Event.h
@@ -10,15 +10,14 @@
namespace crepe {
/**
- * \brief Base class for all event types in the system.
+ * \brief Base struct for all event types in the system.
*/
-class Event {};
+struct Event {};
/**
* \brief Event triggered when a key is pressed.
*/
-class KeyPressEvent : public Event {
-public:
+struct KeyPressEvent : public Event {
//! false if first time press, true if key is repeated
bool repeat = false;
@@ -29,8 +28,7 @@ public:
/**
* \brief Event triggered when a key is released.
*/
-class KeyReleaseEvent : public Event {
-public:
+struct KeyReleaseEvent : public Event {
//! The key that was released.
Keycode key = Keycode::NONE;
};
@@ -38,8 +36,7 @@ public:
/**
* \brief Event triggered when a mouse button is pressed.
*/
-class MousePressEvent : public Event {
-public:
+struct MousePressEvent : public Event {
//! mouse position in world coordinates (game units).
vec2 mouse_pos = {0, 0};
@@ -50,8 +47,7 @@ public:
/**
* \brief Event triggered when a mouse button is clicked (press and release).
*/
-class MouseClickEvent : public Event {
-public:
+struct MouseClickEvent : public Event {
//! mouse position in world coordinates (game units).
vec2 mouse_pos = {0, 0};
@@ -62,8 +58,7 @@ public:
/**
* \brief Event triggered when a mouse button is released.
*/
-class MouseReleaseEvent : public Event {
-public:
+struct MouseReleaseEvent : public Event {
//! mouse position in world coordinates (game units).
vec2 mouse_pos = {0, 0};
@@ -74,8 +69,7 @@ public:
/**
* \brief Event triggered when the mouse is moved.
*/
-class MouseMoveEvent : public Event {
-public:
+struct MouseMoveEvent : public Event {
//! mouse position in world coordinates (game units).
vec2 mouse_pos = {0, 0};
//! The change in mouse position relative to the last position (in pixels).
@@ -85,8 +79,7 @@ public:
/**
* \brief Event triggered when the mouse is moved.
*/
-class MouseScrollEvent : public Event {
-public:
+struct MouseScrollEvent : public Event {
//! mouse position in world coordinates (game units) when the scroll happened.
vec2 mouse_pos = {0, 0};
//! scroll direction (-1 = down, 1 = up)
@@ -98,20 +91,19 @@ public:
/**
* \brief Event triggered to indicate the application is shutting down.
*/
-class ShutDownEvent : public Event {};
+struct ShutDownEvent : public Event {};
/**
* \brief Event triggered to indicate the window is overlapped by another window.
*
* When two windows overlap the bottom window gets distorted and that window has to be redrawn.
*/
-class WindowExposeEvent : public Event {};
+struct WindowExposeEvent : public Event {};
/**
* \brief Event triggered to indicate the window is resized.
*/
-class WindowResizeEvent : public Event {
-public:
+struct WindowResizeEvent : public Event {
//! new window dimensions
ivec2 dimensions = {0, 0};
};
@@ -119,8 +111,7 @@ public:
/**
* \brief Event triggered to indicate the window is moved.
*/
-class WindowMoveEvent : public Event {
-public:
+struct WindowMoveEvent : public Event {
//! The change in position relative to the last position (in pixels).
ivec2 delta_move = {0, 0};
};
@@ -128,12 +119,12 @@ public:
/**
* \brief Event triggered to indicate the window is minimized.
*/
-class WindowMinimizeEvent : public Event {};
+struct WindowMinimizeEvent : public Event {};
/**
* \brief Event triggered to indicate the window is maximized
*/
-class WindowMaximizeEvent : public Event {};
+struct WindowMaximizeEvent : public Event {};
/**
* \brief Event triggered to indicate the window gained focus
@@ -141,7 +132,7 @@ class WindowMaximizeEvent : public Event {};
* This event is triggered when the window receives focus, meaning it becomes the active window
* for user interaction.
*/
-class WindowFocusGainEvent : public Event {};
+struct WindowFocusGainEvent : public Event {};
/**
* \brief Event triggered to indicate the window lost focus
@@ -149,6 +140,6 @@ class WindowFocusGainEvent : public Event {};
* This event is triggered when the window loses focus, meaning it is no longer the active window
* for user interaction.
*/
-class WindowFocusLostEvent : public Event {};
+struct WindowFocusLostEvent : public Event {};
} // namespace crepe
diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp
index 9b94cad..100e210 100644
--- a/src/crepe/api/GameObject.cpp
+++ b/src/crepe/api/GameObject.cpp
@@ -7,13 +7,15 @@
using namespace crepe;
using namespace std;
-GameObject::GameObject(Mediator & mediator, game_object_id_t id, const std::string & name,
- const std::string & tag, const vec2 & position, double rotation,
- double scale)
+GameObject::GameObject(
+ Mediator & mediator, game_object_id_t id, const std::string & name,
+ const std::string & tag, const vec2 & position, double rotation, double scale
+)
: id(id),
mediator(mediator),
- transform(mediator.component_manager->add_component<Transform>(this->id, position,
- rotation, scale)),
+ transform(mediator.component_manager->add_component<Transform>(
+ this->id, position, rotation, scale
+ )),
metadata(mediator.component_manager->add_component<Metadata>(this->id, name, tag)) {}
void GameObject::set_parent(const GameObject & parent) {
diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h
index 572ce3a..043913a 100644
--- a/src/crepe/api/GameObject.h
+++ b/src/crepe/api/GameObject.h
@@ -30,8 +30,10 @@ private:
* \param rotation The rotation of the GameObject
* \param scale The scale of the GameObject
*/
- GameObject(Mediator & mediator, game_object_id_t id, const std::string & name,
- const std::string & tag, const vec2 & position, double rotation, double scale);
+ GameObject(
+ Mediator & mediator, game_object_id_t id, const std::string & name,
+ const std::string & tag, const vec2 & position, double rotation, double scale
+ );
//! ComponentManager instances GameObject
friend class ComponentManager;
diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp
index 9a70334..341c1e2 100644
--- a/src/crepe/api/ParticleEmitter.cpp
+++ b/src/crepe/api/ParticleEmitter.cpp
@@ -4,8 +4,9 @@
using namespace crepe;
using namespace std;
-ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite,
- const Data & data)
+ParticleEmitter::ParticleEmitter(
+ game_object_id_t game_object_id, const Sprite & sprite, const Data & data
+)
: Component(game_object_id),
sprite(sprite),
data(data) {
@@ -15,7 +16,7 @@ ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite &
}
unique_ptr<Component> ParticleEmitter::save() const {
- return unique_ptr<Component>{new ParticleEmitter(*this)};
+ return unique_ptr<Component> {new ParticleEmitter(*this)};
}
void ParticleEmitter::restore(const Component & snapshot) {
diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h
index 6900295..b63d941 100644
--- a/src/crepe/api/Rigidbody.h
+++ b/src/crepe/api/Rigidbody.h
@@ -2,6 +2,7 @@
#include <cmath>
#include <set>
+#include <string>
#include "../Component.h"
@@ -120,26 +121,49 @@ public:
* above 0.0.
*
*/
- float elastisity_coefficient = 0.0;
+ float elasticity_coefficient = 0.0;
/**
- * \brief Offset of all colliders relative to the object's transform position.
+ * \brief Enables collision handling for objects colliding with kinematic objects.
+ *
+ * Enables collision handling for objects colliding with kinematic objects in the collision system.
+ * If `kinematic_collision` is true, dynamic objects cannot pass through this kinematic object.
+ * This ensures that kinematic objects delegate collision handling to the collision system.
+ */
+ bool kinematic_collision = true;
+
+ /**
+ * \brief Defines the collision layers a GameObject interacts with.
*
- * The `offset` defines a positional shift applied to all colliders associated with the object, relative to the object's
- * transform position. This allows for the colliders to be placed at a different position than the object's actual
- * position, without modifying the object's transform itself.
+ * The `collision_layers` represent the set of layers the GameObject can detect collisions with.
+ * Each element in this set corresponds to a layer ID. The GameObject will only collide with other
+ * GameObjects that belong to one these layers.
+ */
+ std::set<int> collision_layers = {0};
+
+ /**
+ * \brief Specifies the collision layer of the GameObject.
*
+ * The `collision_layer` indicates the single layer that this GameObject belongs to.
+ * This determines which layers other objects must match to detect collisions with this object.
*/
- vec2 offset;
+ int collision_layer = 0;
/**
* \brief Defines the collision layers of a GameObject.
*
- * The `collision_layers` specifies the layers that the GameObject will collide with.
- * Each element represents a layer ID, and the GameObject will only detect
- * collisions with other GameObjects that belong to these layers.
+ * The `collision_names` specifies where the GameObject will collide with.
+ * Each element represents a name from the Metadata of the gameobject.
*/
- std::set<int> collision_layers = {0};
+ std::set<std::string> collision_names;
+
+ /**
+ * \brief Defines the collision layers of a GameObject.
+ *
+ * The `collision_tags` specifies where the GameObject will collide with.
+ * Each element represents a tag from the Metadata of the gameobject.
+ */
+ std::set<std::string> collision_tags;
};
public:
diff --git a/src/crepe/api/Scene.cpp b/src/crepe/api/Scene.cpp
index ad729d2..84da7e8 100644
--- a/src/crepe/api/Scene.cpp
+++ b/src/crepe/api/Scene.cpp
@@ -4,8 +4,10 @@ using namespace crepe;
SaveManager & Scene::get_save_manager() const { return mediator->save_manager; }
-GameObject Scene::new_object(const std::string & name, const std::string & tag,
- const vec2 & position, double rotation, double scale) {
+GameObject Scene::new_object(
+ const std::string & name, const std::string & tag, const vec2 & position, double rotation,
+ double scale
+) {
// Forward the call to ComponentManager's new_object method
return mediator->component_manager->new_object(name, tag, position, rotation, scale);
}
diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h
index dcca9d4..b50a0fc 100644
--- a/src/crepe/api/Scene.h
+++ b/src/crepe/api/Scene.h
@@ -60,7 +60,7 @@ private:
OptionalRef<Mediator> mediator;
//! \}
-protected:
+public:
/**
* \brief Retrieve the reference to the SaveManager instance
*
@@ -69,9 +69,10 @@ protected:
SaveManager & get_save_manager() const;
//! \copydoc ComponentManager::new_object
- GameObject new_object(const std::string & name, const std::string & tag = "",
- const vec2 & position = {0, 0}, double rotation = 0,
- double scale = 1);
+ GameObject new_object(
+ const std::string & name, const std::string & tag = "", const vec2 & position = {0, 0},
+ double rotation = 0, double scale = 1
+ );
//! \copydoc ResourceManager::set_persistent
void set_persistent(const Asset & asset, bool persistent);
diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp
index 6de6830..06b535f 100644
--- a/src/crepe/api/Script.cpp
+++ b/src/crepe/api/Script.cpp
@@ -20,6 +20,21 @@ void Script::subscribe(const EventHandler<CollisionEvent> & callback) {
this->subscribe_internal(callback, this->game_object_id);
}
+template <>
+void Script::subscribe(const EventHandler<ButtonExitEvent> & callback) {
+ this->subscribe_internal(callback, this->game_object_id);
+}
+
+template <>
+void Script::subscribe(const EventHandler<ButtonPressEvent> & callback) {
+ this->subscribe_internal(callback, this->game_object_id);
+}
+
+template <>
+void Script::subscribe(const EventHandler<ButtonEnterEvent> & callback) {
+ this->subscribe_internal(callback, this->game_object_id);
+}
+
void Script::set_next_scene(const string & name) {
SceneManager & mgr = this->mediator->scene_manager;
mgr.set_next_scene(name);
@@ -61,4 +76,3 @@ bool Script::get_key_state(Keycode key) const noexcept {
return false;
}
}
-
diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h
index 2422cdc..b000d9d 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -8,6 +8,7 @@
#include "../manager/Mediator.h"
#include "../manager/ReplayManager.h"
#include "../system/CollisionSystem.h"
+#include "../system/InputSystem.h"
#include "../types.h"
#include "../util/Log.h"
#include "../util/OptionalRef.h"
@@ -48,14 +49,23 @@ protected:
*/
virtual void init() {}
/**
- * \brief Script update function (empty by default)
+ * \brief Script fixed update function (empty by default)
*
* \param delta_time Time since last fixed update
*
- * This function is called during the ScriptSystem::update() routine if the \c BehaviorScript
- * component holding this script instance is active.
+ * \note This function is called during the ScriptSystem::update() routine if the \c
+ * BehaviorScript component holding this script instance is active.
*/
- virtual void update(duration_t delta_time) {}
+ virtual void fixed_update(duration_t delta_time) {}
+ /**
+ * \brief Script frame update function (empty by default)
+ *
+ * \param delta_time Time since last frame update
+ *
+ * \note This function is called during the ScriptSystem::update() routine if the \c
+ * BehaviorScript component holding this script instance is active.
+ */
+ virtual void frame_update(duration_t delta_time) {}
//! \}
//! ScriptSystem calls \c init() and \c update()
@@ -119,12 +129,14 @@ protected:
void subscribe(const EventHandler<EventType> & callback);
//! \copydoc EventManager::trigger_event
template <typename EventType>
- void trigger_event(const EventType & event = {},
- event_channel_t channel = EventManager::CHANNEL_ALL);
+ void trigger_event(
+ const EventType & event = {}, event_channel_t channel = EventManager::CHANNEL_ALL
+ );
//! \copydoc EventManager::queue_event
template <typename EventType>
- void queue_event(const EventType & event = {},
- event_channel_t channel = EventManager::CHANNEL_ALL);
+ void queue_event(
+ const EventType & event = {}, event_channel_t channel = EventManager::CHANNEL_ALL
+ );
//! \}
/**
@@ -169,7 +181,7 @@ protected:
OptionalRef<Mediator> & mediator;
replay(OptionalRef<Mediator> & mediator) : mediator(mediator) {}
friend class Script;
- } replay{mediator};
+ } replay {mediator};
/**
* \brief Utility function to retrieve the keyboard state
@@ -261,7 +273,42 @@ void Script::subscribe(const EventHandler<CollisionEvent> & callback);
template <>
void Script::subscribe(const EventHandler<CollisionEvent> & callback, event_channel_t)
= delete;
-
+/**
+ * \brief Subscribe to ButtonPressEvent for the current GameObject
+ *
+ * This is a template specialization for Script::subscribe which automatically sets the event
+ * channel so the callback handler is only called for ButtonPressEvent events that apply to the
+ * current GameObject the parent BehaviorScript is attached to.
+ */
+template <>
+void Script::subscribe(const EventHandler<ButtonPressEvent> & callback);
+template <>
+void Script::subscribe(const EventHandler<ButtonPressEvent> & callback, event_channel_t)
+ = delete;
+/**
+ * \brief Subscribe to ButtonExitEvent for the current GameObject
+ *
+ * This is a template specialization for Script::subscribe which automatically sets the event
+ * channel so the callback handler is only called for ButtonExitEvent events that apply to the
+ * current GameObject the parent BehaviorScript is attached to.
+ */
+template <>
+void Script::subscribe(const EventHandler<ButtonExitEvent> & callback);
+template <>
+void Script::subscribe(const EventHandler<ButtonExitEvent> & callback, event_channel_t)
+ = delete;
+/**
+ * \brief Subscribe to ButtonEnterEvent for the current GameObject
+ *
+ * This is a template specialization for Script::subscribe which automatically sets the event
+ * channel so the callback handler is only called for ButtonEnterEvent events that apply to the
+ * current GameObject the parent BehaviorScript is attached to.
+ */
+template <>
+void Script::subscribe(const EventHandler<ButtonEnterEvent> & callback);
+template <>
+void Script::subscribe(const EventHandler<ButtonEnterEvent> & callback, event_channel_t)
+ = delete;
} // namespace crepe
#include "Script.hpp"
diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp
index 4462a41..c7fa6ff 100644
--- a/src/crepe/api/Script.hpp
+++ b/src/crepe/api/Script.hpp
@@ -14,7 +14,8 @@ T & Script::get_component() const {
RefVector<T> all_components = this->get_components<T>();
if (all_components.size() < 1)
throw runtime_error(
- format("Script: no component found with type = {}", typeid(T).name()));
+ format("Script: no component found with type = {}", typeid(T).name())
+ );
return all_components.back().get();
}
@@ -35,8 +36,9 @@ void Script::logf(std::format_string<Args...> fmt, Args &&... args) {
}
template <typename EventType>
-void Script::subscribe_internal(const EventHandler<EventType> & callback,
- event_channel_t channel) {
+void Script::subscribe_internal(
+ const EventHandler<EventType> & callback, event_channel_t channel
+) {
EventManager & mgr = this->mediator->event_manager;
subscription_t listener = mgr.subscribe<EventType>(
[this, callback](const EventType & data) -> bool {
@@ -54,7 +56,8 @@ void Script::subscribe_internal(const EventHandler<EventType> & callback,
// call user-provided callback
return callback(data);
},
- channel);
+ channel
+ );
this->listeners.push_back(listener);
}
diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp
index 0107c7b..3c77e2e 100644
--- a/src/crepe/api/Sprite.cpp
+++ b/src/crepe/api/Sprite.cpp
@@ -19,3 +19,16 @@ Sprite::Sprite(game_object_id_t id, const Asset & texture, const Sprite::Data &
}
Sprite::~Sprite() { dbg_trace(); }
+
+unique_ptr<Component> Sprite::save() const { return unique_ptr<Component>(new Sprite(*this)); }
+
+void Sprite::restore(const Component & snapshot) {
+ *this = static_cast<const Sprite &>(snapshot);
+}
+
+Sprite & Sprite::operator=(const Sprite & snapshot) {
+ this->active = snapshot.active;
+ this->data = snapshot.data;
+ this->mask = snapshot.mask;
+ return *this;
+}
diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h
index a2409c2..3565bed 100644
--- a/src/crepe/api/Sprite.h
+++ b/src/crepe/api/Sprite.h
@@ -42,10 +42,10 @@ public:
FlipSettings flip;
//! Layer sorting level of the sprite
- const int sorting_in_layer = 0;
+ int sorting_in_layer = 0;
//! Order within the sorting layer
- const int order_in_layer = 0;
+ int order_in_layer = 0;
/**
* \brief width and height of the sprite in game units
@@ -66,6 +66,16 @@ public:
//! independent sprite offset position
vec2 position_offset;
+
+ /**
+ * \brief gives the user the option to render this in world space or in camera space
+ *
+ * - if true will this be rendered in world space this means that the sprite can be
+ * rendered off the screen
+ * - if false --> will the sprite be rendered in camera space. this means that the
+ * coordinates given on the \c Sprite and \c Transform will be inside the camera
+ */
+ bool world_space = true;
};
public:
@@ -109,6 +119,12 @@ private:
//! Render area of the sprite this will also be adjusted by the AnimatorSystem if an Animator
// object is present in GameObject. this is in sprite pixels
Rect mask;
+
+protected:
+ virtual std::unique_ptr<Component> save() const;
+ Sprite(const Sprite &) = default;
+ virtual void restore(const Component & snapshot);
+ virtual Sprite & operator=(const Sprite &);
};
} // namespace crepe
diff --git a/src/crepe/api/Text.cpp b/src/crepe/api/Text.cpp
index 54a4370..e5cc39d 100644
--- a/src/crepe/api/Text.cpp
+++ b/src/crepe/api/Text.cpp
@@ -1,12 +1,27 @@
-#include "../facade/FontFacade.h"
+#include "../types.h"
#include "Text.h"
using namespace crepe;
+using namespace std;
-Text::Text(game_object_id_t id, const vec2 & dimensions, const vec2 & offset,
- const std::string & font_family, const Data & data, const std::string & text)
+Text::Text(
+ game_object_id_t id, const vec2 & dimensions, const std::string & font_family,
+ const Data & data, const vec2 & offset, const std::string & text
+)
: UIObject(id, dimensions, offset),
text(text),
data(data),
font_family(font_family) {}
+
+unique_ptr<Component> Text::save() const { return unique_ptr<Component>(new Text(*this)); }
+
+void Text::restore(const Component & snapshot) { *this = static_cast<const Text &>(snapshot); }
+
+Text & Text::operator=(const Text & snapshot) {
+ this->active = snapshot.active;
+ this->data = snapshot.data;
+ this->text = snapshot.text;
+ this->font_family = snapshot.font_family;
+ return *this;
+}
diff --git a/src/crepe/api/Text.h b/src/crepe/api/Text.h
index c30dc80..859490e 100644
--- a/src/crepe/api/Text.h
+++ b/src/crepe/api/Text.h
@@ -3,7 +3,7 @@
#include <optional>
#include <string>
-#include "../Component.h"
+#include "../types.h"
#include "Asset.h"
#include "Color.h"
@@ -19,22 +19,8 @@ class Text : public UIObject {
public:
//! Text data that does not have to be set in the constructor
struct Data {
- /**
- * \brief fontsize for text rendering
- *
- * \note this is not the actual font size that is loaded in.
- *
- * Since SDL_TTF requires the font size when loading in the font it is not possible to switch the font size.
- * The default font size that is loaded is set in the Config.
- * Instead this value is used to upscale the font texture which can cause blurring or distorted text when upscaling or downscaling too much.
- */
- unsigned int font_size = 16;
-
- //! Layer sorting level of the text
- const int sorting_in_layer = 0;
-
- //! Order within the sorting text
- const int order_in_layer = 0;
+ //! variable indicating if transform is relative to camera(false) or world(true)
+ bool world_space = false;
//! Label text color.
Color text_color = Color::BLACK;
@@ -50,8 +36,10 @@ public:
* \param data Data struct containing extra text parameters.
* \param font Optional font asset that can be passed or left empty.
*/
- Text(game_object_id_t id, const vec2 & dimensions, const vec2 & offset,
- const std::string & font_family, const Data & data, const std::string & text = "");
+ Text(
+ game_object_id_t id, const vec2 & dimensions, const std::string & font_family,
+ const Data & data, const vec2 & offset = {0, 0}, const std::string & text = ""
+ );
//! Label text.
std::string text = "";
@@ -61,6 +49,12 @@ public:
std::optional<Asset> font;
//! Data instance
Data data;
+
+protected:
+ virtual std::unique_ptr<Component> save() const;
+ Text(const Text &) = default;
+ virtual void restore(const Component & snapshot);
+ virtual Text & operator=(const Text &);
};
} // namespace crepe
diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp
index fcfce14..b70174c 100644
--- a/src/crepe/api/Transform.cpp
+++ b/src/crepe/api/Transform.cpp
@@ -14,7 +14,7 @@ Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, d
}
unique_ptr<Component> Transform::save() const {
- return unique_ptr<Component>{new Transform(*this)};
+ return unique_ptr<Component> {new Transform(*this)};
}
void Transform::restore(const Component & snapshot) {
diff --git a/src/crepe/api/UIObject.h b/src/crepe/api/UIObject.h
index f7f4fba..0d9b1f7 100644
--- a/src/crepe/api/UIObject.h
+++ b/src/crepe/api/UIObject.h
@@ -15,7 +15,7 @@ public:
* \param dimensions width and height of the UIObject
* \param offset Offset relative to the GameObject Transform
*/
- UIObject(game_object_id_t id, const vec2 & dimensions, const vec2 & offset);
+ UIObject(game_object_id_t id, const vec2 & dimensions, const vec2 & offset = {0, 0});
//! Width and height of the UIObject
vec2 dimensions;
//! Position offset relative to this GameObjects Transform
diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h
index bf9d124..52e1bb6 100644
--- a/src/crepe/api/Vector2.h
+++ b/src/crepe/api/Vector2.h
@@ -90,6 +90,9 @@ struct Vector2 {
//! Returns the perpendicular vector to this vector.
Vector2 perpendicular() const;
+
+ //! Checks if both components of the vector are NaN.
+ bool is_nan() const;
};
} // namespace crepe
diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp
index ff53cb0..e195760 100644
--- a/src/crepe/api/Vector2.hpp
+++ b/src/crepe/api/Vector2.hpp
@@ -163,4 +163,9 @@ Vector2<T> Vector2<T>::perpendicular() const {
return {-y, x};
}
+template <class T>
+bool Vector2<T>::is_nan() const {
+ return std::isnan(x) && std::isnan(y);
+}
+
} // namespace crepe
diff --git a/src/crepe/facade/FontFacade.cpp b/src/crepe/facade/FontFacade.cpp
index 87f95ab..e284f5a 100644
--- a/src/crepe/facade/FontFacade.cpp
+++ b/src/crepe/facade/FontFacade.cpp
@@ -20,8 +20,9 @@ Asset FontFacade::get_font_asset(const string & font_family) {
= FcNameParse(reinterpret_cast<const FcChar8 *>(font_family.c_str()));
if (raw_pattern == NULL) throw runtime_error("Failed to create font pattern.");
- unique_ptr<FcPattern, function<void(FcPattern *)>> pattern{
- raw_pattern, [](FcPattern * p) { FcPatternDestroy(p); }};
+ unique_ptr<FcPattern, function<void(FcPattern *)>> pattern {
+ raw_pattern, [](FcPattern * p) { FcPatternDestroy(p); }
+ };
FcConfig * config = FcConfigGetCurrent();
if (config == NULL) throw runtime_error("Failed to get current Fontconfig configuration.");
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index 9486ccd..6c93fb2 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -1,5 +1,6 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_blendmode.h>
+#include <SDL2/SDL_hints.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_keycode.h>
#include <SDL2/SDL_pixels.h>
@@ -11,7 +12,6 @@
#include <array>
#include <cmath>
#include <cstddef>
-#include <cstdint>
#include <functional>
#include <memory>
#include <stdexcept>
@@ -21,7 +21,11 @@
#include "../api/Config.h"
#include "../api/Sprite.h"
#include "../util/dbg.h"
+#include "api/Text.h"
+#include "api/Transform.h"
+#include "facade/Font.h"
#include "manager/Mediator.h"
+#include "util/AbsolutePosition.h"
#include "SDLContext.h"
#include "Texture.h"
@@ -32,17 +36,15 @@ using namespace std;
SDLContext::SDLContext(Mediator & mediator) {
dbg_trace();
- if (TTF_Init() == -1) {
- throw runtime_error(format("SDL_ttf initialization failed: {}", TTF_GetError()));
- }
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
throw runtime_error(format("SDLContext: SDL_Init error: {}", SDL_GetError()));
}
auto & cfg = Config::get_instance().window_settings;
- SDL_Window * tmp_window
- = SDL_CreateWindow(cfg.window_title.c_str(), SDL_WINDOWPOS_CENTERED,
- SDL_WINDOWPOS_CENTERED, cfg.default_size.x, cfg.default_size.y, 0);
+ SDL_Window * tmp_window = SDL_CreateWindow(
+ cfg.window_title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+ cfg.default_size.x, cfg.default_size.y, 0
+ );
if (!tmp_window) {
throw runtime_error(format("SDLContext: SDL_Window error: {}", SDL_GetError()));
}
@@ -51,8 +53,8 @@ SDLContext::SDLContext(Mediator & mediator) {
SDL_Renderer * tmp_renderer
= SDL_CreateRenderer(this->game_window.get(), -1, SDL_RENDERER_ACCELERATED);
if (!tmp_renderer) {
- throw runtime_error(
- format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError()));
+ throw runtime_error(format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError())
+ );
}
this->game_renderer
@@ -63,6 +65,10 @@ SDLContext::SDLContext(Mediator & mediator) {
throw runtime_error("SDLContext: SDL_image could not initialize!");
}
+ if (TTF_Init() == -1) {
+ throw runtime_error(format("SDL_ttf initialization failed: {}", TTF_GetError()));
+ }
+
mediator.sdl_context = *this;
}
@@ -75,8 +81,8 @@ SDLContext::~SDLContext() {
// TODO: how are we going to ensure that these are called from the same
// thread that SDL_Init() was called on? This has caused problems for me
// before.
- IMG_Quit();
TTF_Quit();
+ IMG_Quit();
SDL_Quit();
}
@@ -101,7 +107,7 @@ const keyboard_state_t & SDLContext::get_keyboard_state() {
MouseButton SDLContext::sdl_to_mousebutton(Uint8 sdl_button) {
static const std::array<MouseButton, 5> MOUSE_BUTTON_LOOKUP_TABLE = [] {
- std::array<MouseButton, 5> table{};
+ std::array<MouseButton, 5> table {};
table.fill(MouseButton::NONE);
table[SDL_BUTTON_LEFT] = MouseButton::LEFT_MOUSE;
@@ -137,6 +143,8 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const {
= (ctx.sprite.aspect_ratio == 0) ? ctx.texture.get_ratio() : ctx.sprite.aspect_ratio;
vec2 size = data.size;
+ vec2 screen_pos = ctx.pos;
+
if (data.size.x == 0 && data.size.y != 0) {
size.x = data.size.y * aspect_ratio;
}
@@ -145,12 +153,17 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const {
}
size *= cam_aux_data.render_scale * ctx.img_scale * data.scale_offset;
- vec2 screen_pos = (ctx.pos + data.position_offset - cam_aux_data.cam_pos
- + (cam_aux_data.zoomed_viewport) / 2)
- * cam_aux_data.render_scale
- - size / 2 + cam_aux_data.bar_size;
+ if (ctx.sprite.data.world_space) {
+ screen_pos = (screen_pos - cam_aux_data.cam_pos + cam_aux_data.zoomed_viewport / 2)
+ * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ } else {
+ screen_pos
+ = (screen_pos + cam_aux_data.zoomed_viewport / 2) * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ }
- return SDL_FRect{
+ return SDL_FRect {
.x = screen_pos.x,
.y = screen_pos.y,
.w = size.x,
@@ -159,6 +172,7 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const {
}
void SDLContext::draw(const RenderContext & ctx) {
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
const Sprite::Data & data = ctx.sprite.data;
SDL_RendererFlip render_flip
= (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * data.flip.flip_x)
@@ -174,7 +188,7 @@ void SDLContext::draw(const RenderContext & ctx) {
srcrect_ptr = &srcrect;
}
- SDL_FRect dstrect = this->get_dst_rect(SDLContext::DestinationRectangleData{
+ SDL_FRect dstrect = this->get_dst_rect(SDLContext::DestinationRectangleData {
.sprite = ctx.sprite,
.texture = ctx.texture,
.pos = ctx.pos,
@@ -184,8 +198,65 @@ void SDLContext::draw(const RenderContext & ctx) {
double angle = ctx.angle + data.angle_offset;
this->set_color_texture(ctx.texture, ctx.sprite.data.color);
- SDL_RenderCopyExF(this->game_renderer.get(), ctx.texture.get_img(), srcrect_ptr, &dstrect,
- angle, NULL, render_flip);
+ SDL_RenderCopyExF(
+ this->game_renderer.get(), ctx.texture.get_img(), srcrect_ptr, &dstrect, angle, NULL,
+ render_flip
+ );
+}
+
+void SDLContext::draw_text(const RenderText & data) {
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
+
+ const Text & text = data.text;
+ const Font & font = data.font;
+ vec2 absoluut_pos = AbsolutePosition::get_position(data.transform, data.text.offset);
+ std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> font_surface;
+ std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> font_texture;
+
+ SDL_Color color {
+ .r = text.data.text_color.r,
+ .g = text.data.text_color.g,
+ .b = text.data.text_color.b,
+ .a = text.data.text_color.a,
+ };
+ SDL_Surface * tmp_font_surface
+ = TTF_RenderText_Solid(font.get_font(), text.text.c_str(), color);
+ if (tmp_font_surface == NULL) {
+ throw runtime_error(format("draw_text: font surface error: {}", SDL_GetError()));
+ }
+ font_surface = {tmp_font_surface, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }};
+
+ SDL_Texture * tmp_font_texture
+ = SDL_CreateTextureFromSurface(this->game_renderer.get(), font_surface.get());
+ if (tmp_font_texture == NULL) {
+ throw runtime_error(format("draw_text: font texture error: {}", SDL_GetError()));
+ }
+ font_texture
+ = {tmp_font_texture, [](SDL_Texture * texture) { SDL_DestroyTexture(texture); }};
+
+ vec2 size = text.dimensions * cam_aux_data.render_scale * data.transform.scale;
+ vec2 screen_pos = absoluut_pos;
+ if (text.data.world_space) {
+ screen_pos = (screen_pos - cam_aux_data.cam_pos + (cam_aux_data.zoomed_viewport) / 2)
+ * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ } else {
+ screen_pos
+ = (screen_pos + (cam_aux_data.zoomed_viewport) / 2) * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ }
+
+ SDL_FRect dstrect {
+ .x = screen_pos.x,
+ .y = screen_pos.y,
+ .w = size.x,
+ .h = size.y,
+ };
+
+ SDL_RenderCopyExF(
+ this->game_renderer.get(), font_texture.get(), NULL, &dstrect, data.transform.rotation,
+ NULL, SDL_FLIP_NONE
+ );
}
void SDLContext::update_camera_view(const Camera & cam, const vec2 & new_pos) {
@@ -231,8 +302,10 @@ void SDLContext::update_camera_view(const Camera & cam, const vec2 & new_pos) {
render_scale.x = render_scale.y = scale;
}
- SDL_SetRenderDrawColor(this->game_renderer.get(), cam_data.bg_color.r, cam_data.bg_color.g,
- cam_data.bg_color.b, cam_data.bg_color.a);
+ SDL_SetRenderDrawColor(
+ this->game_renderer.get(), cam_data.bg_color.r, cam_data.bg_color.g,
+ cam_data.bg_color.b, cam_data.bg_color.a
+ );
SDL_Rect bg = {
.x = 0,
@@ -367,11 +440,12 @@ std::vector<EventData> SDLContext::get_events() {
return event_list;
}
-void SDLContext::handle_window_event(const SDL_WindowEvent & window_event,
- std::vector<EventData> & event_list) {
+void SDLContext::handle_window_event(
+ const SDL_WindowEvent & window_event, std::vector<EventData> & event_list
+) {
switch (window_event.event) {
case SDL_WINDOWEVENT_EXPOSED:
- event_list.push_back(EventData{EventType::WINDOW_EXPOSE});
+ event_list.push_back(EventData {EventType::WINDOW_EXPOSE});
break;
case SDL_WINDOWEVENT_RESIZED:
event_list.push_back(EventData{
@@ -395,16 +469,16 @@ void SDLContext::handle_window_event(const SDL_WindowEvent & window_event,
break;
case SDL_WINDOWEVENT_MINIMIZED:
- event_list.push_back(EventData{EventType::WINDOW_MINIMIZE});
+ event_list.push_back(EventData {EventType::WINDOW_MINIMIZE});
break;
case SDL_WINDOWEVENT_MAXIMIZED:
- event_list.push_back(EventData{EventType::WINDOW_MAXIMIZE});
+ event_list.push_back(EventData {EventType::WINDOW_MAXIMIZE});
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
- event_list.push_back(EventData{EventType::WINDOW_FOCUS_GAIN});
+ event_list.push_back(EventData {EventType::WINDOW_FOCUS_GAIN});
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
- event_list.push_back(EventData{EventType::WINDOW_FOCUS_LOST});
+ event_list.push_back(EventData {EventType::WINDOW_FOCUS_LOST});
break;
}
}
diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h
index b687f87..bc118f9 100644
--- a/src/crepe/facade/SDLContext.h
+++ b/src/crepe/facade/SDLContext.h
@@ -12,18 +12,21 @@
#include <unordered_map>
#include "../types.h"
+#include "EventData.h"
#include "api/Camera.h"
#include "api/Color.h"
#include "api/KeyCodes.h"
#include "api/Sprite.h"
#include "api/Transform.h"
-#include "types.h"
#include "EventData.h"
#include "FontFacade.h"
+#include "types.h"
namespace crepe {
class Texture;
+class Text;
+class Font;
class Mediator;
/**
@@ -71,12 +74,11 @@ public:
const double & scale;
};
-public:
- /**
- * \brief Gets the singleton instance of SDLContext.
- * \return Reference to the SDLContext instance.
- */
- static SDLContext & get_instance();
+ struct RenderText {
+ const Text & text;
+ const Font & font;
+ const Transform & transform;
+ };
public:
SDLContext(const SDLContext &) = delete;
@@ -114,8 +116,9 @@ public:
* This method checks if any window events are triggered and adds them to the event_list.
*
*/
- void handle_window_event(const SDL_WindowEvent & window_event,
- std::vector<EventData> & event_list);
+ void handle_window_event(
+ const SDL_WindowEvent & window_event, std::vector<EventData> & event_list
+ );
/**
* \brief Converts an SDL scan code to the custom Keycode type.
*
@@ -186,6 +189,13 @@ public:
*/
void draw(const RenderContext & ctx);
+ /**
+ * \brief draws a text to the screen
+ *
+ * \param data Reference to the rendering data needed to draw
+ */
+ void draw_text(const RenderText & data);
+
//! Clears the screen, preparing for a new frame.
void clear_screen();
@@ -245,7 +255,7 @@ private:
private:
//! instance of the font_facade
- FontFacade font_facade{};
+ FontFacade font_facade {};
public:
/**
diff --git a/src/crepe/facade/Texture.cpp b/src/crepe/facade/Texture.cpp
index 9a10f9b..06caa54 100644
--- a/src/crepe/facade/Texture.cpp
+++ b/src/crepe/facade/Texture.cpp
@@ -1,7 +1,8 @@
-#include "../util/dbg.h"
+#include "../Resource.h"
+#include "../facade/SDLContext.h"
#include "../manager/Mediator.h"
#include "../types.h"
-#include "../Resource.h"
+#include "../util/dbg.h"
#include "SDLContext.h"
#include "Texture.h"
@@ -27,4 +28,3 @@ const ivec2 & Texture::get_size() const noexcept { return this->size; }
const float & Texture::get_ratio() const noexcept { return this->aspect_ratio; }
SDL_Texture * Texture::get_img() const noexcept { return this->texture.get(); }
-
diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp
index 745ddae..245419d 100644
--- a/src/crepe/manager/ComponentManager.cpp
+++ b/src/crepe/manager/ComponentManager.cpp
@@ -46,14 +46,16 @@ void ComponentManager::delete_all_components() {
this->next_id = 0;
}
-GameObject ComponentManager::new_object(const string & name, const string & tag,
- const vec2 & position, double rotation, double scale) {
+GameObject ComponentManager::new_object(
+ const string & name, const string & tag, const vec2 & position, double rotation,
+ double scale
+) {
// Find the first available id (taking persistent objects into account)
while (this->persistent[this->next_id]) {
this->next_id++;
}
- GameObject object{this->mediator, this->next_id, name, tag, position, rotation, scale};
+ GameObject object {this->mediator, this->next_id, name, tag, position, rotation, scale};
this->next_id++;
return object;
@@ -64,23 +66,25 @@ void ComponentManager::set_persistent(game_object_id_t id, bool persistent) {
}
set<game_object_id_t> ComponentManager::get_objects_by_name(const string & name) const {
- return this->get_objects_by_predicate<Metadata>(
- [name](const Metadata & data) { return data.name == name; });
+ return this->get_objects_by_predicate<Metadata>([name](const Metadata & data) {
+ return data.name == name;
+ });
}
set<game_object_id_t> ComponentManager::get_objects_by_tag(const string & tag) const {
- return this->get_objects_by_predicate<Metadata>(
- [tag](const Metadata & data) { return data.tag == tag; });
+ return this->get_objects_by_predicate<Metadata>([tag](const Metadata & data) {
+ return data.tag == tag;
+ });
}
ComponentManager::Snapshot ComponentManager::save() {
- Snapshot snapshot{};
+ Snapshot snapshot {};
for (const auto & [type, by_id_index] : this->components) {
for (game_object_id_t id = 0; id < by_id_index.size(); id++) {
const auto & components = by_id_index[id];
for (size_t index = 0; index < components.size(); index++) {
const Component & component = *components[index];
- snapshot.components.push_back(SnapshotComponent{
+ snapshot.components.push_back(SnapshotComponent {
.type = type,
.id = id,
.index = index,
diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h
index c3a5b4a..2eb1f7e 100644
--- a/src/crepe/manager/ComponentManager.h
+++ b/src/crepe/manager/ComponentManager.h
@@ -38,9 +38,10 @@ public:
*
* \note This method automatically assigns a new entity ID
*/
- GameObject new_object(const std::string & name, const std::string & tag = "",
- const vec2 & position = {0, 0}, double rotation = 0,
- double scale = 1);
+ GameObject new_object(
+ const std::string & name, const std::string & tag = "", const vec2 & position = {0, 0},
+ double rotation = 0, double scale = 1
+ );
public:
/**
diff --git a/src/crepe/manager/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp
index 9e70865..6d32edb 100644
--- a/src/crepe/manager/ComponentManager.hpp
+++ b/src/crepe/manager/ComponentManager.hpp
@@ -11,8 +11,10 @@ template <class T, typename... Args>
T & ComponentManager::add_component(game_object_id_t id, Args &&... args) {
using namespace std;
- static_assert(is_base_of<Component, T>::value,
- "add_component must recieve a derivative class of Component");
+ static_assert(
+ is_base_of<Component, T>::value,
+ "add_component must recieve a derivative class of Component"
+ );
// Determine the type of T (this is used as the key of the unordered_map<>)
type_index type = typeid(T);
@@ -40,8 +42,8 @@ T & ComponentManager::add_component(game_object_id_t id, Args &&... args) {
// Check if the vector size is not greater than get_instances_max
int max_instances = instance->get_instances_max();
if (max_instances != -1 && components[type][id].size() >= max_instances) {
- throw std::runtime_error(
- "Exceeded maximum number of instances for this component type");
+ throw std::runtime_error("Exceeded maximum number of instances for this component type"
+ );
}
// store its unique_ptr in the vector<>
@@ -95,8 +97,10 @@ template <typename T>
RefVector<T> ComponentManager::get_components_by_id(game_object_id_t id) const {
using namespace std;
- static_assert(is_base_of<Component, T>::value,
- "get_components_by_id must recieve a derivative class of Component");
+ static_assert(
+ is_base_of<Component, T>::value,
+ "get_components_by_id must recieve a derivative class of Component"
+ );
type_index type = typeid(T);
if (!this->components.contains(type)) return {};
@@ -170,8 +174,8 @@ ComponentManager::get_objects_by_predicate(const std::function<bool(const T &)>
}
template <typename T>
-RefVector<T>
-ComponentManager::get_components_by_ids(const std::set<game_object_id_t> & ids) const {
+RefVector<T> ComponentManager::get_components_by_ids(const std::set<game_object_id_t> & ids
+) const {
using namespace std;
RefVector<T> out = {};
diff --git a/src/crepe/manager/EventManager.h b/src/crepe/manager/EventManager.h
index 639e37f..5766a0c 100644
--- a/src/crepe/manager/EventManager.h
+++ b/src/crepe/manager/EventManager.h
@@ -49,8 +49,8 @@ public:
* \return A unique subscription ID associated with the registered callback.
*/
template <typename EventType>
- subscription_t subscribe(const EventHandler<EventType> & callback,
- event_channel_t channel = CHANNEL_ALL);
+ subscription_t
+ subscribe(const EventHandler<EventType> & callback, event_channel_t channel = CHANNEL_ALL);
/**
* \brief Unsubscribe a previously registered callback.
@@ -106,7 +106,7 @@ private:
* \brief Represents an entry in the event queue.
*/
struct QueueEntry {
- std::unique_ptr<Event> event; ///< The event instance.
+ std::unique_ptr<Event, std::function<void(Event *)>> event; ///< The event instance.
event_channel_t channel = CHANNEL_ALL; ///< The channel associated with the event.
std::type_index type; ///< The type of the event.
};
diff --git a/src/crepe/manager/EventManager.hpp b/src/crepe/manager/EventManager.hpp
index a5f4556..1f44943 100644
--- a/src/crepe/manager/EventManager.hpp
+++ b/src/crepe/manager/EventManager.hpp
@@ -5,24 +5,31 @@
namespace crepe {
template <typename EventType>
-subscription_t EventManager::subscribe(const EventHandler<EventType> & callback,
- event_channel_t channel) {
+subscription_t
+EventManager::subscribe(const EventHandler<EventType> & callback, event_channel_t channel) {
subscription_counter++;
std::type_index event_type = typeid(EventType);
std::unique_ptr<EventHandlerWrapper<EventType>> handler
= std::make_unique<EventHandlerWrapper<EventType>>(callback);
std::vector<CallbackEntry> & handlers = this->subscribers[event_type];
- handlers.emplace_back(CallbackEntry{
- .callback = std::move(handler), .channel = channel, .id = subscription_counter});
+ handlers.emplace_back(CallbackEntry {
+ .callback = std::move(handler), .channel = channel, .id = subscription_counter
+ });
return subscription_counter;
}
template <typename EventType>
void EventManager::queue_event(const EventType & event, event_channel_t channel) {
- static_assert(std::is_base_of<Event, EventType>::value,
- "EventType must derive from Event");
+ static_assert(
+ std::is_base_of<Event, EventType>::value, "EventType must derive from Event"
+ );
this->events_queue.push_back(QueueEntry{
- .event = std::make_unique<EventType>(event),
+ // unique_ptr w/ custom destructor implementation is used because the base Event interface
+ // can't be polymorphic (= have default virtual destructor)
+ .event = {
+ new EventType(event),
+ [](Event * ev) { delete static_cast<EventType *>(ev); },
+ },
.channel = channel,
.type = typeid(EventType),
});
diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp
index e78f92f..b4cd07f 100644
--- a/src/crepe/manager/LoopTimerManager.cpp
+++ b/src/crepe/manager/LoopTimerManager.cpp
@@ -17,9 +17,9 @@ LoopTimerManager::LoopTimerManager(Mediator & mediator) : Manager(mediator) {
void LoopTimerManager::start() {
this->last_frame_time = std::chrono::steady_clock::now();
- this->elapsed_time = elapsed_time_t{0};
- this->elapsed_fixed_time = elapsed_time_t{0};
- this->delta_time = duration_t{0};
+ this->elapsed_time = elapsed_time_t {0};
+ this->elapsed_fixed_time = elapsed_time_t {0};
+ this->delta_time = duration_t {0};
}
void LoopTimerManager::update() {
diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h
index 2f1e6b6..279d6b2 100644
--- a/src/crepe/manager/LoopTimerManager.h
+++ b/src/crepe/manager/LoopTimerManager.h
@@ -157,17 +157,17 @@ private:
//! 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};
+ duration_t maximum_delta_time {0.25};
//! Delta time for the current frame in seconds.
- duration_t delta_time{0.0};
+ duration_t delta_time {0.0};
//! Target time per frame in seconds
- duration_t frame_target_time{1.0 / target_fps};
+ duration_t frame_target_time {1.0 / target_fps};
//! Fixed delta time for fixed updates in seconds.
- duration_t fixed_delta_time{1.0 / 50.0};
+ duration_t fixed_delta_time {1.0 / 50.0};
//! Total elapsed game time in microseconds.
- elapsed_time_t elapsed_time{0};
+ elapsed_time_t elapsed_time {0};
//! Total elapsed time for fixed updates in microseconds.
- elapsed_time_t elapsed_fixed_time{0};
+ elapsed_time_t elapsed_fixed_time {0};
typedef std::chrono::steady_clock::time_point time_point_t;
//! Time of the last frame.
diff --git a/src/crepe/manager/ResourceManager.hpp b/src/crepe/manager/ResourceManager.hpp
index cf5c949..4ca6be0 100644
--- a/src/crepe/manager/ResourceManager.hpp
+++ b/src/crepe/manager/ResourceManager.hpp
@@ -9,17 +9,20 @@ namespace crepe {
template <typename T>
T & ResourceManager::get(const Asset & asset) {
using namespace std;
- static_assert(is_base_of<Resource, T>::value,
- "cache must recieve a derivative class of Resource");
+ static_assert(
+ is_base_of<Resource, T>::value, "cache must recieve a derivative class of Resource"
+ );
CacheEntry & entry = this->get_entry(asset);
if (entry.resource == nullptr) entry.resource = make_unique<T>(asset, this->mediator);
T * concrete_resource = dynamic_cast<T *>(entry.resource.get());
if (concrete_resource == nullptr)
- throw runtime_error(format("ResourceManager: mismatch between requested type and "
- "actual type of resource ({})",
- asset.get_path()));
+ throw runtime_error(format(
+ "ResourceManager: mismatch between requested type and "
+ "actual type of resource ({})",
+ asset.get_path()
+ ));
return *concrete_resource;
}
diff --git a/src/crepe/manager/SceneManager.cpp b/src/crepe/manager/SceneManager.cpp
index d4ca90b..e6f92db 100644
--- a/src/crepe/manager/SceneManager.cpp
+++ b/src/crepe/manager/SceneManager.cpp
@@ -17,10 +17,12 @@ void SceneManager::load_next_scene() {
// next scene not set
if (this->next_scene.empty()) return;
- auto it = find_if(this->scenes.begin(), this->scenes.end(),
- [&next_scene = this->next_scene](unique_ptr<Scene> & scene) {
- return scene.get()->get_name() == next_scene;
- });
+ auto it = find_if(
+ this->scenes.begin(), this->scenes.end(),
+ [&next_scene = this->next_scene](unique_ptr<Scene> & scene) {
+ return scene.get()->get_name() == next_scene;
+ }
+ );
// next scene not found
if (it == this->scenes.end()) return;
diff --git a/src/crepe/manager/SystemManager.cpp b/src/crepe/manager/SystemManager.cpp
index 5ada30f..eabc022 100644
--- a/src/crepe/manager/SystemManager.cpp
+++ b/src/crepe/manager/SystemManager.cpp
@@ -19,12 +19,12 @@ SystemManager::SystemManager(Mediator & mediator) : Manager(mediator) {
this->load_system<InputSystem>();
this->load_system<EventSystem>();
this->load_system<ScriptSystem>();
+ this->load_system<ParticleSystem>();
this->load_system<AISystem>();
this->load_system<PhysicsSystem>();
this->load_system<CollisionSystem>();
this->load_system<AudioSystem>();
this->load_system<AnimatorSystem>();
- this->load_system<ParticleSystem>();
this->load_system<RenderSystem>();
this->load_system<ReplaySystem>();
@@ -32,16 +32,16 @@ SystemManager::SystemManager(Mediator & mediator) : Manager(mediator) {
}
void SystemManager::fixed_update() {
- for (auto & [type, system] : this->systems) {
- if (!system->active) continue;
- system->fixed_update();
+ for (System & system : this->system_order) {
+ if (!system.active) continue;
+ system.fixed_update();
}
}
void SystemManager::frame_update() {
- for (auto & [type, system] : this->systems) {
- if (!system->active) continue;
- system->frame_update();
+ for (System & system : this->system_order) {
+ if (!system.active) continue;
+ system.frame_update();
}
}
diff --git a/src/crepe/manager/SystemManager.h b/src/crepe/manager/SystemManager.h
index 50acf77..614d90c 100644
--- a/src/crepe/manager/SystemManager.h
+++ b/src/crepe/manager/SystemManager.h
@@ -3,6 +3,7 @@
#include <memory>
#include <typeindex>
#include <unordered_map>
+#include <vector>
#include "../system/System.h"
@@ -43,6 +44,13 @@ private:
*/
std::unordered_map<std::type_index, std::unique_ptr<System>> systems;
/**
+ * \brief Collection of System instances
+ *
+ * This map holds System instances indexed by the system's class typeid. It is filled in the
+ * constructor of \c SystemManager using SystemManager::load_system.
+ */
+ std::vector<std::reference_wrapper<System>> system_order;
+ /**
* \brief Initialize a system
* \tparam T System type (must be derivative of \c System)
*/
diff --git a/src/crepe/manager/SystemManager.hpp b/src/crepe/manager/SystemManager.hpp
index 3d26e4c..addd274 100644
--- a/src/crepe/manager/SystemManager.hpp
+++ b/src/crepe/manager/SystemManager.hpp
@@ -11,8 +11,9 @@ namespace crepe {
template <class T>
T & SystemManager::get_system() {
using namespace std;
- static_assert(is_base_of<System, T>::value,
- "get_system must recieve a derivative class of System");
+ static_assert(
+ is_base_of<System, T>::value, "get_system must recieve a derivative class of System"
+ );
const type_info & type = typeid(T);
if (!this->systems.contains(type))
@@ -28,14 +29,16 @@ T & SystemManager::get_system() {
template <class T>
void SystemManager::load_system() {
using namespace std;
- static_assert(is_base_of<System, T>::value,
- "load_system must recieve a derivative class of System");
+ static_assert(
+ is_base_of<System, T>::value, "load_system must recieve a derivative class of System"
+ );
const type_info & type = typeid(T);
if (this->systems.contains(type))
throw runtime_error(format("SystemManager: {} is already initialized", type.name()));
System * system = new T(this->mediator);
this->systems[type] = unique_ptr<System>(system);
+ this->system_order.push_back(*this->systems[type]);
}
} // namespace crepe
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index 0f35010..94445c7 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -28,7 +28,8 @@ void AISystem::fixed_update() {
= mgr.get_components_by_id<Rigidbody>(ai.game_object_id);
if (rigidbodies.empty()) {
throw std::runtime_error(
- "AI component must be attached to a GameObject with a Rigidbody component");
+ "AI component must be attached to a GameObject with a Rigidbody component"
+ );
}
Rigidbody & rigidbody = rigidbodies.front().get();
if (!rigidbody.active) {
@@ -110,8 +111,8 @@ bool AISystem::accumulate_force(const AI & ai, vec2 & running_total, vec2 & forc
return true;
}
-vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody,
- const Transform & transform) const {
+vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody, const Transform & transform)
+ const {
// Calculate the desired velocity
vec2 desired_velocity = ai.seek_target - transform.position;
desired_velocity.normalize();
@@ -120,12 +121,12 @@ vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody,
return desired_velocity - rigidbody.data.linear_velocity;
}
-vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody,
- const Transform & transform) const {
+vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody, const Transform & transform)
+ const {
// Calculate the desired velocity if the entity is within the panic distance
vec2 desired_velocity = transform.position - ai.flee_target;
if (desired_velocity.length_squared() > ai.square_flee_panic_distance) {
- return vec2{0, 0};
+ return vec2 {0, 0};
}
desired_velocity.normalize();
desired_velocity *= rigidbody.data.max_linear_velocity;
@@ -133,8 +134,8 @@ vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody,
return desired_velocity - rigidbody.data.linear_velocity;
}
-vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody,
- const Transform & transform) const {
+vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody, const Transform & transform)
+ const {
// Calculate the desired velocity (taking into account the deceleration rate)
vec2 to_target = ai.arrive_target - transform.position;
float distance = to_target.length();
@@ -150,12 +151,12 @@ vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody,
return desired_velocity - rigidbody.data.linear_velocity;
}
- return vec2{0, 0};
+ return vec2 {0, 0};
}
vec2 AISystem::path_follow(AI & ai, const Rigidbody & rigidbody, const Transform & transform) {
if (ai.path.empty()) {
- return vec2{0, 0};
+ return vec2 {0, 0};
}
// Get the target node
diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp
index e5ab2fa..143d5d6 100644
--- a/src/crepe/system/AnimatorSystem.cpp
+++ b/src/crepe/system/AnimatorSystem.cpp
@@ -1,7 +1,8 @@
+#include <chrono>
+
#include "../api/Animator.h"
#include "../manager/ComponentManager.h"
#include "../manager/LoopTimerManager.h"
-#include <chrono>
#include "AnimatorSystem.h"
@@ -13,28 +14,31 @@ void AnimatorSystem::frame_update() {
LoopTimerManager & timer = this->mediator.loop_timer;
RefVector<Animator> animations = mgr.get_components_by_type<Animator>();
- float elapsed_time = duration_cast<duration<float>>(timer.get_elapsed_time()).count();
+ duration_t elapsed_time = timer.get_delta_time();
for (Animator & a : animations) {
if (!a.active) continue;
if (a.data.fps == 0) continue;
Animator::Data & ctx = a.data;
- float frame_duration = 1.0f / ctx.fps;
- int last_frame = ctx.row;
+ a.elapsed_time += elapsed_time;
+ duration_t frame_duration = 1000ms / ctx.fps;
int cycle_end = (ctx.cycle_end == -1) ? a.grid_size.x : ctx.cycle_end;
- int total_frames = cycle_end - ctx.cycle_start;
-
- int curr_frame = static_cast<int>(elapsed_time / frame_duration) % total_frames;
+ if (a.elapsed_time >= frame_duration) {
+ a.elapsed_time = 0ms;
+ a.frame++;
+ if (a.frame == cycle_end) {
+ a.frame = ctx.cycle_start;
+ if (!ctx.looping) {
+ a.active = false;
+ continue;
+ }
+ }
+ }
- ctx.row = ctx.cycle_start + curr_frame;
+ ctx.row = ctx.cycle_start + a.frame;
a.spritesheet.mask.x = ctx.row * a.spritesheet.mask.w;
- a.spritesheet.mask.y = (ctx.col * a.spritesheet.mask.h);
-
- if (!ctx.looping && curr_frame == ctx.cycle_start && last_frame == total_frames - 1) {
- a.active = false;
- }
}
}
diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp
index d4e8b9f..3c2232f 100644
--- a/src/crepe/system/AudioSystem.cpp
+++ b/src/crepe/system/AudioSystem.cpp
@@ -36,6 +36,8 @@ void AudioSystem::diff_update(AudioSource & component, Sound & resource) {
if (component.oneshot_play) {
component.voice = context.play(resource);
+ context.set_loop(component.voice, component.loop);
+ context.set_volume(component.voice, component.volume);
component.oneshot_play = false;
}
if (component.oneshot_stop) {
diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp
index 9d88d9f..571ac70 100644
--- a/src/crepe/system/CollisionSystem.cpp
+++ b/src/crepe/system/CollisionSystem.cpp
@@ -1,6 +1,7 @@
#include <algorithm>
#include <cmath>
#include <cstddef>
+#include <emmintrin.h>
#include <functional>
#include <optional>
#include <utility>
@@ -15,13 +16,23 @@
#include "api/Rigidbody.h"
#include "api/Transform.h"
#include "api/Vector2.h"
+#include "util/AbsolutePosition.h"
+#include "util/OptionalRef.h"
-#include "Collider.h"
#include "CollisionSystem.h"
#include "types.h"
-#include "util/OptionalRef.h"
using namespace crepe;
+using enum Rigidbody::BodyType;
+
+CollisionSystem::CollisionInfo CollisionSystem::CollisionInfo::operator-() const {
+ return {
+ .self = this->other,
+ .other = this->self,
+ .resolution = -this->resolution,
+ .resolution_direction = this->resolution_direction,
+ };
+}
void CollisionSystem::fixed_update() {
std::vector<CollisionInternal> all_colliders;
@@ -33,15 +44,17 @@ void CollisionSystem::fixed_update() {
if (!rigidbody.active) continue;
id = rigidbody.game_object_id;
Transform & transform = mgr.get_components_by_id<Transform>(id).front().get();
+ Metadata & metadata = mgr.get_components_by_id<Metadata>(id).front().get();
// Check if the boxcollider is active and has the same id as the rigidbody.
RefVector<BoxCollider> boxcolliders = mgr.get_components_by_type<BoxCollider>();
for (BoxCollider & boxcollider : boxcolliders) {
if (boxcollider.game_object_id != id) continue;
if (!boxcollider.active) continue;
- all_colliders.push_back({.id = id,
- .collider = collider_variant{boxcollider},
- .transform = transform,
- .rigidbody = rigidbody});
+ all_colliders.push_back(
+ {.id = id,
+ .collider = collider_variant {boxcollider},
+ .info = {transform, rigidbody, metadata}}
+ );
}
// Check if the circlecollider is active and has the same id as the rigidbody.
RefVector<CircleCollider> circlecolliders
@@ -49,312 +62,461 @@ void CollisionSystem::fixed_update() {
for (CircleCollider & circlecollider : circlecolliders) {
if (circlecollider.game_object_id != id) continue;
if (!circlecollider.active) continue;
- all_colliders.push_back({.id = id,
- .collider = collider_variant{circlecollider},
- .transform = transform,
- .rigidbody = rigidbody});
+ all_colliders.push_back(
+ {.id = id,
+ .collider = collider_variant {circlecollider},
+ .info = {transform, rigidbody, metadata}}
+ );
}
}
- // Check between all colliders if there is a collision
+ // Check between all colliders if there is a collision (collision handling)
std::vector<std::pair<CollisionInternal, CollisionInternal>> collided
= this->gather_collisions(all_colliders);
- // For both objects call the collision handler
+ // For the object convert the info and call the collision handler if needed
for (auto & collision_pair : collided) {
- this->collision_handler_request(collision_pair.first, collision_pair.second);
- this->collision_handler_request(collision_pair.second, collision_pair.first);
+ // Convert internal struct to external struct
+ CollisionInfo info
+ = this->get_collision_info(collision_pair.first, collision_pair.second);
+ // Determine if and/or what collison handler is needed.
+ this->determine_collision_handler(info);
}
}
-void CollisionSystem::collision_handler_request(CollisionInternal & this_data,
- CollisionInternal & other_data) {
+// Below is for collision detection
+std::vector<std::pair<CollisionSystem::CollisionInternal, CollisionSystem::CollisionInternal>>
+CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) {
- CollisionInternalType type
- = this->get_collider_type(this_data.collider, other_data.collider);
- std::pair<vec2, CollisionSystem::Direction> resolution_data
- = this->collision_handler(this_data, other_data, type);
- ComponentManager & mgr = this->mediator.component_manager;
- OptionalRef<Metadata> this_metadata
- = mgr.get_components_by_id<Metadata>(this_data.id).front().get();
- OptionalRef<Metadata> other_metadata
- = mgr.get_components_by_id<Metadata>(other_data.id).front().get();
- OptionalRef<Collider> this_collider;
- OptionalRef<Collider> other_collider;
- switch (type) {
- case CollisionInternalType::BOX_BOX: {
- this_collider = std::get<std::reference_wrapper<BoxCollider>>(this_data.collider);
- other_collider
- = std::get<std::reference_wrapper<BoxCollider>>(other_data.collider);
- break;
- }
- case CollisionInternalType::BOX_CIRCLE: {
- this_collider = std::get<std::reference_wrapper<BoxCollider>>(this_data.collider);
- other_collider
- = std::get<std::reference_wrapper<CircleCollider>>(other_data.collider);
- break;
- }
- case CollisionInternalType::CIRCLE_BOX: {
- this_collider
- = std::get<std::reference_wrapper<CircleCollider>>(this_data.collider);
- other_collider
- = std::get<std::reference_wrapper<BoxCollider>>(other_data.collider);
- break;
- }
- case CollisionInternalType::CIRCLE_CIRCLE: {
- this_collider
- = std::get<std::reference_wrapper<CircleCollider>>(this_data.collider);
- other_collider
- = std::get<std::reference_wrapper<CircleCollider>>(other_data.collider);
- break;
+ // TODO:
+ // If no colliders skip
+ // Check if colliders has rigidbody if not skip
+
+ // TODO:
+ // If amount is higer than lets say 16 for now use quadtree otwerwise skip
+ // Quadtree code
+ // Quadtree is placed over the input vector
+
+ // Return data of collided colliders which are variants
+ std::vector<std::pair<CollisionInternal, CollisionInternal>> collisions_ret;
+ //using visit to visit the variant to access the active and id.
+ for (size_t i = 0; i < colliders.size(); ++i) {
+ for (size_t j = i + 1; j < colliders.size(); ++j) {
+ if (colliders[i].id == colliders[j].id) continue;
+ if (!should_collide(colliders[i], colliders[j])) continue;
+ CollisionInternalType type
+ = get_collider_type(colliders[i].collider, colliders[j].collider);
+ if (!detect_collision(colliders[i], colliders[j], type)) continue;
+ //fet
+ collisions_ret.emplace_back(colliders[i], colliders[j]);
}
}
+ return collisions_ret;
+}
- // collision info
- crepe::CollisionSystem::CollisionInfo collision_info{
- .this_collider = this_collider,
- .this_transform = this_data.transform,
- .this_rigidbody = this_data.rigidbody,
- .this_metadata = this_metadata,
- .other_collider = other_collider,
- .other_transform = other_data.transform,
- .other_rigidbody = other_data.rigidbody,
- .other_metadata = other_metadata,
- .resolution = resolution_data.first,
- .resolution_direction = resolution_data.second,
- };
+bool CollisionSystem::should_collide(
+ const CollisionInternal & self, const CollisionInternal & other
+) const {
+
+ const Rigidbody::Data & self_rigidbody = self.info.rigidbody.data;
+ const Rigidbody::Data & other_rigidbody = other.info.rigidbody.data;
+ const Metadata & self_metadata = self.info.metadata;
+ const Metadata & other_metadata = other.info.metadata;
+
+ // Check collision layers
+ if (self_rigidbody.collision_layers.contains(other_rigidbody.collision_layer)) return true;
+ if (other_rigidbody.collision_layers.contains(self_rigidbody.collision_layer)) return true;
+
+ // Check names
+ if (self_rigidbody.collision_names.contains(other_metadata.name)) return true;
+ if (other_rigidbody.collision_names.contains(self_metadata.name)) return true;
+
+ // Check tags
+ if (self_rigidbody.collision_tags.contains(other_metadata.tag)) return true;
+ if (other_rigidbody.collision_tags.contains(self_metadata.tag)) return true;
- // Determine if static needs to be called
- this->determine_collision_handler(collision_info);
+ return false;
}
-std::pair<vec2, CollisionSystem::Direction>
-CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal & data2,
- CollisionInternalType type) {
+CollisionSystem::CollisionInternalType CollisionSystem::get_collider_type(
+ const collider_variant & collider1, const collider_variant & collider2
+) const {
+ if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider1)) {
+ if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) {
+ return CollisionInternalType::CIRCLE_CIRCLE;
+ } else {
+ return CollisionInternalType::CIRCLE_BOX;
+ }
+ } else {
+ if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) {
+ return CollisionInternalType::BOX_CIRCLE;
+ } else {
+ return CollisionInternalType::BOX_BOX;
+ }
+ }
+}
+
+bool CollisionSystem::detect_collision(
+ CollisionInternal & self, CollisionInternal & other, const CollisionInternalType & type
+) {
vec2 resolution;
switch (type) {
case CollisionInternalType::BOX_BOX: {
- const BoxCollider & collider1
- = std::get<std::reference_wrapper<BoxCollider>>(data1.collider);
- const BoxCollider & collider2
- = std::get<std::reference_wrapper<BoxCollider>>(data2.collider);
- vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform,
- data1.rigidbody);
- vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform,
- data2.rigidbody);
- resolution = this->get_box_box_resolution(collider1, collider2, collider_pos1,
- collider_pos2);
+ // Box-Box collision detection
+ const BoxColliderInternal BOX1
+ = {.collider = std::get<std::reference_wrapper<BoxCollider>>(self.collider),
+ .transform = self.info.transform,
+ .rigidbody = self.info.rigidbody};
+ const BoxColliderInternal BOX2
+ = {.collider = std::get<std::reference_wrapper<BoxCollider>>(other.collider),
+ .transform = other.info.transform,
+ .rigidbody = other.info.rigidbody};
+ // Get resolution vector from box-box collision detection
+ resolution = this->get_box_box_detection(BOX1, BOX2);
+ // If no collision (NaN values), return false
+ if (resolution.is_nan()) return false;
break;
}
case CollisionInternalType::BOX_CIRCLE: {
- const BoxCollider & collider1
- = std::get<std::reference_wrapper<BoxCollider>>(data1.collider);
- const CircleCollider & collider2
- = std::get<std::reference_wrapper<CircleCollider>>(data2.collider);
- vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform,
- data1.rigidbody);
- vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform,
- data2.rigidbody);
- resolution = -this->get_circle_box_resolution(collider2, collider1, collider_pos2,
- collider_pos1);
+ // Box-Circle collision detection
+ const BoxColliderInternal BOX1
+ = {.collider = std::get<std::reference_wrapper<BoxCollider>>(self.collider),
+ .transform = self.info.transform,
+ .rigidbody = self.info.rigidbody};
+ const CircleColliderInternal CIRCLE2
+ = {.collider
+ = std::get<std::reference_wrapper<CircleCollider>>(other.collider),
+ .transform = other.info.transform,
+ .rigidbody = other.info.rigidbody};
+ // Get resolution vector from box-circle collision detection
+ resolution = this->get_box_circle_detection(BOX1, CIRCLE2);
+ // If no collision (NaN values), return false
+ if (resolution.is_nan()) return false;
+ // Invert the resolution vector for proper collision response
+ resolution = -resolution;
break;
}
case CollisionInternalType::CIRCLE_CIRCLE: {
- const CircleCollider & collider1
- = std::get<std::reference_wrapper<CircleCollider>>(data1.collider);
- const CircleCollider & collider2
- = std::get<std::reference_wrapper<CircleCollider>>(data2.collider);
- vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform,
- data1.rigidbody);
- vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform,
- data2.rigidbody);
- resolution = this->get_circle_circle_resolution(collider1, collider2,
- collider_pos1, collider_pos2);
+ // Circle-Circle collision detection
+ const CircleColliderInternal CIRCLE1
+ = {.collider = std::get<std::reference_wrapper<CircleCollider>>(self.collider),
+ .transform = self.info.transform,
+ .rigidbody = self.info.rigidbody};
+ const CircleColliderInternal CIRCLE2
+ = {.collider
+ = std::get<std::reference_wrapper<CircleCollider>>(other.collider),
+ .transform = other.info.transform,
+ .rigidbody = other.info.rigidbody};
+ // Get resolution vector from circle-circle collision detection
+ resolution = this->get_circle_circle_detection(CIRCLE1, CIRCLE2);
+ // If no collision (NaN values), return false
+ if (resolution.is_nan()) return false;
break;
}
case CollisionInternalType::CIRCLE_BOX: {
- const CircleCollider & collider1
- = std::get<std::reference_wrapper<CircleCollider>>(data1.collider);
- const BoxCollider & collider2
- = std::get<std::reference_wrapper<BoxCollider>>(data2.collider);
- vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform,
- data1.rigidbody);
- vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform,
- data2.rigidbody);
- resolution = this->get_circle_box_resolution(collider1, collider2, collider_pos1,
- collider_pos2);
+ // Circle-Box collision detection
+ const CircleColliderInternal CIRCLE1
+ = {.collider = std::get<std::reference_wrapper<CircleCollider>>(self.collider),
+ .transform = self.info.transform,
+ .rigidbody = self.info.rigidbody};
+ const BoxColliderInternal BOX2
+ = {.collider = std::get<std::reference_wrapper<BoxCollider>>(other.collider),
+ .transform = other.info.transform,
+ .rigidbody = other.info.rigidbody};
+ // Get resolution vector from box-circle collision detection (order swapped)
+ resolution = this->get_box_circle_detection(BOX2, CIRCLE1);
+ // If no collision (NaN values), return false
+ if (resolution.is_nan()) return false;
break;
}
+ case CollisionInternalType::NONE:
+ // No collision detection needed if the type is NONE
+ return false;
+ break;
}
+ // Store the calculated resolution vector for the 'self' collider
+ self.resolution = resolution;
+ // Calculate the resolution direction based on the rigidbody data
+ self.resolution_direction
+ = this->resolution_correction(self.resolution, self.info.rigidbody.data);
+ // For the 'other' collider, the resolution is the opposite direction of 'self'
+ other.resolution = -self.resolution;
+ other.resolution_direction = self.resolution_direction;
+
+ // Return true if a collision was detected and resolution was calculated
+ return true;
+}
- Direction resolution_direction = Direction::NONE;
- if (resolution.x != 0 && resolution.y != 0) {
- resolution_direction = Direction::BOTH;
- } else if (resolution.x != 0) {
- resolution_direction = Direction::X_DIRECTION;
- //checks if the other velocity has a value and if this object moved
- if (data1.rigidbody.data.linear_velocity.x != 0
- && data1.rigidbody.data.linear_velocity.y != 0)
- resolution.y = -data1.rigidbody.data.linear_velocity.y
- * (resolution.x / data1.rigidbody.data.linear_velocity.x);
- } else if (resolution.y != 0) {
- resolution_direction = Direction::Y_DIRECTION;
- //checks if the other velocity has a value and if this object moved
- if (data1.rigidbody.data.linear_velocity.x != 0
- && data1.rigidbody.data.linear_velocity.y != 0)
- resolution.x = -data1.rigidbody.data.linear_velocity.x
- * (resolution.y / data1.rigidbody.data.linear_velocity.y);
- }
+vec2 CollisionSystem::get_box_box_detection(
+ const BoxColliderInternal & box1, const BoxColliderInternal & box2
+) const {
+ vec2 resolution {NAN, NAN};
+ // Get current positions of colliders
+ vec2 pos1 = AbsolutePosition::get_position(box1.transform, box1.collider.offset);
+ vec2 pos2 = AbsolutePosition::get_position(box2.transform, box2.collider.offset);
- return std::make_pair(resolution, resolution_direction);
-}
+ // Scale dimensions
+ vec2 scaled_box1 = box1.collider.dimensions * box1.transform.scale;
+ vec2 scaled_box2 = box2.collider.dimensions * box2.transform.scale;
+ vec2 delta = pos2 - pos1;
-vec2 CollisionSystem::get_box_box_resolution(const BoxCollider & box_collider1,
- const BoxCollider & box_collider2,
- const vec2 & final_position1,
- const vec2 & final_position2) const {
- vec2 resolution; // Default resolution vector
- vec2 delta = final_position2 - final_position1;
-
- // Compute half-dimensions of the boxes
- float half_width1 = box_collider1.dimensions.x / 2.0;
- float half_height1 = box_collider1.dimensions.y / 2.0;
- float half_width2 = box_collider2.dimensions.x / 2.0;
- float half_height2 = box_collider2.dimensions.y / 2.0;
-
- // Calculate overlaps along X and Y axes
- float overlap_x = (half_width1 + half_width2) - std::abs(delta.x);
- float overlap_y = (half_height1 + half_height2) - std::abs(delta.y);
-
- // Check if there is a collision should always be true
- if (overlap_x > 0 && overlap_y > 0) {
- // Determine the direction of resolution
- if (overlap_x < overlap_y) {
- // Resolve along the X-axis (smallest overlap)
- resolution.x = (delta.x > 0) ? -overlap_x : overlap_x;
- } else if (overlap_y < overlap_x) {
- // Resolve along the Y-axis (smallest overlap)
- resolution.y = (delta.y > 0) ? -overlap_y : overlap_y;
- } else {
- // Equal overlap, resolve both directions with preference
- resolution.x = (delta.x > 0) ? -overlap_x : overlap_x;
- resolution.y = (delta.y > 0) ? -overlap_y : overlap_y;
+ // Calculate half-extents (half width and half height)
+ float half_width1 = scaled_box1.x / 2.0;
+ float half_height1 = scaled_box1.y / 2.0;
+ float half_width2 = scaled_box2.x / 2.0;
+ float half_height2 = scaled_box2.y / 2.0;
+
+ if (pos1.x + half_width1 > pos2.x - half_width2
+ && pos1.x - half_width1 < pos2.x + half_width2
+ && pos1.y + half_height1 > pos2.y - half_height2
+ && pos1.y - half_height1 < pos2.y + half_height2) {
+ resolution = {0, 0};
+ float overlap_x = (half_width1 + half_width2) - std::abs(delta.x);
+ float overlap_y = (half_height1 + half_height2) - std::abs(delta.y);
+ if (overlap_x > 0 && overlap_y > 0) {
+ // Determine the direction of resolution
+ if (overlap_x < overlap_y) {
+ // Resolve along the X-axis (smallest overlap)
+ resolution.x = (delta.x > 0) ? -overlap_x : overlap_x;
+ } else if (overlap_y < overlap_x) {
+ // Resolve along the Y-axis (smallest overlap)
+ resolution.y = (delta.y > 0) ? -overlap_y : overlap_y;
+ } else {
+ // Equal overlap, resolve both directions with preference
+ resolution.x = (delta.x > 0) ? -overlap_x : overlap_x;
+ resolution.y = (delta.y > 0) ? -overlap_y : overlap_y;
+ }
}
}
-
return resolution;
}
-vec2 CollisionSystem::get_circle_circle_resolution(const CircleCollider & circle_collider1,
- const CircleCollider & circle_collider2,
- const vec2 & final_position1,
- const vec2 & final_position2) const {
- vec2 delta = final_position2 - final_position1;
+vec2 CollisionSystem::get_box_circle_detection(
+ const BoxColliderInternal & box, const CircleColliderInternal & circle
+) const {
+ /// Get current positions of colliders
+ vec2 box_pos = AbsolutePosition::get_position(box.transform, box.collider.offset);
+ vec2 circle_pos = AbsolutePosition::get_position(circle.transform, circle.collider.offset);
- // Compute the distance between the two circle centers
- float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y);
+ // Scale dimensions
+ vec2 scaled_box = box.collider.dimensions * box.transform.scale;
+ float scaled_circle_radius = circle.collider.radius * circle.transform.scale;
- // Compute the combined radii of the two circles
- float combined_radius = circle_collider1.radius + circle_collider2.radius;
+ // Calculate box half-extents
+ float half_width = scaled_box.x / 2.0f;
+ float half_height = scaled_box.y / 2.0f;
- // Compute the penetration depth
- float penetration_depth = combined_radius - distance;
+ // Find the closest point on the box to the circle's center
+ float closest_x
+ = std::max(box_pos.x - half_width, std::min(circle_pos.x, box_pos.x + half_width));
+ float closest_y
+ = std::max(box_pos.y - half_height, std::min(circle_pos.y, box_pos.y + half_height));
- // Normalize the delta vector to get the collision direction
- vec2 collision_normal = delta / distance;
+ float distance_x = circle_pos.x - closest_x;
+ float distance_y = circle_pos.y - closest_y;
+ float distance_squared = distance_x * distance_x + distance_y * distance_y;
+ if (distance_squared < scaled_circle_radius * scaled_circle_radius) {
+ vec2 delta = circle_pos - box_pos;
- // Compute the resolution vector
- vec2 resolution = -collision_normal * penetration_depth;
+ // Clamp circle center to the nearest point on the box
+ vec2 closest_point;
+ closest_point.x = std::clamp(delta.x, -half_width, half_width);
+ closest_point.y = std::clamp(delta.y, -half_height, half_height);
- return resolution;
+ // Find the vector from the circle center to the closest point
+ vec2 closest_delta = delta - closest_point;
+
+ float distance
+ = std::sqrt(closest_delta.x * closest_delta.x + closest_delta.y * closest_delta.y);
+ vec2 collision_normal = closest_delta / distance;
+
+ // Compute penetration depth
+ float penetration_depth = scaled_circle_radius - distance;
+
+ // Compute the resolution vector
+ return vec2 {collision_normal * penetration_depth};
+ }
+ // No collision
+ return vec2 {NAN, NAN};
}
-vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider & circle_collider,
- const BoxCollider & box_collider,
- const vec2 & circle_position,
- const vec2 & box_position) const {
- vec2 delta = circle_position - box_position;
+vec2 CollisionSystem::get_circle_circle_detection(
+ const CircleColliderInternal & circle1, const CircleColliderInternal & circle2
+) const {
+ // Get current positions of colliders
+ vec2 final_position1
+ = AbsolutePosition::get_position(circle1.transform, circle1.collider.offset);
+ vec2 final_position2
+ = AbsolutePosition::get_position(circle2.transform, circle2.collider.offset);
- // Compute half-dimensions of the box
- float half_width = box_collider.dimensions.x / 2.0f;
- float half_height = box_collider.dimensions.y / 2.0f;
+ // Scale dimensions
+ float scaled_circle1 = circle1.collider.radius * circle1.transform.scale;
+ float scaled_circle2 = circle2.collider.radius * circle2.transform.scale;
- // Clamp circle center to the nearest point on the box
- vec2 closest_point;
- closest_point.x = std::clamp(delta.x, -half_width, half_width);
- closest_point.y = std::clamp(delta.y, -half_height, half_height);
+ float distance_x = final_position1.x - final_position2.x;
+ float distance_y = final_position1.y - final_position2.y;
+ float distance_squared = distance_x * distance_x + distance_y * distance_y;
+
+ // Calculate the sum of the radii
+ float radius_sum = scaled_circle1 + scaled_circle2;
- // Find the vector from the circle center to the closest point
- vec2 closest_delta = delta - closest_point;
+ // Check for collision (distance squared must be less than the square of the radius sum)
+ if (distance_squared < radius_sum * radius_sum) {
+ vec2 delta = final_position2 - final_position1;
- // Normalize the delta to get the collision direction
- float distance
- = std::sqrt(closest_delta.x * closest_delta.x + closest_delta.y * closest_delta.y);
- vec2 collision_normal = closest_delta / distance;
+ // Compute the distance between the two circle centers
+ float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y);
- // Compute penetration depth
- float penetration_depth = circle_collider.radius - distance;
+ // Compute the combined radii of the two circles
+ float combined_radius = scaled_circle1 + scaled_circle2;
- // Compute the resolution vector
- vec2 resolution = collision_normal * penetration_depth;
+ // Compute the penetration depth
+ float penetration_depth = combined_radius - distance;
- return resolution;
+ // Normalize the delta vector to get the collision direction
+ vec2 collision_normal = delta / distance;
+
+ // Compute the resolution vector
+ vec2 resolution = -collision_normal * penetration_depth;
+
+ return resolution;
+ }
+ // No collision
+ return vec2 {NAN, NAN};
+ ;
+}
+
+CollisionSystem::Direction
+CollisionSystem::resolution_correction(vec2 & resolution, const Rigidbody::Data & rigidbody) {
+
+ // Calculate the other value to move back correctly
+ // If only X or Y has a value determine what is should be to move back.
+ Direction resolution_direction = Direction::NONE;
+ // If both are not zero a perfect corner has been hit
+ if (resolution.x != 0 && resolution.y != 0) {
+ resolution_direction = Direction::BOTH;
+ // If x is not zero a horizontal action was latest action.
+ } else if (resolution.x != 0) {
+ resolution_direction = Direction::X_DIRECTION;
+ // If both are 0 resolution y should not be changed (y_velocity can be 0 by kinematic object movement)
+ if (rigidbody.linear_velocity.x != 0 && rigidbody.linear_velocity.y != 0)
+ resolution.y
+ = -rigidbody.linear_velocity.y * (resolution.x / rigidbody.linear_velocity.x);
+ } else if (resolution.y != 0) {
+ resolution_direction = Direction::Y_DIRECTION;
+ // If both are 0 resolution x should not be changed (x_velocity can be 0 by kinematic object movement)
+ if (rigidbody.linear_velocity.x != 0 && rigidbody.linear_velocity.y != 0)
+ resolution.x
+ = -rigidbody.linear_velocity.x * (resolution.y / rigidbody.linear_velocity.y);
+ }
+
+ return resolution_direction;
}
-void CollisionSystem::determine_collision_handler(CollisionInfo & info) {
- // Check rigidbody type for static
- if (info.this_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) return;
- // If second body is static perform the static collision handler in this system
- if (info.other_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) {
- this->static_collision_handler(info);
+CollisionSystem::CollisionInfo CollisionSystem::get_collision_info(
+ const CollisionInternal & in_self, const CollisionInternal & in_other
+) const {
+
+ crepe::CollisionSystem::ColliderInfo self {
+ .transform = in_self.info.transform,
+ .rigidbody = in_self.info.rigidbody,
+ .metadata = in_self.info.metadata,
};
- // Call collision event for user
- CollisionEvent data(info);
- EventManager & emgr = this->mediator.event_manager;
- emgr.trigger_event<CollisionEvent>(data, info.this_collider.game_object_id);
+
+ crepe::CollisionSystem::ColliderInfo other {
+ .transform = in_other.info.transform,
+ .rigidbody = in_other.info.rigidbody,
+ .metadata = in_other.info.metadata,
+ };
+
+ struct CollisionInfo collision_info {
+ .self = self, .other = other, .resolution = in_self.resolution,
+ .resolution_direction = in_self.resolution_direction,
+ };
+ return collision_info;
}
-void CollisionSystem::static_collision_handler(CollisionInfo & info) {
+void CollisionSystem::determine_collision_handler(const CollisionInfo & info) {
+ Rigidbody::BodyType self_type = info.self.rigidbody.data.body_type;
+ Rigidbody::BodyType other_type = info.other.rigidbody.data.body_type;
+ bool self_kinematic = info.self.rigidbody.data.kinematic_collision;
+ bool other_kinematic = info.other.rigidbody.data.kinematic_collision;
+ // Inverted collision info
+ CollisionInfo inverted = -info;
+ // If both objects are static skip handle call collision script
+ if (self_type == STATIC && other_type == STATIC) return;
+
+ // First body is not dynamic
+ if (self_type != DYNAMIC) {
+ bool static_collision = self_type == STATIC && other_type == DYNAMIC;
+ bool kinematic_collision
+ = self_type == KINEMATIC && other_type == DYNAMIC && self_kinematic;
+
+ // Handle collision
+ if (static_collision || kinematic_collision) this->static_collision_handler(inverted);
+ // Call scripts
+ this->call_collision_events(inverted);
+ return;
+ }
+
+ // Second body is not dynamic
+ if (other_type != DYNAMIC) {
+ bool static_collision = other_type == STATIC;
+ bool kinematic_collision = other_type == KINEMATIC && other_kinematic;
+ // Handle collision
+ if (static_collision || kinematic_collision) this->static_collision_handler(info);
+ // Call scripts
+ this->call_collision_events(info);
+ return;
+ }
+
+ // Dynamic
+ // Handle collision
+ this->dynamic_collision_handler(info);
+ // Call scripts
+ this->call_collision_events(info);
+}
+
+void CollisionSystem::static_collision_handler(const CollisionInfo & info) {
+
+ vec2 & transform_pos = info.self.transform.position;
+ float elasticity = info.self.rigidbody.data.elasticity_coefficient;
+ vec2 & rigidbody_vel = info.self.rigidbody.data.linear_velocity;
+
// Move object back using calculate move back value
- info.this_transform.position += info.resolution;
+ transform_pos += info.resolution;
switch (info.resolution_direction) {
case Direction::BOTH:
//bounce
- if (info.this_rigidbody.data.elastisity_coefficient > 0) {
- info.this_rigidbody.data.linear_velocity
- = -info.this_rigidbody.data.linear_velocity
- * info.this_rigidbody.data.elastisity_coefficient;
+ if (elasticity > 0) {
+ rigidbody_vel = -rigidbody_vel * elasticity;
}
//stop movement
else {
- info.this_rigidbody.data.linear_velocity = {0, 0};
+ rigidbody_vel = {0, 0};
}
break;
case Direction::Y_DIRECTION:
// Bounce
- if (info.this_rigidbody.data.elastisity_coefficient > 0) {
- info.this_rigidbody.data.linear_velocity.y
- = -info.this_rigidbody.data.linear_velocity.y
- * info.this_rigidbody.data.elastisity_coefficient;
+ if (elasticity > 0) {
+ rigidbody_vel.y = -rigidbody_vel.y * elasticity;
}
// Stop movement
else {
- info.this_rigidbody.data.linear_velocity.y = 0;
- info.this_transform.position.x -= info.resolution.x;
+ rigidbody_vel.y = 0;
+ transform_pos.x -= info.resolution.x;
}
break;
case Direction::X_DIRECTION:
// Bounce
- if (info.this_rigidbody.data.elastisity_coefficient > 0) {
- info.this_rigidbody.data.linear_velocity.x
- = -info.this_rigidbody.data.linear_velocity.x
- * info.this_rigidbody.data.elastisity_coefficient;
+ if (elasticity > 0) {
+ rigidbody_vel.x = -rigidbody_vel.x * elasticity;
}
// Stop movement
else {
- info.this_rigidbody.data.linear_velocity.x = 0;
- info.this_transform.position.y -= info.resolution.y;
+ rigidbody_vel.x = 0;
+ transform_pos.y -= info.resolution.y;
}
break;
case Direction::NONE:
@@ -363,213 +525,80 @@ void CollisionSystem::static_collision_handler(CollisionInfo & info) {
}
}
-std::vector<std::pair<CollisionSystem::CollisionInternal, CollisionSystem::CollisionInternal>>
-CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) {
+void CollisionSystem::dynamic_collision_handler(const CollisionInfo & info) {
- // TODO:
- // If no colliders skip
- // Check if colliders has rigidbody if not skip
+ vec2 & self_transform_pos = info.self.transform.position;
+ vec2 & other_transform_pos = info.other.transform.position;
+ float self_elasticity = info.self.rigidbody.data.elasticity_coefficient;
+ float other_elasticity = info.other.rigidbody.data.elasticity_coefficient;
+ vec2 & self_rigidbody_vel = info.self.rigidbody.data.linear_velocity;
+ vec2 & other_rigidbody_vel = info.other.rigidbody.data.linear_velocity;
- // TODO:
- // If amount is higer than lets say 16 for now use quadtree otwerwise skip
- // Quadtree code
- // Quadtree is placed over the input vector
+ self_transform_pos += info.resolution / 2;
+ other_transform_pos += -(info.resolution / 2);
- // Return data of collided colliders which are variants
- std::vector<std::pair<CollisionInternal, CollisionInternal>> collisions_ret;
- //using visit to visit the variant to access the active and id.
- for (size_t i = 0; i < colliders.size(); ++i) {
- for (size_t j = i + 1; j < colliders.size(); ++j) {
- if (colliders[i].id == colliders[j].id) continue;
- if (!have_common_layer(colliders[i].rigidbody.data.collision_layers,
- colliders[j].rigidbody.data.collision_layers))
- continue;
- CollisionInternalType type
- = get_collider_type(colliders[i].collider, colliders[j].collider);
- if (!get_collision(
- {
- .collider = colliders[i].collider,
- .transform = colliders[i].transform,
- .rigidbody = colliders[i].rigidbody,
- },
- {
- .collider = colliders[j].collider,
- .transform = colliders[j].transform,
- .rigidbody = colliders[j].rigidbody,
- },
- type))
- continue;
- collisions_ret.emplace_back(colliders[i], colliders[j]);
- }
- }
-
- return collisions_ret;
-}
-
-bool CollisionSystem::have_common_layer(const std::set<int> & layers1,
- const std::set<int> & layers2) {
+ switch (info.resolution_direction) {
+ case Direction::BOTH:
+ if (self_elasticity > 0) {
+ self_rigidbody_vel = -self_rigidbody_vel * self_elasticity;
+ } else {
+ self_rigidbody_vel = {0, 0};
+ }
- // Check if any number is equal in the layers
- for (int num : layers1) {
- if (layers2.contains(num)) {
- // Common layer found
- return true;
+ if (other_elasticity > 0) {
+ other_rigidbody_vel = -other_rigidbody_vel * other_elasticity;
+ } else {
+ other_rigidbody_vel = {0, 0};
+ }
break;
- }
- }
- // No common layer found
- return false;
-}
+ case Direction::Y_DIRECTION:
+ if (self_elasticity > 0) {
+ self_rigidbody_vel.y = -self_rigidbody_vel.y * self_elasticity;
+ }
+ // Stop movement
+ else {
+ self_rigidbody_vel.y = 0;
+ self_transform_pos.x -= info.resolution.x;
+ }
-CollisionSystem::CollisionInternalType
-CollisionSystem::get_collider_type(const collider_variant & collider1,
- const collider_variant & collider2) const {
- if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider1)) {
- if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) {
- return CollisionInternalType::CIRCLE_CIRCLE;
- } else {
- return CollisionInternalType::CIRCLE_BOX;
- }
- } else {
- if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) {
- return CollisionInternalType::BOX_CIRCLE;
- } else {
- return CollisionInternalType::BOX_BOX;
- }
- }
-}
+ if (other_elasticity > 0) {
+ other_rigidbody_vel.y = -other_rigidbody_vel.y * other_elasticity;
+ }
+ // Stop movement
+ else {
+ other_rigidbody_vel.y = 0;
+ other_transform_pos.x -= info.resolution.x;
+ }
+ break;
+ case Direction::X_DIRECTION:
+ if (self_elasticity > 0) {
+ self_rigidbody_vel.x = -self_rigidbody_vel.x * self_elasticity;
+ }
+ // Stop movement
+ else {
+ self_rigidbody_vel.x = 0;
+ self_transform_pos.y -= info.resolution.y;
+ }
-bool CollisionSystem::get_collision(const CollisionInternal & first_info,
- const CollisionInternal & second_info,
- CollisionInternalType type) const {
- switch (type) {
- case CollisionInternalType::BOX_BOX: {
- const BoxCollider & box_collider1
- = std::get<std::reference_wrapper<BoxCollider>>(first_info.collider);
- const BoxCollider & box_collider2
- = std::get<std::reference_wrapper<BoxCollider>>(second_info.collider);
- return this->get_box_box_collision(box_collider1, box_collider2,
- first_info.transform, second_info.transform,
- second_info.rigidbody, second_info.rigidbody);
- }
- case CollisionInternalType::BOX_CIRCLE: {
- const BoxCollider & box_collider
- = std::get<std::reference_wrapper<BoxCollider>>(first_info.collider);
- const CircleCollider & circle_collider
- = std::get<std::reference_wrapper<CircleCollider>>(second_info.collider);
- return this->get_box_circle_collision(
- box_collider, circle_collider, first_info.transform, second_info.transform,
- second_info.rigidbody, second_info.rigidbody);
- }
- case CollisionInternalType::CIRCLE_CIRCLE: {
- const CircleCollider & circle_collider1
- = std::get<std::reference_wrapper<CircleCollider>>(first_info.collider);
- const CircleCollider & circle_collider2
- = std::get<std::reference_wrapper<CircleCollider>>(second_info.collider);
- return this->get_circle_circle_collision(
- circle_collider1, circle_collider2, first_info.transform,
- second_info.transform, second_info.rigidbody, second_info.rigidbody);
- }
- case CollisionInternalType::CIRCLE_BOX: {
- const CircleCollider & circle_collider
- = std::get<std::reference_wrapper<CircleCollider>>(first_info.collider);
- const BoxCollider & box_collider
- = std::get<std::reference_wrapper<BoxCollider>>(second_info.collider);
- return this->get_box_circle_collision(
- box_collider, circle_collider, first_info.transform, second_info.transform,
- second_info.rigidbody, second_info.rigidbody);
- }
+ if (other_elasticity > 0) {
+ other_rigidbody_vel.x = -other_rigidbody_vel.x * other_elasticity;
+ }
+ // Stop movement
+ else {
+ other_rigidbody_vel.x = 0;
+ other_transform_pos.y -= info.resolution.y;
+ }
+ break;
+ case Direction::NONE:
+ // Not possible
+ break;
}
- return false;
}
-bool CollisionSystem::get_box_box_collision(const BoxCollider & box1, const BoxCollider & box2,
- const Transform & transform1,
- const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const {
- // Get current positions of colliders
- vec2 final_position1 = this->get_current_position(box1.offset, transform1, rigidbody1);
- vec2 final_position2 = this->get_current_position(box2.offset, transform2, rigidbody2);
-
- // Calculate half-extents (half width and half height)
- float half_width1 = box1.dimensions.x / 2.0;
- float half_height1 = box1.dimensions.y / 2.0;
- float half_width2 = box2.dimensions.x / 2.0;
- float half_height2 = box2.dimensions.y / 2.0;
-
- // Check if the boxes overlap along the X and Y axes
- return (final_position1.x + half_width1 > final_position2.x - half_width2
- && final_position1.x - half_width1 < final_position2.x + half_width2
- && final_position1.y + half_height1 > final_position2.y - half_height2
- && final_position1.y - half_height1 < final_position2.y + half_height2);
-}
-
-bool CollisionSystem::get_box_circle_collision(const BoxCollider & box1,
- const CircleCollider & circle2,
- const Transform & transform1,
- const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const {
- // Get current positions of colliders
- vec2 final_position1 = this->get_current_position(box1.offset, transform1, rigidbody1);
- vec2 final_position2 = this->get_current_position(circle2.offset, transform2, rigidbody2);
-
- // Calculate box half-extents
- float half_width = box1.dimensions.x / 2.0;
- float half_height = box1.dimensions.y / 2.0;
-
- // Find the closest point on the box to the circle's center
- float closest_x = std::max(final_position1.x - half_width,
- std::min(final_position2.x, final_position1.x + half_width));
- float closest_y = std::max(final_position1.y - half_height,
- std::min(final_position2.y, final_position1.y + half_height));
-
- // Calculate the distance squared between the circle's center and the closest point on the box
- float distance_x = final_position2.x - closest_x;
- float distance_y = final_position2.y - closest_y;
- float distance_squared = distance_x * distance_x + distance_y * distance_y;
-
- // Compare distance squared with the square of the circle's radius
- return distance_squared < circle2.radius * circle2.radius;
-}
-
-bool CollisionSystem::get_circle_circle_collision(const CircleCollider & circle1,
- const CircleCollider & circle2,
- const Transform & transform1,
- const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const {
- // Get current positions of colliders
- vec2 final_position1 = this->get_current_position(circle1.offset, transform1, rigidbody1);
- vec2 final_position2 = this->get_current_position(circle2.offset, transform2, rigidbody2);
-
- float distance_x = final_position1.x - final_position2.x;
- float distance_y = final_position1.y - final_position2.y;
- float distance_squared = distance_x * distance_x + distance_y * distance_y;
-
- // Calculate the sum of the radii
- float radius_sum = circle1.radius + circle2.radius;
-
- // Check if the distance between the centers is less than or equal to the sum of the radii
- return distance_squared < radius_sum * radius_sum;
-}
-
-vec2 CollisionSystem::get_current_position(const vec2 & collider_offset,
- const Transform & transform,
- const Rigidbody & rigidbody) const {
- // Get the rotation in radians
- float radians1 = transform.rotation * (M_PI / 180.0);
-
- // Calculate total offset with scale
- vec2 total_offset = (rigidbody.data.offset + collider_offset) * transform.scale;
-
- // Rotate
- float rotated_total_offset_x1
- = total_offset.x * cos(radians1) - total_offset.y * sin(radians1);
- float rotated_total_offset_y1
- = total_offset.x * sin(radians1) + total_offset.y * cos(radians1);
-
- // Final positions considering scaling and rotation
- return (transform.position + vec2(rotated_total_offset_x1, rotated_total_offset_y1));
+void CollisionSystem::call_collision_events(const CollisionInfo & info) {
+ CollisionEvent data(info);
+ CollisionEvent data_inverted(-info);
+ EventManager & emgr = this->mediator.event_manager;
+ emgr.trigger_event<CollisionEvent>(data, info.self.transform.game_object_id);
+ emgr.trigger_event<CollisionEvent>(data_inverted, info.other.transform.game_object_id);
}
diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h
index 48a8f86..ff2d35f 100644
--- a/src/crepe/system/CollisionSystem.h
+++ b/src/crepe/system/CollisionSystem.h
@@ -23,33 +23,6 @@ public:
using System::System;
private:
- //! A variant type that can hold either a BoxCollider or a CircleCollider.
- using collider_variant = std::variant<std::reference_wrapper<BoxCollider>,
- std::reference_wrapper<CircleCollider>>;
-
- //! Enum representing the types of collider pairs for collision detection.
- enum class CollisionInternalType {
- BOX_BOX,
- CIRCLE_CIRCLE,
- BOX_CIRCLE,
- CIRCLE_BOX,
- };
-
- /**
- * \brief A structure to store the collision data of a single collider.
- *
- * This structure all components and id that are for needed within this system when calculating or handeling collisions.
- * The transform and rigidbody are mostly needed for location and rotation.
- * In rigidbody additional info is written about what the body of the object is,
- * and how it should respond on a collision.
- */
- struct CollisionInternal {
- game_object_id_t id = 0;
- collider_variant collider;
- Transform & transform;
- Rigidbody & rigidbody;
- };
-
//! Enum representing movement directions during collision resolution.
enum class Direction {
//! No movement required.
@@ -59,30 +32,74 @@ private:
//! Movement in the Y direction.
Y_DIRECTION,
//! Movement in both X and Y directions.
- BOTH
+ BOTH,
};
public:
+ //! Structure representing components of the collider
+ struct ColliderInfo {
+ Transform & transform;
+ Rigidbody & rigidbody;
+ Metadata & metadata;
+ };
+
/**
* \brief Structure representing detailed collision information between two colliders.
*
* Includes information about the colliding objects and the resolution data for handling the collision.
*/
struct CollisionInfo {
- Collider & this_collider;
- Transform & this_transform;
- Rigidbody & this_rigidbody;
- Metadata & this_metadata;
- Collider & other_collider;
- Transform & other_transform;
- Rigidbody & other_rigidbody;
- Metadata & other_metadata;
+ ColliderInfo self;
+ ColliderInfo other;
//! The resolution vector for the collision.
vec2 resolution;
//! The direction of movement for resolving the collision.
Direction resolution_direction = Direction::NONE;
+ CollisionInfo operator-() const;
};
+private:
+ //! A variant type that can hold either a BoxCollider or a CircleCollider.
+ using collider_variant = std::variant<
+ std::reference_wrapper<BoxCollider>, std::reference_wrapper<CircleCollider>>;
+
+ //! Enum representing the types of collider pairs for collision detection.
+ enum class CollisionInternalType {
+ BOX_BOX,
+ CIRCLE_CIRCLE,
+ BOX_CIRCLE,
+ CIRCLE_BOX,
+ NONE,
+ };
+
+ /**
+ * \brief A structure to store the collision data of a single collider.
+ *
+ * This structure all components and id that are for needed within this system when calculating or handling collisions.
+ * The transform and rigidbody are mostly needed for location and rotation.
+ * In rigidbody additional info is written about what the body of the object is,
+ * and how it should respond on a collision.
+ */
+ struct CollisionInternal {
+ game_object_id_t id = 0;
+ collider_variant collider;
+ ColliderInfo info;
+ vec2 resolution;
+ Direction resolution_direction = Direction::NONE;
+ };
+
+ //! Structure of a collider with additional components
+ template <typename ColliderType>
+ struct ColliderInternal {
+ ColliderType & collider;
+ Transform & transform;
+ Rigidbody & rigidbody;
+ };
+ //! Predefined BoxColliderInternal. (System is only made for this type)
+ using BoxColliderInternal = ColliderInternal<BoxCollider>;
+ //! Predefined CircleColliderInternal. (System is only made for this type)
+ using CircleColliderInternal = ColliderInternal<CircleCollider>;
+
public:
//! Updates the collision system by checking for collisions between colliders and handling them.
void fixed_update() override;
@@ -97,117 +114,94 @@ private:
* \param collider2 Second collider variant (BoxCollider or CircleCollider).
* \return The combined type of the two colliders.
*/
- CollisionInternalType get_collider_type(const collider_variant & collider1,
- const collider_variant & collider2) const;
-
- /**
- * \brief Calculates the current position of a collider.
- *
- * Combines the Collider offset, Transform position, and Rigidbody offset to compute the position of the collider.
- *
- * \param collider_offset The offset of the collider.
- * \param transform The Transform of the associated game object.
- * \param rigidbody The Rigidbody of the associated game object.
- * \return The calculated position of the collider.
- */
- vec2 get_current_position(const vec2 & collider_offset, const Transform & transform,
- const Rigidbody & rigidbody) const;
+ CollisionInternalType get_collider_type(
+ const collider_variant & collider1, const collider_variant & collider2
+ ) const;
private:
/**
- * \brief Handles collision resolution between two colliders.
+ * \brief Converts internal collision data into user-accessible collision information.
*
- * Processes collision data and adjusts objects to resolve collisions and/or calls the user oncollision script function.
+ * This function processes collision data from two colliding entities and packages it
+ * into a structured format that is accessible for further use,
+ * such as resolving collisions and triggering user-defined collision scripts.
*
* \param data1 Collision data for the first collider.
* \param data2 Collision data for the second collider.
*/
- void collision_handler_request(CollisionInternal & data1, CollisionInternal & data2);
+ CollisionInfo
+ get_collision_info(const CollisionInternal & data1, const CollisionInternal & data2) const;
/**
- * \brief Resolves collision between two colliders and calculates the movement required.
+ * \brief Corrects the collision resolution vector and determines its direction.
*
- * Determines the displacement and direction needed to separate colliders based on their types.
+ * This function adjusts the provided resolution vector based on the
+ * rigidbody's linear velocity to ensure consistent collision correction. If the resolution
+ * vector has only one non-zero component (either x or y), the missing component is computed
+ * based on the rigidbody's velocity. If both components are non-zero, it indicates a corner
+ * collision. The function also identifies the direction of the resolution and returns it.
*
- * \param data1 Collision data for the first collider.
- * \param data2 Collision data for the second collider.
- * \param type The type of collider pair.
- * \return A pair containing the resolution vector and direction for the first collider.
+ * \param resolution resolution vector that needs to be corrected
+ * \param rigidbody rigidbody data used to correct resolution
+ * \return A Direction indicating the resolution direction
*/
- std::pair<vec2, Direction> collision_handler(CollisionInternal & data1,
- CollisionInternal & data2,
- CollisionInternalType type);
+ Direction resolution_correction(vec2 & resolution, const Rigidbody::Data & rigidbody);
/**
- * \brief Calculates the resolution vector for two BoxColliders.
+ * \brief Determines the appropriate collision handler for a given collision event.
*
- * Computes the displacement required to separate two overlapping BoxColliders.
+ * This function identifies the correct collision resolution process based on the body types
+ * of the colliders involved in the collision. It delegates
+ * collision handling to specific handlers and calls collision event scripts
+ * as needed.
*
- * \param box_collider1 The first BoxCollider.
- * \param box_collider2 The second BoxCollider.
- * \param position1 The position of the first BoxCollider.
- * \param position2 The position of the second BoxCollider.
- * \return The resolution vector for the collision.
+ * \param info Collision information containing data about both colliders.
*/
- vec2 get_box_box_resolution(const BoxCollider & box_collider1,
- const BoxCollider & box_collider2, const vec2 & position1,
- const vec2 & position2) const;
+ void determine_collision_handler(const CollisionInfo & info);
/**
- * \brief Calculates the resolution vector for two CircleCollider.
+ * \brief Calls both collision script
*
- * Computes the displacement required to separate two overlapping CircleCollider.
+ * Calls both collision script to let user add additonal handling or handle full collision.
*
- * \param circle_collider1 The first CircleCollider.
- * \param circle_collider2 The second CircleCollider.
- * \param final_position1 The position of the first CircleCollider.
- * \param final_position2 The position of the second CircleCollider.
- * \return The resolution vector for the collision.
+ * \param info Collision information containing data about both colliders.
*/
- vec2 get_circle_circle_resolution(const CircleCollider & circle_collider1,
- const CircleCollider & circle_collider2,
- const vec2 & final_position1,
- const vec2 & final_position2) const;
+ void call_collision_events(const CollisionInfo & info);
/**
- * \brief Calculates the resolution vector for two CircleCollider.
- *
- * Computes the displacement required to separate two overlapping CircleCollider.
+ * \brief Handles collisions involving static objects.
*
- * \param circle_collider The first CircleCollider.
- * \param box_collider The second CircleCollider.
- * \param circle_position The position of the CircleCollider.
- * \param box_position The position of the BoxCollider.
- * \return The resolution vector for the collision.
- */
- vec2 get_circle_box_resolution(const CircleCollider & circle_collider,
- const BoxCollider & box_collider,
- const vec2 & circle_position,
- const vec2 & box_position) const;
-
- /**
- * \brief Determines the appropriate collision handler for a collision.
+ * This function resolves collisions between static and dynamic objects by adjusting
+ * the position of the static object and modifying the velocity of the dynamic object
+ * if elasticity is enabled. The position of the static object is corrected
+ * based on the collision resolution, and the dynamic object's velocity is adjusted
+ * accordingly to reflect the collision response.
*
- * Decides the correct resolution process based on the dynamic or static nature of the colliders involved.
+ * The handling includes stopping movement, applying bouncing based on the elasticity
+ * coefficient, and adjusting the position of the dynamic object if needed.
*
* \param info Collision information containing data about both colliders.
*/
- void determine_collision_handler(CollisionInfo & info);
+ void static_collision_handler(const CollisionInfo & info);
/**
- * \brief Handles collisions involving static objects.
+ * \brief Handles collisions involving dynamic objects.
*
- * Resolves collisions by adjusting positions and modifying velocities if bounce is enabled.
+ * Resolves collisions between two dynamic objects by adjusting their positions and modifying
+ * their velocities based on the collision resolution. If elasticity is enabled,
+ * the velocity of both objects is reversed and scaled by the respective elasticity coefficient.
+ * The positions of the objects are adjusted based on the collision resolution.
*
* \param info Collision information containing data about both colliders.
*/
- void static_collision_handler(CollisionInfo & info);
+ void dynamic_collision_handler(const CollisionInfo & info);
private:
/**
* \brief Checks for collisions between colliders.
*
- * Identifies collisions and generates pairs of colliding objects for further processing.
+ * This function checks all active colliders and identifies pairs of colliding objects.
+ * For each identified collision, the appropriate collision data is returned as pairs for further processing.
*
* \param colliders A collection of all active colliders.
* \return A list of collision pairs with their associated data.
@@ -216,86 +210,84 @@ private:
gather_collisions(std::vector<CollisionInternal> & colliders);
/**
- * \brief Checks if two collision layers have at least one common layer.
+ * \brief Checks if the settings allow collision
*
- * This function checks if there is any overlapping layer between the two inputs.
- * It compares each layer from the first input to see
- * if it exists in the second input. If at least one common layer is found,
- * the function returns true, indicating that the two colliders share a common
- * collision layer.
+ * This function checks if there is any collison layer where each object is located in.
+ * After checking the layers it checks the names and at last the tags.
+ * if in all three sets nothing is found collision can not happen.
*
- * \param layers1 all collision layers for the first collider.
- * \param layers2 all collision layers for the second collider.
- * \return Returns true if there is at least one common layer, false otherwise.
+ * \param this_rigidbody Rigidbody of first object
+ * \param other_rigidbody Rigidbody of second collider
+ * \param this_metadata Rigidbody of first object
+ * \param other_metadata Rigidbody of second object
+ * \return Returns true if there is at least one comparison found.
*/
-
- bool have_common_layer(const std::set<int> & layers1, const std::set<int> & layers2);
+ bool should_collide(
+ const CollisionInternal & self,
+ const CollisionInternal & other
+ ) const; //done
/**
* \brief Checks for collision between two colliders.
*
- * Calls the appropriate collision detection function based on the collider types.
+ * This function determines whether two colliders are colliding based on their types.
+ * It calls the appropriate collision detection function based on the collider pair type and stores the collision resolution data.
+ * If a collision is detected, it returns true, otherwise false.
*
* \param first_info Collision data for the first collider.
* \param second_info Collision data for the second collider.
* \param type The type of collider pair.
* \return True if a collision is detected, otherwise false.
*/
- bool get_collision(const CollisionInternal & first_info,
- const CollisionInternal & second_info,
- CollisionInternalType type) const;
+ bool detect_collision(
+ CollisionInternal & first_info, CollisionInternal & second_info,
+ const CollisionInternalType & type
+ );
/**
* \brief Detects collisions between two BoxColliders.
*
- * \param box1 The first BoxCollider.
- * \param box2 The second BoxCollider.
- * \param transform1 Transform of the first object.
- * \param transform2 Transform of the second object.
- * \param rigidbody1 Rigidbody of the first object.
- * \param rigidbody2 Rigidbody of the second object.
- * \return True if a collision is detected, otherwise false.
+ * This function checks whether two `BoxCollider` are colliding based on their positions and scaled dimensions.
+ * If a collision is detected, it calculates the overlap along the X and Y axes and returns the resolution vector.
+ * If no collision is detected, it returns a vector with NaN values.
+
+ * \param box1 Information about the first BoxCollider.
+ * \param box2 Information about the second BoxCollider.
+ * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}.
*/
- bool get_box_box_collision(const BoxCollider & box1, const BoxCollider & box2,
- const Transform & transform1, const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const;
+ vec2 get_box_box_detection(
+ const BoxColliderInternal & box1, const BoxColliderInternal & box2
+ ) const;
/**
* \brief Check collision for box on circle collider
*
- * \param box1 The BoxCollider
- * \param circle2 The CircleCollider
- * \param transform1 Transform of the first object.
- * \param transform2 Transform of the second object.
- * \param rigidbody1 Rigidbody of the first object.
- * \param rigidbody2 Rigidbody of the second object.
- * \return True if a collision is detected, otherwise false.
+ * This function detects if a collision occurs between a rectangular box and a circular collider.
+ * If a collision is detected, the function calculates the resolution vector to resolve the collision.
+ * If no collision is detected, it returns a vector with NaN values
+ *
+ * \param box1 Information about the BoxCollider.
+ * \param circle2 Information about the circleCollider.
+ * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}.
*/
- bool get_box_circle_collision(const BoxCollider & box1, const CircleCollider & circle2,
- const Transform & transform1, const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const;
+ vec2 get_box_circle_detection(
+ const BoxColliderInternal & box1, const CircleColliderInternal & circle2
+ ) const;
/**
* \brief Check collision for circle on circle collider
*
- * \param circle1 First CircleCollider
- * \param circle2 Second CircleCollider
- * \param transform1 Transform of the first object.
- * \param transform2 Transform of the second object.
- * \param rigidbody1 Rigidbody of the first object.
- * \param rigidbody2 Rigidbody of the second object.
- * \return True if a collision is detected, otherwise false.
+ * This function detects if a collision occurs between two circular colliders.
+ * If a collision is detected, it calculates the resolution vector to resolve the collision.
+ * If no collision is detected, it returns a vector with NaN values.
*
- * \return status of collision
+ * \param circle1 Information about the first circleCollider.
+ * \param circle2 Information about the second circleCollider.
+ * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}.
*/
- bool get_circle_circle_collision(const CircleCollider & circle1,
- const CircleCollider & circle2,
- const Transform & transform1,
- const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const;
+ vec2 get_circle_circle_detection(
+ const CircleColliderInternal & circle1, const CircleColliderInternal & circle2
+ ) const;
};
/**
diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp
index d209282..be7eda6 100644
--- a/src/crepe/system/InputSystem.cpp
+++ b/src/crepe/system/InputSystem.cpp
@@ -1,8 +1,8 @@
#include "../api/Button.h"
+#include "../api/Config.h"
#include "../facade/SDLContext.h"
#include "../manager/ComponentManager.h"
#include "../manager/EventManager.h"
-#include "util/Log.h"
#include "InputSystem.h"
@@ -12,7 +12,7 @@ void InputSystem::fixed_update() {
ComponentManager & mgr = this->mediator.component_manager;
SDLContext & context = this->mediator.sdl_context;
std::vector<EventData> event_list = context.get_events();
- RefVector<Camera> cameras = mgr.get_components_by_type<Camera>();
+ const RefVector<Camera> cameras = mgr.get_components_by_type<Camera>();
OptionalRef<Camera> curr_cam_ref;
// Find the active camera
@@ -24,9 +24,8 @@ void InputSystem::fixed_update() {
if (!curr_cam_ref) return;
Camera & current_cam = curr_cam_ref;
- RefVector<Transform> transform_vec
- = mgr.get_components_by_id<Transform>(current_cam.game_object_id);
- Transform & cam_transform = transform_vec.front().get();
+ const Transform & cam_transform
+ = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front();
vec2 camera_origin = cam_transform.position + current_cam.data.postion_offset
- (current_cam.viewport_size / 2);
@@ -45,12 +44,13 @@ void InputSystem::fixed_update() {
}
}
-void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camera_origin,
- const Camera & current_cam) {
+void InputSystem::handle_mouse_event(
+ const EventData & event, const vec2 & camera_origin, const Camera & current_cam
+) {
EventManager & event_mgr = this->mediator.event_manager;
vec2 adjusted_mouse;
adjusted_mouse.x = event.data.mouse_data.mouse_position.x + camera_origin.x;
- adjusted_mouse.x = event.data.mouse_data.mouse_position.y + camera_origin.y;
+ adjusted_mouse.y = event.data.mouse_data.mouse_position.y + camera_origin.y;
// Check if the mouse is within the viewport
if ((adjusted_mouse.x < camera_origin.x
|| adjusted_mouse.x > camera_origin.x + current_cam.viewport_size.x
@@ -83,7 +83,9 @@ void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camer
.mouse_pos = adjusted_mouse,
.button = event.data.mouse_data.mouse_button,
});
- this->handle_click(event.data.mouse_data.mouse_button, adjusted_mouse);
+ this->handle_click(
+ event.data.mouse_data.mouse_button, adjusted_mouse, current_cam
+ );
}
break;
}
@@ -93,7 +95,7 @@ void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camer
.mouse_pos = adjusted_mouse,
.mouse_delta = event.data.mouse_data.rel_mouse_move,
});
- this->handle_move(event, adjusted_mouse);
+ this->handle_move(event, adjusted_mouse, current_cam);
break;
case EventType::MOUSE_WHEEL:
@@ -115,7 +117,8 @@ void InputSystem::handle_non_mouse_event(const EventData & event) {
case EventType::KEY_DOWN:
event_mgr.queue_event<KeyPressEvent>(
- {.repeat = event.data.key_data.key_repeat, .key = event.data.key_data.key});
+ {.repeat = event.data.key_data.key_repeat, .key = event.data.key_data.key}
+ );
break;
case EventType::KEY_UP:
event_mgr.queue_event<KeyReleaseEvent>({.key = event.data.key_data.key});
@@ -128,11 +131,13 @@ void InputSystem::handle_non_mouse_event(const EventData & event) {
break;
case EventType::WINDOW_RESIZE:
event_mgr.queue_event<WindowResizeEvent>(
- WindowResizeEvent{.dimensions = event.data.window_data.resize_dimension});
+ WindowResizeEvent {.dimensions = event.data.window_data.resize_dimension}
+ );
break;
case EventType::WINDOW_MOVE:
event_mgr.queue_event<WindowMoveEvent>(
- {.delta_move = event.data.window_data.move_delta});
+ {.delta_move = event.data.window_data.move_delta}
+ );
break;
case EventType::WINDOW_MINIMIZE:
event_mgr.queue_event<WindowMinimizeEvent>({});
@@ -151,59 +156,70 @@ void InputSystem::handle_non_mouse_event(const EventData & event) {
}
}
-void InputSystem::handle_move(const EventData & event_data, const vec2 & mouse_pos) {
+void InputSystem::handle_move(
+ const EventData & event_data, const vec2 & mouse_pos, const Camera & current_cam
+) {
ComponentManager & mgr = this->mediator.component_manager;
EventManager & event_mgr = this->mediator.event_manager;
- RefVector<Button> buttons = mgr.get_components_by_type<Button>();
+ const RefVector<Button> buttons = mgr.get_components_by_type<Button>();
for (Button & button : buttons) {
if (!button.active) continue;
- Metadata & metadata
- = mgr.get_components_by_id<Metadata>(button.game_object_id).front();
- Transform & transform
+
+ const Transform & transform
= mgr.get_components_by_id<Transform>(button.game_object_id).front();
+ const Transform & cam_transform
+ = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front();
+ const Metadata & metadata
+ = mgr.get_components_by_id<Metadata>(button.game_object_id).front();
bool was_hovering = button.hover;
- if (this->is_mouse_inside_button(mouse_pos, button, transform)) {
+
+ if (this->is_mouse_inside_button(mouse_pos, button, transform, cam_transform)) {
button.hover = true;
if (!was_hovering) {
- event_mgr.trigger_event<ButtonEnterEvent>(metadata);
+ event_mgr.trigger_event<ButtonEnterEvent>(metadata, metadata.game_object_id);
}
} else {
button.hover = false;
if (was_hovering) {
- event_mgr.trigger_event<ButtonExitEvent>(metadata);
+ event_mgr.trigger_event<ButtonExitEvent>(metadata, metadata.game_object_id);
}
}
}
}
-void InputSystem::handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos) {
+void InputSystem::handle_click(
+ const MouseButton & mouse_button, const vec2 & mouse_pos, const Camera & current_cam
+) {
ComponentManager & mgr = this->mediator.component_manager;
EventManager & event_mgr = this->mediator.event_manager;
- RefVector<Button> buttons = mgr.get_components_by_type<Button>();
-
+ const RefVector<Button> buttons = mgr.get_components_by_type<Button>();
+ const Transform & cam_transform
+ = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front();
for (Button & button : buttons) {
if (!button.active) continue;
- Metadata & metadata
+ const Metadata & metadata
= mgr.get_components_by_id<Metadata>(button.game_object_id).front();
- Transform & transform
+ const Transform & transform
= mgr.get_components_by_id<Transform>(button.game_object_id).front();
-
- if (this->is_mouse_inside_button(mouse_pos, button, transform)) {
- event_mgr.trigger_event<ButtonPressEvent>(metadata);
+ if (this->is_mouse_inside_button(mouse_pos, button, transform, cam_transform)) {
+ event_mgr.trigger_event<ButtonPressEvent>(metadata, metadata.game_object_id);
}
}
}
-bool InputSystem::is_mouse_inside_button(const vec2 & mouse_pos, const Button & button,
- const Transform & transform) {
- int actual_x = transform.position.x + button.offset.x;
- int actual_y = transform.position.y + button.offset.y;
-
- int half_width = button.dimensions.x / 2;
- int half_height = button.dimensions.y / 2;
+bool InputSystem::is_mouse_inside_button(
+ const vec2 & mouse_pos, const Button & button, const Transform & transform,
+ const Transform & cam_transform
+) {
+ vec2 actual_pos = transform.position + button.offset;
+ if (!button.data.world_space) {
+ actual_pos += cam_transform.position;
+ }
+ vec2 half_dimensions = button.dimensions * transform.scale / 2;
- // Check if the mouse is within the button's boundaries
- return mouse_pos.x >= actual_x - half_width && mouse_pos.x <= actual_x + half_width
- && mouse_pos.y >= actual_y - half_height && mouse_pos.y <= actual_y + half_height;
+ return mouse_pos.x >= actual_pos.x - half_dimensions.x
+ && mouse_pos.x <= actual_pos.x + half_dimensions.x
+ && mouse_pos.y >= actual_pos.y - half_dimensions.y
+ && mouse_pos.y <= actual_pos.y + half_dimensions.y;
}
diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h
index 0f1bfa1..be62367 100644
--- a/src/crepe/system/InputSystem.h
+++ b/src/crepe/system/InputSystem.h
@@ -1,12 +1,9 @@
#pragma once
-#include "../api/Config.h"
-#include "../facade/EventData.h"
-
#include "../api/Event.h"
#include "../api/Metadata.h"
+#include "../facade/EventData.h"
#include "../types.h"
-#include "../util/OptionalRef.h"
#include "System.h"
@@ -23,7 +20,7 @@ public:
/**
* \param metadata Metadata of the button pressed
*/
- ButtonPressEvent(const Metadata & metadata) : metadata(metadata){};
+ ButtonPressEvent(const Metadata & metadata) : metadata(metadata) {};
};
//! Event triggered when the mouse enters a button
class ButtonEnterEvent : public Event {
@@ -33,7 +30,7 @@ public:
/**
* \param metadata Metadata of the button pressed
*/
- ButtonEnterEvent(const Metadata & metadata) : metadata(metadata){};
+ ButtonEnterEvent(const Metadata & metadata) : metadata(metadata) {};
};
//! Event triggered when the mouse leaves a button
class ButtonExitEvent : public Event {
@@ -43,7 +40,7 @@ public:
/**
* \param metadata Metadata of the button pressed
*/
- ButtonExitEvent(const Metadata & metadata) : metadata(metadata){};
+ ButtonExitEvent(const Metadata & metadata) : metadata(metadata) {};
};
/**
@@ -79,8 +76,9 @@ private:
* This method processes mouse events, adjusts the mouse position to world coordinates,
* and triggers the appropriate mouse-specific event handling logic.
*/
- void handle_mouse_event(const EventData & event, const vec2 & camera_origin,
- const Camera & current_cam);
+ void handle_mouse_event(
+ const EventData & event, const vec2 & camera_origin, const Camera & current_cam
+ );
/**
* \brief Handles non-mouse-related events.
* \param event The event data for the non-mouse event.
@@ -94,20 +92,26 @@ private:
* \param mouse_button The mouse button involved in the click.
* \param world_mouse_x The X coordinate of the mouse in world space.
* \param world_mouse_y The Y coordinate of the mouse in world space.
+ * \param current_cam The current active camera.
*
* This method processes the mouse click event and triggers the corresponding button action.
*/
- void handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos);
+ void handle_click(
+ const MouseButton & mouse_button, const vec2 & mouse_pos, const Camera & current_cam
+ );
/**
* \brief Handles the mouse movement event.
* \param event_data The event data containing information about the mouse movement.
* \param world_mouse_x The X coordinate of the mouse in world space.
* \param world_mouse_y The Y coordinate of the mouse in world space.
+ * \param current_cam The current active camera.
*
* This method processes the mouse movement event and updates the button hover state.
*/
- void handle_move(const EventData & event_data, const vec2 & mouse_pos);
+ void handle_move(
+ const EventData & event_data, const vec2 & mouse_pos, const Camera & current_cam
+ );
/**
* \brief Checks if the mouse position is inside the bounds of the button.
@@ -115,10 +119,13 @@ private:
* \param world_mouse_y The Y coordinate of the mouse in world space.
* \param button The button to check.
* \param transform The transform component of the button.
+ * \param cam_transform the transform of the current active camera
* \return True if the mouse is inside the button, false otherwise.
*/
- bool is_mouse_inside_button(const vec2 & mouse_pos, const Button & button,
- const Transform & transform);
+ bool is_mouse_inside_button(
+ const vec2 & mouse_pos, const Button & button, const Transform & transform,
+ const Transform & cam_transform
+ );
/**
* \brief Handles the button press event, calling the on_click callback if necessary.
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index bbc7366..f026390 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -7,6 +7,7 @@
#include "../api/Transform.h"
#include "../manager/ComponentManager.h"
#include "../manager/LoopTimerManager.h"
+#include "util/AbsolutePosition.h"
#include "ParticleSystem.h"
@@ -48,9 +49,11 @@ void ParticleSystem::fixed_update() {
void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & transform) {
constexpr float DEG_TO_RAD = M_PI / 180.0;
- vec2 initial_position = emitter.data.offset + transform.position;
- float random_angle
- = this->generate_random_angle(emitter.data.min_angle, emitter.data.max_angle);
+ vec2 initial_position = AbsolutePosition::get_position(transform, emitter.data.offset);
+ float random_angle = this->generate_random_angle(
+ emitter.data.min_angle + transform.rotation,
+ emitter.data.max_angle + transform.rotation
+ );
float random_speed
= this->generate_random_speed(emitter.data.min_speed, emitter.data.max_speed);
@@ -61,8 +64,9 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform &
for (Particle & particle : emitter.particles) {
if (!particle.active) {
- particle.reset(emitter.data.end_lifespan, initial_position, velocity,
- random_angle);
+ particle.reset(
+ emitter.data.end_lifespan, initial_position, velocity, random_angle
+ );
break;
}
}
@@ -80,8 +84,9 @@ void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform & t
for (Particle & particle : emitter.particles) {
const vec2 & position = particle.position;
- bool within_bounds = (position.x >= left && position.x <= right && position.y >= top
- && position.y <= bottom);
+ bool within_bounds
+ = (position.x >= left && position.x <= right && position.y >= top
+ && position.y <= bottom);
//if not within bounds do a reset or stop velocity
if (!within_bounds) {
if (emitter.data.boundary.reset_on_exit) {
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index 33218f6..30bb422 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -16,6 +16,9 @@
#include "../facade/Texture.h"
#include "../manager/ComponentManager.h"
#include "../manager/ResourceManager.h"
+#include "api/Text.h"
+#include "facade/Font.h"
+#include "util/AbsolutePosition.h"
#include "RenderSystem.h"
#include "types.h"
@@ -70,11 +73,34 @@ RefVector<Sprite> RenderSystem::sort(RefVector<Sprite> & objs) const {
void RenderSystem::frame_update() {
this->clear_screen();
this->render();
+ this->render_text();
this->present_screen();
}
-bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) {
+void RenderSystem::render_text() {
+ SDLContext & ctx = this->mediator.sdl_context;
+ ComponentManager & mgr = this->mediator.component_manager;
+ ResourceManager & resource_manager = this->mediator.resource_manager;
+
+ RefVector<Text> texts = mgr.get_components_by_type<Text>();
+
+ for (Text & text : texts) {
+ if (!text.active) continue;
+ if (!text.font.has_value())
+ text.font.emplace(ctx.get_font_from_name(text.font_family));
+
+ const Font & font = resource_manager.get<Font>(text.font.value());
+ const auto & transform
+ = mgr.get_components_by_id<Transform>(text.game_object_id).front().get();
+ ctx.draw_text(SDLContext::RenderText {
+ .text = text,
+ .font = font,
+ .transform = transform,
+ });
+ }
+}
+bool RenderSystem::render_particle(const Sprite & sprite, const Transform & transform) {
ComponentManager & mgr = this->mediator.component_manager;
SDLContext & ctx = this->mediator.sdl_context;
ResourceManager & resource_manager = this->mediator.resource_manager;
@@ -92,29 +118,30 @@ bool RenderSystem::render_particle(const Sprite & sprite, const double & scale)
for (const Particle & p : em.particles) {
if (!p.active) continue;
+ if (p.time_in_life < em.data.begin_lifespan) continue;
- ctx.draw(SDLContext::RenderContext{
+ ctx.draw(SDLContext::RenderContext {
.sprite = sprite,
.texture = res,
.pos = p.position,
- .angle = p.angle,
- .scale = scale,
+ .angle = p.angle + transform.rotation,
+ .scale = transform.scale,
});
}
}
return rendering_particles;
}
-void RenderSystem::render_normal(const Sprite & sprite, const Transform & tm) {
+void RenderSystem::render_normal(const Sprite & sprite, const Transform & transform) {
SDLContext & ctx = this->mediator.sdl_context;
ResourceManager & resource_manager = this->mediator.resource_manager;
const Texture & res = resource_manager.get<Texture>(sprite.source);
-
- ctx.draw(SDLContext::RenderContext{
+ vec2 pos = AbsolutePosition::get_position(transform, sprite.data.position_offset);
+ ctx.draw(SDLContext::RenderContext {
.sprite = sprite,
.texture = res,
- .pos = tm.position,
- .angle = tm.rotation,
- .scale = tm.scale,
+ .pos = pos,
+ .angle = transform.rotation,
+ .scale = transform.scale,
});
}
@@ -126,35 +153,16 @@ void RenderSystem::render() {
ResourceManager & resource_manager = this->mediator.resource_manager;
RefVector<Sprite> sorted_sprites = this->sort(sprites);
RefVector<Text> text_components = mgr.get_components_by_type<Text>();
- for (Text & text : text_components) {
- const Transform & transform
- = mgr.get_components_by_id<Transform>(text.game_object_id).front().get();
- this->render_text(text, transform);
- }
+
for (const Sprite & sprite : sorted_sprites) {
if (!sprite.active) continue;
const Transform & transform
= mgr.get_components_by_id<Transform>(sprite.game_object_id).front().get();
- bool rendered_particles = this->render_particle(sprite, transform.scale);
+ bool rendered_particles = this->render_particle(sprite, transform);
if (rendered_particles) continue;
this->render_normal(sprite, transform);
}
}
-void RenderSystem::render_text(Text & text, const Transform & tm) {
- SDLContext & ctx = this->mediator.sdl_context;
-
- if (!text.font.has_value()) {
- text.font.emplace(ctx.get_font_from_name(text.font_family));
- }
-
- ResourceManager & resource_manager = this->mediator.resource_manager;
-
- if (!text.font.has_value()) {
- return;
- }
- const Asset & font_asset = text.font.value();
- const Font & res = resource_manager.get<Font>(font_asset);
-}
diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h
index 656ad5b..627a743 100644
--- a/src/crepe/system/RenderSystem.h
+++ b/src/crepe/system/RenderSystem.h
@@ -36,34 +36,28 @@ private:
//! Updates the active camera used for rendering.
void update_camera();
- //! Renders the whole screen
+ //! Renders all the sprites and particles
void render();
+ //! Renders all Text components
+ void render_text();
+
+private:
/**
* \brief Renders all the particles on the screen from a given sprite.
*
* \param sprite renders the particles with given texture
- * \param tm the Transform component for scale. This is not a const reference because each
- * particle has a position and rotation that needs to overwrite the transform position and
- * rotation without overwriting the current transform. and because the transform
- * constructor is now protected i cannot make tmp inside
+ * \param transform the component that holds the position, rotation, and scale.
* \return true if particles have been rendered
*/
- bool render_particle(const Sprite & sprite, const double & scale);
- /**
- * \brief Renders all Text components
- *
- * \param text The text component to be rendered.
- * \param tm the Transform component that holds the position,rotation and scale
- */
- void render_text(Text & text, const Transform & tm);
+ bool render_particle(const Sprite & sprite, const Transform & transform);
/**
* \brief renders a sprite with a Transform component on the screen
*
* \param sprite the sprite component that holds all the data
- * \param tm the Transform component that holds the position,rotation and scale
+ * \param transform the Transform component that holds the position,rotation and scale
*/
- void render_normal(const Sprite & sprite, const Transform & tm);
+ void render_normal(const Sprite & sprite, const Transform & transform);
/**
* \brief sort a vector sprite objects with
@@ -72,12 +66,6 @@ private:
* \return returns a sorted reference vector
*/
RefVector<Sprite> sort(RefVector<Sprite> & objs) const;
-
- /**
- * \todo Add text rendering using SDL_ttf for text components.
- * \todo Implement a text component and a button component.
- * \todo Consider adding text input functionality.
- */
};
} // namespace crepe
diff --git a/src/crepe/system/ScriptSystem.cpp b/src/crepe/system/ScriptSystem.cpp
index 746285f..ed0c7cc 100644
--- a/src/crepe/system/ScriptSystem.cpp
+++ b/src/crepe/system/ScriptSystem.cpp
@@ -1,7 +1,6 @@
#include "../api/BehaviorScript.h"
#include "../api/Script.h"
#include "../manager/ComponentManager.h"
-#include "../util/dbg.h"
#include "ScriptSystem.h"
@@ -9,10 +8,21 @@ using namespace std;
using namespace crepe;
void ScriptSystem::fixed_update() {
- dbg_trace();
+ LoopTimerManager & timer = this->mediator.loop_timer;
+ duration_t delta_time = timer.get_scaled_fixed_delta_time();
+ this->update(&Script::fixed_update, delta_time);
+}
- ComponentManager & mgr = this->mediator.component_manager;
+void ScriptSystem::frame_update() {
LoopTimerManager & timer = this->mediator.loop_timer;
+ duration_t delta_time = timer.get_delta_time();
+ this->update(&Script::frame_update, delta_time);
+}
+
+void ScriptSystem::update(
+ void (Script::*update_function)(duration_t), const duration_t & delta_time
+) {
+ ComponentManager & mgr = this->mediator.component_manager;
RefVector<BehaviorScript> behavior_scripts = mgr.get_components_by_type<BehaviorScript>();
for (BehaviorScript & behavior_script : behavior_scripts) {
@@ -26,7 +36,6 @@ void ScriptSystem::fixed_update() {
script->initialized = true;
}
- duration_t delta_time = timer.get_scaled_fixed_delta_time();
- script->update(delta_time);
+ (*script.*update_function)(delta_time);
}
}
diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h
index 612c2ae..257b615 100644
--- a/src/crepe/system/ScriptSystem.h
+++ b/src/crepe/system/ScriptSystem.h
@@ -1,5 +1,7 @@
#pragma once
+#include "../manager/LoopTimerManager.h"
+
#include "System.h"
namespace crepe {
@@ -9,20 +11,27 @@ class Script;
/**
* \brief Script system
*
- * The script system is responsible for all \c BehaviorScript components, and
- * calls the methods on classes derived from \c Script.
+ * The script system is responsible for all \c BehaviorScript components, and calls the methods
+ * on classes derived from \c Script.
*/
class ScriptSystem : public System {
public:
using System::System;
+
+public:
+ //! Call Script::fixed_update() on all active \c BehaviorScript instances
+ void fixed_update() override;
+ //! Call Script::frame_update() on all active \c BehaviorScript instances
+ void frame_update() override;
+
+private:
/**
- * \brief Call Script::update() on all active \c BehaviorScript instances
+ * \brief Call Script `*_update` member function on all active \c BehaviorScript instances
*
- * This routine updates all scripts sequentially using the Script::update()
- * method. It also calls Script::init() if this has not been done before on
- * the \c BehaviorScript instance.
+ * \note This routine also calls Script::init() if this has not been done before on the \c
+ * BehaviorScript instance.
*/
- void fixed_update() override;
+ void update(void (Script::*update_function)(duration_t), const duration_t & delta_time);
};
} // namespace crepe
diff --git a/src/crepe/util/AbsolutePosition.cpp b/src/crepe/util/AbsolutePosition.cpp
new file mode 100644
index 0000000..29ade23
--- /dev/null
+++ b/src/crepe/util/AbsolutePosition.cpp
@@ -0,0 +1,20 @@
+#include "AbsolutePosition.h"
+
+using namespace crepe;
+
+vec2 AbsolutePosition::get_position(const Transform & transform, const vec2 & offset) {
+ // Get the rotation in radians
+ float radians1 = transform.rotation * (M_PI / 180.0);
+
+ // Calculate total offset with scale
+ vec2 total_offset = offset * transform.scale;
+
+ // Rotate
+ float rotated_total_offset_x1
+ = total_offset.x * cos(radians1) - total_offset.y * sin(radians1);
+ float rotated_total_offset_y1
+ = total_offset.x * sin(radians1) + total_offset.y * cos(radians1);
+
+ // Final positions considering scaling and rotation
+ return (transform.position + vec2(rotated_total_offset_x1, rotated_total_offset_y1));
+}
diff --git a/src/crepe/util/AbsolutePosition.h b/src/crepe/util/AbsolutePosition.h
new file mode 100644
index 0000000..857c1ac
--- /dev/null
+++ b/src/crepe/util/AbsolutePosition.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "api/Transform.h"
+
+#include "types.h"
+
+namespace crepe {
+
+/**
+ * \brief A class for calculating the absolute position of an object.
+ *
+ * This class provides a utility function to get the position of an object in the world space,
+ * taking into account the transform and any additional offset.
+ */
+class AbsolutePosition {
+public:
+ /**
+ * \brief Get the absolute position of an object.
+ *
+ * This function calculates the absolute position by combining the transform position with an optional offset.
+ *
+ * \param transform The transform of the object, which contains its position, rotation, and scale.
+ * \param offset The offset to apply to the object's position (in local space).
+ * \return The absolute position of the object as a 2D vector.
+ */
+ static vec2 get_position(const Transform & transform, const vec2 & offset);
+};
+
+} // namespace crepe
diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt
index 94ed906..33160a7 100644
--- a/src/crepe/util/CMakeLists.txt
+++ b/src/crepe/util/CMakeLists.txt
@@ -1,6 +1,7 @@
target_sources(crepe PUBLIC
LogColor.cpp
Log.cpp
+ AbsolutePosition.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -11,5 +12,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
Proxy.hpp
OptionalRef.h
OptionalRef.hpp
+ AbsolutePosition.h
)
diff --git a/src/crepe/util/dbg.h b/src/crepe/util/dbg.h
index c7283ee..e448070 100644
--- a/src/crepe/util/dbg.h
+++ b/src/crepe/util/dbg.h
@@ -5,10 +5,13 @@
// utility macros
#define _crepe_logf_here(level, fmt, ...) \
- crepe::Log::logf(level, "{}" fmt, \
- crepe::LogColor().fg_white(false).str(std::format( \
- "{} ({}:{})", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__)), \
- __VA_ARGS__)
+ crepe::Log::logf( \
+ level, "{}" fmt, \
+ crepe::LogColor().fg_white(false).str( \
+ std::format("{} ({}:{})", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__) \
+ ), \
+ __VA_ARGS__ \
+ )
// very illegal global function-style macros
// NOLINTBEGIN