aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/crepe/Resource.cpp3
-rw-r--r--src/crepe/Resource.h3
-rw-r--r--src/crepe/api/AI.cpp89
-rw-r--r--src/crepe/api/AI.h128
-rw-r--r--src/crepe/api/Animator.cpp20
-rw-r--r--src/crepe/api/Animator.h17
-rw-r--r--src/crepe/api/CMakeLists.txt4
-rw-r--r--src/crepe/api/LoopManager.cpp3
-rw-r--r--src/crepe/api/LoopManager.h13
-rw-r--r--src/crepe/api/LoopTimer.cpp14
-rw-r--r--src/crepe/api/LoopTimer.h12
-rw-r--r--src/crepe/api/Script.cpp8
-rw-r--r--src/crepe/api/Script.h3
-rw-r--r--src/crepe/api/Script.hpp3
-rw-r--r--src/crepe/api/Sprite.cpp11
-rw-r--r--src/crepe/api/Sprite.h23
-rw-r--r--src/crepe/api/Texture.cpp38
-rw-r--r--src/crepe/api/Texture.h69
-rw-r--r--src/crepe/api/Vector2.h24
-rw-r--r--src/crepe/api/Vector2.hpp48
-rw-r--r--src/crepe/facade/CMakeLists.txt2
-rw-r--r--src/crepe/facade/SDLContext.cpp54
-rw-r--r--src/crepe/facade/SDLContext.h77
-rw-r--r--src/crepe/facade/Sound.cpp2
-rw-r--r--src/crepe/facade/Sound.h3
-rw-r--r--src/crepe/facade/Texture.cpp28
-rw-r--r--src/crepe/facade/Texture.h69
-rw-r--r--src/crepe/manager/Mediator.h12
-rw-r--r--src/crepe/manager/ResourceManager.cpp2
-rw-r--r--src/crepe/manager/ResourceManager.hpp2
-rw-r--r--src/crepe/manager/SaveManager.cpp30
-rw-r--r--src/crepe/manager/SaveManager.h32
-rw-r--r--src/crepe/system/AISystem.cpp186
-rw-r--r--src/crepe/system/AISystem.h81
-rw-r--r--src/crepe/system/AnimatorSystem.cpp2
-rw-r--r--src/crepe/system/AudioSystem.cpp3
-rw-r--r--src/crepe/system/CMakeLists.txt2
-rw-r--r--src/crepe/system/InputSystem.cpp5
-rw-r--r--src/crepe/system/RenderSystem.cpp9
-rw-r--r--src/crepe/util/OptionalRef.h10
-rw-r--r--src/crepe/util/OptionalRef.hpp7
-rw-r--r--src/example/AITest.cpp86
-rw-r--r--src/example/CMakeLists.txt2
-rw-r--r--src/example/rendering_particle.cpp18
-rw-r--r--src/example/savemgr.cpp44
-rw-r--r--src/test/CMakeLists.txt2
-rw-r--r--src/test/InputTest.cpp1
-rw-r--r--src/test/ParticleTest.cpp4
-rw-r--r--src/test/Profiling.cpp44
-rw-r--r--src/test/RenderSystemTest.cpp36
-rw-r--r--src/test/ResourceManagerTest.cpp5
-rw-r--r--src/test/SaveManagerTest.cpp40
-rw-r--r--src/test/ScriptSaveManagerTest.cpp35
-rw-r--r--src/test/Vector2Test.cpp148
54 files changed, 1207 insertions, 409 deletions
diff --git a/src/crepe/Resource.cpp b/src/crepe/Resource.cpp
index 27b4c4b..85913ed 100644
--- a/src/crepe/Resource.cpp
+++ b/src/crepe/Resource.cpp
@@ -1,5 +1,6 @@
#include "Resource.h"
+#include "manager/Mediator.h"
using namespace crepe;
-Resource::Resource(const Asset & asset) {}
+Resource::Resource(const Asset & asset, Mediator & mediator) {}
diff --git a/src/crepe/Resource.h b/src/crepe/Resource.h
index eceb15b..d65206b 100644
--- a/src/crepe/Resource.h
+++ b/src/crepe/Resource.h
@@ -4,6 +4,7 @@ namespace crepe {
class ResourceManager;
class Asset;
+class Mediator;
/**
* \brief Resource interface
@@ -17,7 +18,7 @@ class Asset;
*/
class Resource {
public:
- Resource(const Asset & src);
+ Resource(const Asset & src, Mediator & mediator);
virtual ~Resource() = default;
Resource(const Resource &) = delete;
diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp
new file mode 100644
index 0000000..2195249
--- /dev/null
+++ b/src/crepe/api/AI.cpp
@@ -0,0 +1,89 @@
+#include <stdexcept>
+#include <type_traits>
+
+#include "AI.h"
+#include "types.h"
+
+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) {
+ if (radius <= 0) {
+ throw std::runtime_error("Radius must be greater than 0");
+ }
+
+ // The step size is determined by the radius (step size is in radians)
+ float step = RADIUS_TO_STEP / radius;
+ // Force at least MIN_STEP steps (in case of a small radius)
+ if (step > 2 * M_PI / MIN_STEP) {
+ step = 2 * M_PI / MIN_STEP;
+ }
+ // The path node distance is determined by the step size and the radius
+ this->path_node_distance = radius * step * PATH_NODE_DISTANCE_FACTOR;
+
+ 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))});
+ }
+ } 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))});
+ }
+ }
+}
+
+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");
+ }
+
+ float max_radius = std::max(radius_x, radius_y);
+ // The step size is determined by the radius (step size is in radians)
+ float step = RADIUS_TO_STEP / max_radius;
+ // Force at least MIN_STEP steps (in case of a small radius)
+ if (step > 2 * M_PI / MIN_STEP) {
+ step = 2 * M_PI / MIN_STEP;
+ }
+ // The path node distance is determined by the step size and the radius
+ this->path_node_distance = max_radius * step * PATH_NODE_DISTANCE_FACTOR;
+
+ std::function<vec2(vec2, vec2)> rotate_point = [rotation](vec2 point, vec2 center) {
+ float s = sin(rotation);
+ float c = cos(rotation);
+
+ // Translate point back to origin
+ point.x -= center.x;
+ point.y -= center.y;
+
+ // Rotate point
+ float xnew = point.x * c - point.y * s;
+ float ynew = point.x * s + point.y * c;
+
+ // Translate point back
+ point.x = xnew + center.x;
+ point.y = ynew + center.y;
+
+ return point;
+ };
+
+ 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))};
+ 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))};
+ path.push_back(rotate_point(point, center));
+ }
+ }
+}
+
+} // namespace crepe
diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h
new file mode 100644
index 0000000..c780a91
--- /dev/null
+++ b/src/crepe/api/AI.h
@@ -0,0 +1,128 @@
+#pragma once
+
+#include "Component.h"
+#include "types.h"
+
+namespace crepe {
+
+/**
+ * \brief The AI component is used to control the movement of an entity using AI.
+ *
+ * The AI component can be used to control the movement of an entity. The AI component can be used
+ * to implement different behaviors such as seeking, fleeing, arriving, and path following.
+ */
+class AI : public Component {
+public:
+ //! The different types of behaviors that can be used
+ enum BehaviorTypeMask {
+ SEEK = 0x00002,
+ FLEE = 0x00004,
+ ARRIVE = 0x00008,
+ PATH_FOLLOW = 0x00010,
+ };
+
+public:
+ /**
+ * \param id The id of the game object
+ * \param max_force The maximum force that can be applied to the entity
+ */
+ AI(game_object_id_t id, float max_force);
+
+ /**
+ * \brief Check if a behavior is on (aka activated)
+ *
+ * \param behavior The behavior to check
+ * \return true if the behavior is on, false otherwise
+ */
+ bool on(BehaviorTypeMask behavior) const { return (flags & behavior); }
+ //! Turn on the seek behavior
+ void seek_on() { flags |= SEEK; }
+ //! Turn off the seek behavior
+ void seek_off() { flags &= ~SEEK; }
+ //! Turn on the flee behavior
+ void flee_on() { flags |= FLEE; }
+ //! Turn off the flee behavior
+ void flee_off() { flags &= ~FLEE; }
+ //! Turn on the arrive behavior
+ void arrive_on() { flags |= ARRIVE; }
+ //! Turn off the arrive behavior
+ void arrive_off() { flags &= ~ARRIVE; }
+ //! Turn on the path follow behavior
+ void path_follow_on() { flags |= PATH_FOLLOW; }
+ //! Turn off the path follow behavior
+ void path_follow_off() { flags &= ~PATH_FOLLOW; }
+
+ /**
+ * \brief Add a path node (for the path following behavior)
+ *
+ * \note The path is not relative to the entity's position (it is an absolute path)
+ *
+ * \param node The path node to add
+ */
+ void add_path_node(const vec2 & node) { path.push_back(node); }
+ /**
+ * \brief Make a circle path (for the path following behavior)
+ *
+ * \note The path is not relative to the entity's position (it is an absolute path)
+ *
+ * \param radius The radius of the circle (in game units)
+ * \param center The center of the circle (in game units)
+ * \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);
+ /**
+ * \brief Make an oval path (for the path following behavior)
+ *
+ * \note The path is not relative to the entity's position (it is an absolute path)
+ *
+ * \param radius_x The x radius of the oval (in game units)
+ * \param radius_y The y radius of the oval (in game units)
+ * \param center The center of the oval (in game units)
+ * \param start_angle The start angle of the oval (in radians)
+ * \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);
+
+public:
+ //! The maximum force that can be applied to the entity (higher values will make the entity adjust faster)
+ float max_force;
+
+ //! The target to seek at
+ vec2 seek_target;
+ //! The target to arrive at
+ vec2 arrive_target;
+ //! The target to flee from
+ vec2 flee_target;
+ //! The distance at which the entity will start to flee from the target
+ float square_flee_panic_distance = 200.0f * 200.0f;
+ //! The deceleration rate for the arrive behavior (higher values will make the entity decelerate faster (less overshoot))
+ float arrive_deceleration = 40.0f;
+ //! The path to follow (for the path following behavior)
+ std::vector<vec2> path;
+ //! The distance from the path node at which the entity will move to the next node (automatically set by make_circle_path())
+ float path_node_distance = 400.0f;
+ //! Looping behavior for the path
+ bool path_loop = true;
+
+private:
+ //! The flags for the behaviors
+ int flags = 0;
+ //! The current path index
+ size_t path_index = 0;
+
+ //! The AISystem is the only class that should access the flags and path_index variables
+ friend class AISystem;
+
+ //! The minimum amount of steps for the path following behavior
+ static constexpr int MIN_STEP = 16;
+ //! The radius to step size ratio for the path following behavior
+ static constexpr float RADIUS_TO_STEP = 400.0f;
+ //! The path node distance factor for the path following behavior
+ static constexpr float PATH_NODE_DISTANCE_FACTOR = 0.75f;
+};
+
+} // namespace crepe
diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp
index b8a91dc..4ce4bf0 100644
--- a/src/crepe/api/Animator.cpp
+++ b/src/crepe/api/Animator.cpp
@@ -7,23 +7,21 @@
using namespace crepe;
-Animator::Animator(game_object_id_t id, Sprite & spritesheet, unsigned int max_row,
- unsigned int max_col, 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),
- max_rows(max_row),
- max_columns(max_col),
+ grid_size(grid_size),
data(data) {
dbg_trace();
- this->spritesheet.mask.h /= this->max_columns;
- this->spritesheet.mask.w /= this->max_rows;
- this->spritesheet.mask.x = this->data.row * this->spritesheet.mask.w;
- this->spritesheet.mask.y = this->data.col * this->spritesheet.mask.h;
+ this->spritesheet.mask.w = single_frame_size.x;
+ this->spritesheet.mask.h = single_frame_size.y;
+ this->spritesheet.mask.x = 0;
+ this->spritesheet.mask.y = 0;
- // need to do this for to get the aspect ratio for a single clipping in the spritesheet
this->spritesheet.aspect_ratio
- = static_cast<double>(this->spritesheet.mask.w) / this->spritesheet.mask.h;
+ = static_cast<float>(single_frame_size.x) / single_frame_size.y;
}
Animator::~Animator() { dbg_trace(); }
@@ -54,6 +52,6 @@ void Animator::set_anim(int col) {
void Animator::next_anim() {
Animator::Data & ctx = this->data;
- ctx.row = ctx.row++ % this->max_rows;
+ 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 7c850b8..5918800 100644
--- a/src/crepe/api/Animator.h
+++ b/src/crepe/api/Animator.h
@@ -75,27 +75,28 @@ public:
*
* \param id The unique identifier for the component, typically assigned automatically.
* \param spritesheet the reference to the spritesheet
- * \param max_row maximum of rows inside the given spritesheet
- * \param max_col maximum of columns inside the given spritesheet
+ * \param single_frame_size the width and height in pixels of a single frame inside the
+ * spritesheet
+ * \param grid_size the max rows and columns inside the given spritesheet
* \param data extra animation data for more control
*
* This constructor sets up the Animator with the given parameters, and initializes the
* animation system.
*/
- Animator(game_object_id_t id, Sprite & spritesheet, unsigned int max_row,
- unsigned int max_col, 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:
- //! The maximum number of columns in the sprite sheet.
- const unsigned int max_columns;
- //! The maximum number of rows in the sprite sheet.
- const unsigned int max_rows;
Animator::Data data;
private:
//! A reference to the Sprite sheet containing.
Sprite & spritesheet;
+
+ //! The maximum number of rows and columns inside the spritesheet
+ const uvec2 grid_size;
+
//! Uses the spritesheet
friend AnimatorSystem;
};
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index a163faf..46deb67 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -6,7 +6,6 @@ target_sources(crepe PUBLIC
ParticleEmitter.cpp
Transform.cpp
Color.cpp
- Texture.cpp
Sprite.cpp
Config.cpp
Metadata.cpp
@@ -23,6 +22,7 @@ target_sources(crepe PUBLIC
Script.cpp
Button.cpp
UIObject.cpp
+ AI.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -38,7 +38,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
Vector2.h
Vector2.hpp
Color.h
- Texture.h
Scene.h
Metadata.h
Camera.h
@@ -55,4 +54,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
Asset.h
Button.h
UIObject.h
+ AI.h
)
diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp
index 3e9e21c..1f0ba72 100644
--- a/src/crepe/api/LoopManager.cpp
+++ b/src/crepe/api/LoopManager.cpp
@@ -1,3 +1,4 @@
+#include "../system/AISystem.h"
#include "../system/AnimatorSystem.h"
#include "../system/AudioSystem.h"
#include "../system/CollisionSystem.h"
@@ -22,6 +23,7 @@ LoopManager::LoopManager() {
this->load_system<ScriptSystem>();
this->load_system<InputSystem>();
this->load_system<AudioSystem>();
+ this->load_system<AISystem>();
}
void LoopManager::process_input() { this->get_system<InputSystem>().update(); }
@@ -37,6 +39,7 @@ void LoopManager::fixed_update() {
EventManager & ev = this->mediator.event_manager;
ev.dispatch_events();
this->get_system<ScriptSystem>().update();
+ this->get_system<AISystem>().update();
this->get_system<PhysicsSystem>().update();
this->get_system<CollisionSystem>().update();
this->get_system<AudioSystem>().update();
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
index 0947f94..1bafa56 100644
--- a/src/crepe/api/LoopManager.h
+++ b/src/crepe/api/LoopManager.h
@@ -5,10 +5,12 @@
#include "../facade/SDLContext.h"
#include "../manager/ComponentManager.h"
#include "../manager/ResourceManager.h"
+#include "../manager/SaveManager.h"
#include "../manager/SceneManager.h"
#include "../system/System.h"
#include "LoopTimer.h"
+#include "manager/ResourceManager.h"
namespace crepe {
@@ -98,11 +100,12 @@ private:
SceneManager scene_manager{mediator};
//! Resource manager instance
ResourceManager resource_manager{mediator};
-
- //! SDL context \todo no more singletons!
- SDLContext & sdl_context = SDLContext::get_instance();
- //! Loop timer \todo no more singletons!
- LoopTimer & loop_timer = LoopTimer::get_instance();
+ //! Save manager instance
+ SaveManager save_manager{mediator};
+ //! SDLContext instance
+ SDLContext sdl_context{mediator};
+ //! LoopTimer instance
+ LoopTimer loop_timer{mediator};
private:
/**
diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp
index 15a0e3a..56e48d3 100644
--- a/src/crepe/api/LoopTimer.cpp
+++ b/src/crepe/api/LoopTimer.cpp
@@ -1,17 +1,16 @@
#include <chrono>
-#include "../facade/SDLContext.h"
#include "../util/Log.h"
+#include "facade/SDLContext.h"
+#include "manager/Manager.h"
#include "LoopTimer.h"
using namespace crepe;
-LoopTimer::LoopTimer() { dbg_trace(); }
-
-LoopTimer & LoopTimer::get_instance() {
- static LoopTimer instance;
- return instance;
+LoopTimer::LoopTimer(Mediator & mediator) : Manager(mediator) {
+ dbg_trace();
+ mediator.timer = *this;
}
void LoopTimer::start() {
@@ -67,7 +66,8 @@ void LoopTimer::enforce_frame_rate() {
= std::chrono::duration_cast<std::chrono::milliseconds>(this->frame_target_time
- frame_duration);
if (delay_time.count() > 0) {
- SDLContext::get_instance().delay(delay_time.count());
+ SDLContext & ctx = this->mediator.sdl_context;
+ ctx.delay(delay_time.count());
}
}
diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h
index 9393439..0a73a4c 100644
--- a/src/crepe/api/LoopTimer.h
+++ b/src/crepe/api/LoopTimer.h
@@ -1,19 +1,13 @@
#pragma once
+#include "manager/Manager.h"
#include <chrono>
namespace crepe {
-class LoopTimer {
+class LoopTimer : public Manager {
public:
/**
- * \brief Get the singleton instance of LoopTimer.
- *
- * \return A reference to the LoopTimer instance.
- */
- static LoopTimer & get_instance();
-
- /**
* \brief Get the current delta time for the current frame.
*
* \return Delta time in seconds since the last frame.
@@ -102,7 +96,7 @@ private:
*
* Private constructor for singleton pattern to restrict instantiation outside the class.
*/
- LoopTimer();
+ LoopTimer(Mediator & mediator);
/**
* \brief Update the timer to the current frame.
diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp
index 4091fd4..753a9e3 100644
--- a/src/crepe/api/Script.cpp
+++ b/src/crepe/api/Script.cpp
@@ -8,8 +8,7 @@ using namespace crepe;
using namespace std;
Script::~Script() {
- Mediator & mediator = this->mediator;
- EventManager & mgr = mediator.event_manager;
+ EventManager & mgr = this->mediator->event_manager;
for (auto id : this->listeners) {
mgr.unsubscribe(id);
}
@@ -21,7 +20,8 @@ void Script::subscribe(const EventHandler<CollisionEvent> & callback) {
}
void Script::set_next_scene(const string & name) {
- Mediator & mediator = this->mediator;
- SceneManager & mgr = mediator.scene_manager;
+ SceneManager & mgr = this->mediator->scene_manager;
mgr.set_next_scene(name);
}
+
+SaveManager & Script::get_save_manager() const { return this->mediator->save_manager; }
diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h
index 49bdcf5..668e5d1 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -132,6 +132,9 @@ protected:
*/
void set_next_scene(const std::string & name);
+ //! Retrieve SaveManager reference
+ SaveManager & get_save_manager() const;
+
//! \}
private:
diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp
index 16e0dc5..225a51c 100644
--- a/src/crepe/api/Script.hpp
+++ b/src/crepe/api/Script.hpp
@@ -31,8 +31,7 @@ void Script::logf(Args &&... args) {
template <typename EventType>
void Script::subscribe_internal(const EventHandler<EventType> & callback,
event_channel_t channel) {
- Mediator & mediator = this->mediator;
- EventManager & mgr = mediator.event_manager;
+ EventManager & mgr = this->mediator->event_manager;
subscription_t listener = mgr.subscribe<EventType>(
[this, callback](const EventType & data) -> bool {
bool & active = this->active;
diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp
index cc0e20a..ba684ba 100644
--- a/src/crepe/api/Sprite.cpp
+++ b/src/crepe/api/Sprite.cpp
@@ -1,26 +1,21 @@
#include <cmath>
-#include <utility>
#include "../util/Log.h"
+#include "api/Asset.h"
#include "Component.h"
#include "Sprite.h"
-#include "Texture.h"
#include "types.h"
using namespace std;
using namespace crepe;
-Sprite::Sprite(game_object_id_t id, Texture & texture, const Sprite::Data & data)
+Sprite::Sprite(game_object_id_t id, const Asset & texture, const Sprite::Data & data)
: Component(id),
- texture(std::move(texture)),
+ source(texture),
data(data) {
dbg_trace();
-
- this->mask.w = this->texture.get_size().x;
- this->mask.h = this->texture.get_size().y;
- this->aspect_ratio = static_cast<double>(this->mask.w) / this->mask.h;
}
Sprite::~Sprite() { dbg_trace(); }
diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h
index dbf41e4..a2409c2 100644
--- a/src/crepe/api/Sprite.h
+++ b/src/crepe/api/Sprite.h
@@ -1,9 +1,9 @@
#pragma once
#include "../Component.h"
+#include "api/Asset.h"
#include "Color.h"
-#include "Texture.h"
#include "types.h"
namespace crepe {
@@ -74,24 +74,15 @@ public:
* \param texture asset of the image
* \param ctx all the sprite data
*/
- Sprite(game_object_id_t id, Texture & texture, const Data & data);
+ Sprite(game_object_id_t id, const Asset & texture, const Data & data);
~Sprite();
//! Texture used for the sprite
- const Texture texture;
+ const Asset source;
Data data;
private:
- /**
- * \brief ratio of the img
- *
- * - This will multiply one of \c size variable if it is 0.
- * - Will be adjusted if \c Animator component is added to an GameObject that is why this
- * value cannot be const.
- */
- float aspect_ratio;
-
//! Reads the mask of sprite
friend class SDLContext;
@@ -101,6 +92,14 @@ private:
//! Reads the all the variables plus the mask
friend class AnimatorSystem;
+ /**
+ * \aspect_ratio the ratio of the sprite image
+ *
+ * - this value will only be set by the \c Animator component for the ratio of the Animation
+ * - if \c Animator component is not added it will not use this ratio (because 0) and will use aspect_ratio of the Asset.
+ */
+ float aspect_ratio = 0;
+
struct Rect {
int w = 0;
int h = 0;
diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp
deleted file mode 100644
index 2b56271..0000000
--- a/src/crepe/api/Texture.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#include "../facade/SDLContext.h"
-#include "../util/Log.h"
-
-#include "Asset.h"
-#include "Texture.h"
-#include "types.h"
-
-using namespace crepe;
-using namespace std;
-
-Texture::Texture(const Asset & src) {
- dbg_trace();
- this->load(src);
-}
-
-Texture::~Texture() {
- dbg_trace();
- this->texture.reset();
-}
-
-Texture::Texture(Texture && other) noexcept : texture(std::move(other.texture)) {}
-
-Texture & Texture::operator=(Texture && other) noexcept {
- if (this != &other) {
- texture = std::move(other.texture);
- }
- return *this;
-}
-
-void Texture::load(const Asset & res) {
- SDLContext & ctx = SDLContext::get_instance();
- this->texture = ctx.texture_from_path(res.get_path());
-}
-
-ivec2 Texture::get_size() const {
- if (this->texture == nullptr) return {};
- return SDLContext::get_instance().get_size(*this);
-}
diff --git a/src/crepe/api/Texture.h b/src/crepe/api/Texture.h
deleted file mode 100644
index 1817910..0000000
--- a/src/crepe/api/Texture.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#pragma once
-
-// FIXME: this header can't be included because this is an API header, and SDL2 development
-// headers won't be bundled with crepe. Why is this facade in the API namespace?
-
-#include <SDL2/SDL_render.h>
-#include <functional>
-#include <memory>
-
-#include "Asset.h"
-#include "types.h"
-
-namespace crepe {
-
-class SDLContext;
-class Animator;
-
-/**
- * \class Texture
- * \brief Manages texture loading and properties.
- *
- * The Texture class is responsible for loading an image from a source and providing access to
- * its dimensions. Textures can be used for rendering.
- */
-class Texture {
-
-public:
- /**
- * \brief Constructs a Texture from an Asset resource.
- * \param src Asset with texture data to load.
- */
- Texture(const Asset & src);
-
- /**
- * \brief Destroys the Texture instance, freeing associated resources.
- */
- ~Texture();
- // FIXME: this constructor shouldn't be necessary because this class doesn't manage memory
-
- Texture(Texture && other) noexcept;
- Texture & operator=(Texture && other) noexcept;
- Texture(const Texture &) = delete;
- Texture & operator=(const Texture &) = delete;
-
- /**
- * \brief Gets the width and height of the texture.
- * \return Width and height of the texture in pixels.
- */
- ivec2 get_size() const;
-
-private:
- /**
- * \brief Loads the texture from an Asset resource.
- * \param res Unique pointer to an Asset resource to load the texture from.
- */
- void load(const Asset & res);
-
-private:
- //! The texture of the class from the library
- std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> texture;
-
- //! Grants SDLContext access to private members.
- friend class SDLContext;
-
- //! Grants Animator access to private members.
- friend class Animator;
-};
-
-} // namespace crepe
diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h
index c278c87..bf9d124 100644
--- a/src/crepe/api/Vector2.h
+++ b/src/crepe/api/Vector2.h
@@ -66,6 +66,30 @@ struct Vector2 {
//! Checks if this vector is not equal to another vector.
bool operator!=(const Vector2<T> & other) const;
+
+ //! Truncates the vector to a maximum length.
+ void truncate(T max);
+
+ //! Normalizes the vector (resulting in vector with a length of 1).
+ void normalize();
+
+ //! Returns the length of the vector.
+ T length() const;
+
+ //! Returns the squared length of the vector.
+ T length_squared() const;
+
+ //! Returns the dot product (inwendig product) of this vector and another vector.
+ T dot(const Vector2<T> & other) const;
+
+ //! Returns the distance between this vector and another vector.
+ T distance(const Vector2<T> & other) const;
+
+ //! Returns the squared distance between this vector and another vector.
+ T distance_squared(const Vector2<T> & other) const;
+
+ //! Returns the perpendicular vector to this vector.
+ Vector2 perpendicular() const;
};
} // namespace crepe
diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp
index cad15f8..ff53cb0 100644
--- a/src/crepe/api/Vector2.hpp
+++ b/src/crepe/api/Vector2.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <cmath>
+
#include "Vector2.h"
namespace crepe {
@@ -115,4 +117,50 @@ bool Vector2<T>::operator!=(const Vector2<T> & other) const {
return !(*this == other);
}
+template <class T>
+void Vector2<T>::truncate(T max) {
+ if (length() > max) {
+ normalize();
+ *this *= max;
+ }
+}
+
+template <class T>
+void Vector2<T>::normalize() {
+ T len = length();
+ if (len > 0) {
+ *this /= len;
+ }
+}
+
+template <class T>
+T Vector2<T>::length() const {
+ return std::sqrt(x * x + y * y);
+}
+
+template <class T>
+T Vector2<T>::length_squared() const {
+ return x * x + y * y;
+}
+
+template <class T>
+T Vector2<T>::dot(const Vector2<T> & other) const {
+ return x * other.x + y * other.y;
+}
+
+template <class T>
+T Vector2<T>::distance(const Vector2<T> & other) const {
+ return (*this - other).length();
+}
+
+template <class T>
+T Vector2<T>::distance_squared(const Vector2<T> & other) const {
+ return (*this - other).length_squared();
+}
+
+template <class T>
+Vector2<T> Vector2<T>::perpendicular() const {
+ return {-y, x};
+}
+
} // namespace crepe
diff --git a/src/crepe/facade/CMakeLists.txt b/src/crepe/facade/CMakeLists.txt
index 4cc53bc..0598e16 100644
--- a/src/crepe/facade/CMakeLists.txt
+++ b/src/crepe/facade/CMakeLists.txt
@@ -1,5 +1,6 @@
target_sources(crepe PUBLIC
Sound.cpp
+ Texture.cpp
SoundContext.cpp
SDLContext.cpp
DB.cpp
@@ -7,6 +8,7 @@ target_sources(crepe PUBLIC
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
Sound.h
+ Texture.h
SoundContext.h
SDLContext.h
DB.h
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index 52929e1..b95ba6a 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -18,23 +18,18 @@
#include "../api/Color.h"
#include "../api/Config.h"
#include "../api/Sprite.h"
-#include "../api/Texture.h"
#include "../util/Log.h"
+#include "manager/Mediator.h"
#include "SDLContext.h"
+#include "Texture.h"
#include "types.h"
using namespace crepe;
using namespace std;
-SDLContext & SDLContext::get_instance() {
- static SDLContext instance;
- return instance;
-}
-
-SDLContext::SDLContext() {
+SDLContext::SDLContext(Mediator & mediator) {
dbg_trace();
-
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
throw runtime_error(format("SDLContext: SDL_Init error: {}", SDL_GetError()));
}
@@ -62,6 +57,8 @@ SDLContext::SDLContext() {
if (!(IMG_Init(img_flags) & img_flags)) {
throw runtime_error("SDLContext: SDL_image could not initialize!");
}
+
+ mediator.sdl_context = *this;
}
SDLContext::~SDLContext() {
@@ -221,25 +218,19 @@ void SDLContext::present_screen() {
SDL_RenderPresent(this->game_renderer.get());
}
-SDL_Rect SDLContext::get_src_rect(const Sprite & sprite) const {
- return SDL_Rect{
- .x = sprite.mask.x,
- .y = sprite.mask.y,
- .w = sprite.mask.w,
- .h = sprite.mask.h,
- };
-}
-
SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const {
const Sprite::Data & data = ctx.sprite.data;
+ float aspect_ratio
+ = (ctx.sprite.aspect_ratio == 0) ? ctx.texture.get_ratio() : ctx.sprite.aspect_ratio;
+
vec2 size = data.size;
if (data.size.x == 0 && data.size.y != 0) {
- size.x = data.size.y * ctx.sprite.aspect_ratio;
+ size.x = data.size.y * aspect_ratio;
}
if (data.size.y == 0 && data.size.x != 0) {
- size.y = data.size.x / ctx.sprite.aspect_ratio;
+ size.y = data.size.x / aspect_ratio;
}
const CameraValues & cam = ctx.cam;
@@ -260,15 +251,24 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const {
}
void SDLContext::draw(const RenderContext & ctx) {
-
const Sprite::Data & data = ctx.sprite.data;
SDL_RendererFlip render_flip
= (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * data.flip.flip_x)
| (SDL_FLIP_VERTICAL * data.flip.flip_y));
- SDL_Rect srcrect = this->get_src_rect(ctx.sprite);
+ SDL_Rect srcrect;
+ SDL_Rect * srcrect_ptr = NULL;
+ if (ctx.sprite.mask.w != 0 || ctx.sprite.mask.h != 0) {
+ srcrect.w = ctx.sprite.mask.w;
+ srcrect.h = ctx.sprite.mask.h;
+ srcrect.x = ctx.sprite.mask.x;
+ srcrect.y = ctx.sprite.mask.y;
+ srcrect_ptr = &srcrect;
+ }
+
SDL_FRect dstrect = this->get_dst_rect(SDLContext::DestinationRectangleData{
.sprite = ctx.sprite,
+ .texture = ctx.texture,
.cam = ctx.cam,
.pos = ctx.pos,
.img_scale = ctx.scale,
@@ -276,9 +276,9 @@ void SDLContext::draw(const RenderContext & ctx) {
double angle = ctx.angle + data.angle_offset;
- this->set_color_texture(ctx.sprite.texture, ctx.sprite.data.color);
- SDL_RenderCopyExF(this->game_renderer.get(), ctx.sprite.texture.texture.get(), &srcrect,
- &dstrect, angle, NULL, render_flip);
+ 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);
}
SDLContext::CameraValues SDLContext::set_camera(const Camera & cam) {
@@ -368,7 +368,7 @@ SDLContext::texture_from_path(const std::string & path) {
ivec2 SDLContext::get_size(const Texture & ctx) {
ivec2 size;
- SDL_QueryTexture(ctx.texture.get(), NULL, NULL, &size.x, &size.y);
+ SDL_QueryTexture(ctx.get_img(), NULL, NULL, &size.x, &size.y);
return size;
}
@@ -435,6 +435,6 @@ std::vector<SDLContext::EventData> SDLContext::get_events() {
return event_list;
}
void SDLContext::set_color_texture(const Texture & texture, const Color & color) {
- SDL_SetTextureColorMod(texture.texture.get(), color.r, color.g, color.b);
- SDL_SetTextureAlphaMod(texture.texture.get(), color.a);
+ SDL_SetTextureColorMod(texture.get_img(), color.r, color.g, color.b);
+ SDL_SetTextureAlphaMod(texture.get_img(), color.a);
}
diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h
index e232511..46b779f 100644
--- a/src/crepe/facade/SDLContext.h
+++ b/src/crepe/facade/SDLContext.h
@@ -14,14 +14,15 @@
#include "api/Color.h"
#include "api/KeyCodes.h"
#include "api/Sprite.h"
-#include "api/Texture.h"
#include "api/Transform.h"
+
#include "types.h"
namespace crepe {
-class LoopManager;
-class InputSystem;
+class Texture;
+class Mediator;
+
/**
* \class SDLContext
* \brief Facade for the SDL library
@@ -62,6 +63,7 @@ public:
//! rendering data needed to render on screen
struct RenderContext {
const Sprite & sprite;
+ const Texture & texture;
const CameraValues & cam;
const vec2 & pos;
const double & angle;
@@ -92,20 +94,27 @@ public:
float scroll_delta = INFINITY;
ivec2 rel_mouse_move = {-1, -1};
};
- /**
- * \brief Gets the singleton instance of SDLContext.
- * \return Reference to the SDLContext instance.
- */
- static SDLContext & get_instance();
+public:
SDLContext(const SDLContext &) = delete;
SDLContext(SDLContext &&) = delete;
SDLContext & operator=(const SDLContext &) = delete;
SDLContext & operator=(SDLContext &&) = delete;
-private:
- //! will only use get_events
- friend class InputSystem;
+public:
+ /**
+ * \brief Constructs an SDLContext instance.
+ * Initializes SDL, creates a window and renderer.
+ */
+ SDLContext(Mediator & mediator);
+
+ /**
+ * \brief Destroys the SDLContext instance.
+ * Cleans up SDL resources, including the window and renderer.
+ */
+ ~SDLContext();
+
+public:
/**
* \brief Retrieves a list of all events from the SDL context.
*
@@ -139,9 +148,7 @@ private:
*/
MouseButton sdl_to_mousebutton(Uint8 sdl_button);
-private:
- //! Will only use delay
- friend class LoopTimer;
+public:
/**
* \brief Gets the current SDL ticks since the program started.
* \return Current ticks in milliseconds as a constant uint64_t.
@@ -157,23 +164,7 @@ private:
*/
void delay(int ms) const;
-private:
- /**
- * \brief Constructs an SDLContext instance.
- * Initializes SDL, creates a window and renderer.
- */
- SDLContext();
-
- /**
- * \brief Destroys the SDLContext instance.
- * Cleans up SDL resources, including the window and renderer.
- */
- ~SDLContext();
-
-private:
- //! Will use the funtions: texture_from_path, get_width,get_height.
- friend class Texture;
-
+public:
/**
* \brief Loads a texture from a file path.
* \param path Path to the image file.
@@ -184,14 +175,11 @@ private:
/**
* \brief Gets the size of a texture.
* \param texture Reference to the Texture object.
- * \return Width and height of the texture as an integer.
+ * \return Width and height of the texture as an integer in pixels.
*/
ivec2 get_size(const Texture & ctx);
-private:
- //! Will use draw,clear_screen, present_screen, camera.
- friend class RenderSystem;
-
+public:
/**
* \brief Draws a sprite to the screen using the specified transform and camera.
* \param RenderContext Reference to rendering data to draw
@@ -207,33 +195,24 @@ private:
/**
* \brief sets the background of the camera (will be adjusted in future PR)
* \param camera Reference to the Camera object.
+ * \return camera data the component cannot store
*/
CameraValues set_camera(const Camera & camera);
-private:
+public:
//! the data needed to construct a sdl dst rectangle
struct DestinationRectangleData {
const Sprite & sprite;
+ const Texture & texture;
const CameraValues & cam;
const vec2 & pos;
const double & img_scale;
};
- /**
- * \brief calculates the sqaure size of the image
- *
- * \param sprite Reference to the sprite to calculate the rectangle
- * \return sdl rectangle to draw a src image
- */
- SDL_Rect get_src_rect(const Sprite & sprite) const;
/**
* \brief calculates the sqaure size of the image for destination
*
- * \param sprite Reference to the sprite to calculate rectangle
- * \param pos the pos in world units
- * \param cam the camera of the current scene
- * \param cam_pos the current postion of the camera
- * \param img_scale the image multiplier for increasing img size
+ * \param data needed to calculate a destination rectangle
* \return sdl rectangle to draw a dst image to draw on the screen
*/
SDL_FRect get_dst_rect(const DestinationRectangleData & data) const;
diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp
index ad50637..97e455e 100644
--- a/src/crepe/facade/Sound.cpp
+++ b/src/crepe/facade/Sound.cpp
@@ -6,7 +6,7 @@
using namespace crepe;
using namespace std;
-Sound::Sound(const Asset & src) : Resource(src) {
+Sound::Sound(const Asset & src, Mediator & mediator) : Resource(src, mediator) {
this->sample.load(src.get_path().c_str());
dbg_trace();
}
diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h
index 85d141b..4a5d692 100644
--- a/src/crepe/facade/Sound.h
+++ b/src/crepe/facade/Sound.h
@@ -8,6 +8,7 @@
namespace crepe {
class SoundContext;
+class Mediator;
/**
* \brief Sound resource facade
@@ -17,7 +18,7 @@ class SoundContext;
*/
class Sound : public Resource {
public:
- Sound(const Asset & src);
+ Sound(const Asset & src, Mediator & mediator);
~Sound(); // dbg_trace
private:
diff --git a/src/crepe/facade/Texture.cpp b/src/crepe/facade/Texture.cpp
new file mode 100644
index 0000000..b63403d
--- /dev/null
+++ b/src/crepe/facade/Texture.cpp
@@ -0,0 +1,28 @@
+#include "../util/Log.h"
+#include "facade/SDLContext.h"
+#include "manager/Mediator.h"
+
+#include "Resource.h"
+#include "Texture.h"
+#include "types.h"
+
+using namespace crepe;
+using namespace std;
+
+Texture::Texture(const Asset & src, Mediator & mediator) : Resource(src, mediator) {
+ dbg_trace();
+ SDLContext & ctx = mediator.sdl_context;
+ this->texture = ctx.texture_from_path(src.get_path());
+ this->size = ctx.get_size(*this);
+ this->aspect_ratio = static_cast<float>(this->size.x) / this->size.y;
+}
+
+Texture::~Texture() {
+ dbg_trace();
+ this->texture.reset();
+}
+
+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/facade/Texture.h b/src/crepe/facade/Texture.h
new file mode 100644
index 0000000..cdacac4
--- /dev/null
+++ b/src/crepe/facade/Texture.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <SDL2/SDL_render.h>
+#include <memory>
+
+#include "../Resource.h"
+
+#include "types.h"
+
+namespace crepe {
+
+class Mediator;
+class Asset;
+
+/**
+ * \class Texture
+ * \brief Manages texture loading and properties.
+ *
+ * The Texture class is responsible for loading an image from a source and providing access to
+ * its dimensions. Textures can be used for rendering.
+ */
+class Texture : public Resource {
+
+public:
+ /**
+ * \brief Constructs a Texture from an Asset resource.
+ * \param src Asset with texture data to load.
+ * \param mediator use the SDLContext reference to load the image
+ */
+ Texture(const Asset & src, Mediator & mediator);
+
+ /**
+ * \brief Destroys the Texture instance
+ */
+ ~Texture();
+
+ /**
+ * \brief get width and height of image in pixels
+ * \return pixel size width and height
+ *
+ */
+ const ivec2 & get_size() const noexcept;
+
+ /**
+ * \brief aspect_ratio of image
+ * \return ratio
+ *
+ */
+ const float & get_ratio() const noexcept;
+
+ /**
+ * \brief get the image texture
+ * \return SDL_Texture
+ *
+ */
+ SDL_Texture * get_img() const noexcept;
+
+private:
+ //! The texture of the class from the library
+ std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> texture;
+
+ // texture size in pixel
+ ivec2 size;
+
+ //! ratio of image
+ float aspect_ratio;
+};
+
+} // namespace crepe
diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h
index ba0b41f..628154a 100644
--- a/src/crepe/manager/Mediator.h
+++ b/src/crepe/manager/Mediator.h
@@ -3,16 +3,16 @@
#include "../util/OptionalRef.h"
// TODO: remove these singletons:
-#include "../facade/SDLContext.h"
#include "EventManager.h"
-#include "SaveManager.h"
-#include "api/LoopTimer.h"
namespace crepe {
class ComponentManager;
class SceneManager;
+class SaveManager;
class ResourceManager;
+class SDLContext;
+class LoopTimer;
/**
* Struct to pass references to classes that would otherwise need to be singletons down to
@@ -27,13 +27,13 @@ class ResourceManager;
* \warning This class should never be directly accessible from the API
*/
struct Mediator {
+ OptionalRef<SDLContext> sdl_context;
OptionalRef<ComponentManager> component_manager;
OptionalRef<SceneManager> scene_manager;
- OptionalRef<SaveManager> save_manager = SaveManager::get_instance();
+ OptionalRef<SaveManager> save_manager;
OptionalRef<EventManager> event_manager = EventManager::get_instance();
OptionalRef<ResourceManager> resource_manager;
- OptionalRef<SDLContext> sdl_context = SDLContext::get_instance();
- OptionalRef<LoopTimer> timer = LoopTimer::get_instance();
+ OptionalRef<LoopTimer> timer;
};
} // namespace crepe
diff --git a/src/crepe/manager/ResourceManager.cpp b/src/crepe/manager/ResourceManager.cpp
index 7c01808..a141a46 100644
--- a/src/crepe/manager/ResourceManager.cpp
+++ b/src/crepe/manager/ResourceManager.cpp
@@ -6,8 +6,8 @@ using namespace crepe;
using namespace std;
ResourceManager::ResourceManager(Mediator & mediator) : Manager(mediator) {
- mediator.resource_manager = *this;
dbg_trace();
+ mediator.resource_manager = *this;
}
ResourceManager::~ResourceManager() { dbg_trace(); }
diff --git a/src/crepe/manager/ResourceManager.hpp b/src/crepe/manager/ResourceManager.hpp
index 5167d71..cf5c949 100644
--- a/src/crepe/manager/ResourceManager.hpp
+++ b/src/crepe/manager/ResourceManager.hpp
@@ -13,7 +13,7 @@ T & ResourceManager::get(const Asset & asset) {
"cache must recieve a derivative class of Resource");
CacheEntry & entry = this->get_entry(asset);
- if (entry.resource == nullptr) entry.resource = make_unique<T>(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)
diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp
index 034a283..f313ed2 100644
--- a/src/crepe/manager/SaveManager.cpp
+++ b/src/crepe/manager/SaveManager.cpp
@@ -1,13 +1,25 @@
#include "../ValueBroker.h"
#include "../api/Config.h"
#include "../facade/DB.h"
-#include "../util/Log.h"
#include "SaveManager.h"
using namespace std;
using namespace crepe;
+SaveManager::SaveManager(Mediator & mediator) : Manager(mediator) {
+ mediator.save_manager = *this;
+}
+
+DB & SaveManager::get_db() {
+ if (this->db == nullptr) {
+ Config & cfg = Config::get_instance();
+ this->db
+ = {new DB(cfg.savemgr.location), [](void * db) { delete static_cast<DB *>(db); }};
+ }
+ return *static_cast<DB *>(this->db.get());
+}
+
template <>
string SaveManager::serialize(const string & value) const noexcept {
return value;
@@ -90,22 +102,6 @@ int32_t SaveManager::deserialize(const string & value) const noexcept {
return deserialize<int64_t>(value);
}
-SaveManager::SaveManager() { dbg_trace(); }
-
-SaveManager & SaveManager::get_instance() {
- dbg_trace();
- static SaveManager instance;
- return instance;
-}
-
-DB & SaveManager::get_db() {
- Config & cfg = Config::get_instance();
- // TODO: make this path relative to XDG_DATA_HOME on Linux and whatever the
- // default equivalent is on Windows using some third party library
- static DB db(cfg.savemgr.location);
- return db;
-}
-
bool SaveManager::has(const string & key) {
DB & db = this->get_db();
return db.has(key);
diff --git a/src/crepe/manager/SaveManager.h b/src/crepe/manager/SaveManager.h
index e2ef005..1e34bc0 100644
--- a/src/crepe/manager/SaveManager.h
+++ b/src/crepe/manager/SaveManager.h
@@ -1,9 +1,12 @@
#pragma once
+#include <functional>
#include <memory>
#include "../ValueBroker.h"
+#include "Manager.h"
+
namespace crepe {
class DB;
@@ -18,7 +21,7 @@ class DB;
*
* The underlying database is a key-value store.
*/
-class SaveManager {
+class SaveManager : public Manager {
public:
/**
* \brief Get a read/write reference to a value and initialize it if it does not yet exist
@@ -63,8 +66,8 @@ public:
*/
bool has(const std::string & key);
-private:
- SaveManager();
+public:
+ SaveManager(Mediator & mediator);
virtual ~SaveManager() = default;
private:
@@ -89,26 +92,13 @@ private:
template <typename T>
T deserialize(const std::string & value) const noexcept;
-public:
- // singleton
- static SaveManager & get_instance();
- SaveManager(const SaveManager &) = delete;
- SaveManager(SaveManager &&) = delete;
- SaveManager & operator=(const SaveManager &) = delete;
- SaveManager & operator=(SaveManager &&) = delete;
+protected:
+ //! Create or return DB
+ virtual DB & get_db();
private:
- /**
- * \brief Create an instance of DB and return its reference
- *
- * \returns DB instance
- *
- * This function exists because DB is a facade class, which can't directly be used in the API
- * without workarounds
- *
- * TODO: better solution
- */
- static DB & get_db();
+ //! Database
+ std::unique_ptr<void, std::function<void(void *)>> db = nullptr;
};
} // namespace crepe
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
new file mode 100644
index 0000000..7f04432
--- /dev/null
+++ b/src/crepe/system/AISystem.cpp
@@ -0,0 +1,186 @@
+#include <algorithm>
+#include <cmath>
+
+#include "api/LoopTimer.h"
+#include "manager/ComponentManager.h"
+#include "manager/Mediator.h"
+
+#include "AISystem.h"
+
+using namespace crepe;
+
+void AISystem::update() {
+ const Mediator & mediator = this->mediator;
+ ComponentManager & mgr = mediator.component_manager;
+ LoopTimer & timer = mediator.timer;
+ RefVector<AI> ai_components = mgr.get_components_by_type<AI>();
+
+ //TODO: Use fixed loop dt (this is not available at master at the moment)
+ double dt = timer.get_delta_time();
+
+ // Loop through all AI components
+ for (AI & ai : ai_components) {
+ if (!ai.active) {
+ continue;
+ }
+
+ RefVector<Rigidbody> rigidbodies
+ = 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");
+ }
+ Rigidbody & rigidbody = rigidbodies.front().get();
+ if (!rigidbody.active) {
+ continue;
+ }
+ if (rigidbody.data.mass <= 0) {
+ throw std::runtime_error("Mass must be greater than 0");
+ }
+
+ // Calculate the force to apply to the entity
+ vec2 force = this->calculate(ai, rigidbody);
+ // Calculate the acceleration (using the above calculated force)
+ vec2 acceleration = force / rigidbody.data.mass;
+ // Finally, update Rigidbody's velocity
+ rigidbody.data.linear_velocity += acceleration * dt;
+ }
+}
+
+vec2 AISystem::calculate(AI & ai, const Rigidbody & rigidbody) {
+ const Mediator & mediator = this->mediator;
+ ComponentManager & mgr = mediator.component_manager;
+ RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id);
+ Transform & transform = transforms.front().get();
+
+ vec2 force;
+
+ // Run all the behaviors that are on, and stop if the force gets too high
+ if (ai.on(AI::BehaviorTypeMask::FLEE)) {
+ vec2 force_to_add = this->flee(ai, rigidbody, transform);
+
+ if (!this->accumulate_force(ai, force, force_to_add)) {
+ return force;
+ }
+ }
+ if (ai.on(AI::BehaviorTypeMask::ARRIVE)) {
+ vec2 force_to_add = this->arrive(ai, rigidbody, transform);
+
+ if (!this->accumulate_force(ai, force, force_to_add)) {
+ return force;
+ }
+ }
+ if (ai.on(AI::BehaviorTypeMask::SEEK)) {
+ vec2 force_to_add = this->seek(ai, rigidbody, transform);
+
+ if (!this->accumulate_force(ai, force, force_to_add)) {
+ return force;
+ }
+ }
+ if (ai.on(AI::BehaviorTypeMask::PATH_FOLLOW)) {
+ vec2 force_to_add = this->path_follow(ai, rigidbody, transform);
+
+ if (!this->accumulate_force(ai, force, force_to_add)) {
+ return force;
+ }
+ }
+
+ return force;
+}
+
+bool AISystem::accumulate_force(const AI & ai, vec2 & running_total, vec2 & force_to_add) {
+ float magnitude = running_total.length();
+ float magnitude_remaining = ai.max_force - magnitude;
+
+ if (magnitude_remaining <= 0.0f) {
+ // If the force is already at/above the max force, return false
+ return false;
+ }
+
+ float magnitude_to_add = force_to_add.length();
+ if (magnitude_to_add < magnitude_remaining) {
+ // If the force to add is less than the remaining force, add it
+ running_total += force_to_add;
+ } else {
+ // If the force to add is greater than the remaining force, add a fraction of it
+ force_to_add.normalize();
+ running_total += force_to_add * magnitude_remaining;
+ }
+
+ return true;
+}
+
+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();
+ desired_velocity *= rigidbody.data.max_linear_velocity;
+
+ return desired_velocity - rigidbody.data.linear_velocity;
+}
+
+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};
+ }
+ desired_velocity.normalize();
+ desired_velocity *= rigidbody.data.max_linear_velocity;
+
+ return desired_velocity - rigidbody.data.linear_velocity;
+}
+
+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();
+ if (distance > 0.0f) {
+ if (ai.arrive_deceleration <= 0.0f) {
+ throw std::runtime_error("Deceleration rate must be greater than 0");
+ }
+
+ float speed = distance / ai.arrive_deceleration;
+ speed = std::min(speed, rigidbody.data.max_linear_velocity.length());
+ vec2 desired_velocity = to_target * (speed / distance);
+
+ return desired_velocity - rigidbody.data.linear_velocity;
+ }
+
+ return vec2{0, 0};
+}
+
+vec2 AISystem::path_follow(AI & ai, const Rigidbody & rigidbody, const Transform & transform) {
+ if (ai.path.empty()) {
+ return vec2{0, 0};
+ }
+
+ // Get the target node
+ vec2 target = ai.path.at(ai.path_index);
+ // Calculate the force to apply to the entity
+ vec2 to_target = target - transform.position;
+ if (to_target.length_squared() > ai.path_node_distance * ai.path_node_distance) {
+ // If the entity is not close enough to the target node, seek it
+ ai.seek_target = target;
+ ai.arrive_target = target;
+ } else {
+ // If the entity is close enough to the target node, move to the next node
+ ai.path_index++;
+ if (ai.path_index >= ai.path.size()) {
+ if (ai.path_loop) {
+ // If the path is looping, reset the path index
+ ai.path_index = 0;
+ } else {
+ // If the path is not looping, arrive at the last node
+ ai.path_index = ai.path.size() - 1;
+ return this->arrive(ai, rigidbody, transform);
+ }
+ }
+ }
+
+ // Seek the target node
+ return this->seek(ai, rigidbody, transform);
+}
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
new file mode 100644
index 0000000..d5f8a8e
--- /dev/null
+++ b/src/crepe/system/AISystem.h
@@ -0,0 +1,81 @@
+#pragma once
+
+#include "api/AI.h"
+#include "api/Rigidbody.h"
+
+#include "System.h"
+#include "api/Transform.h"
+#include "types.h"
+
+namespace crepe {
+
+/**
+ * \brief The AISystem is used to control the movement of entities using AI.
+ *
+ * The AISystem is used to control the movement of entities using AI. The AISystem can be used to
+ * implement different behaviors such as seeking, fleeing, arriving, and path following.
+ */
+class AISystem : public System {
+public:
+ using System::System;
+
+ //! Update the AI system
+ void update() override;
+
+private:
+ /**
+ * \brief Calculate the total force to apply to the entity
+ *
+ * \param ai The AI component
+ * \param rigidbody The Rigidbody component
+ */
+ vec2 calculate(AI & ai, const Rigidbody & rigidbody);
+ /**
+ * \brief Accumulate the force to apply to the entity
+ *
+ * \param ai The AI component
+ * \param running_total The running total of the force
+ * \param force_to_add The force to add
+ * \return true if the force was added, false otherwise
+ */
+ bool accumulate_force(const AI & ai, vec2 & running_total, vec2 & force_to_add);
+
+ /**
+ * \brief Calculate the seek force
+ *
+ * \param ai The AI component
+ * \param rigidbody The Rigidbody component
+ * \param transform The Transform component
+ * \return The seek force
+ */
+ vec2 seek(const AI & ai, const Rigidbody & rigidbody, const Transform & transform) const;
+ /**
+ * \brief Calculate the flee force
+ *
+ * \param ai The AI component
+ * \param rigidbody The Rigidbody component
+ * \param transform The Transform component
+ * \return The flee force
+ */
+ vec2 flee(const AI & ai, const Rigidbody & rigidbody, const Transform & transform) const;
+ /**
+ * \brief Calculate the arrive force
+ *
+ * \param ai The AI component
+ * \param rigidbody The Rigidbody component
+ * \param transform The Transform component
+ * \return The arrive force
+ */
+ vec2 arrive(const AI & ai, const Rigidbody & rigidbody, const Transform & transform) const;
+ /**
+ * \brief Calculate the path follow force
+ *
+ * \param ai The AI component
+ * \param rigidbody The Rigidbody component
+ * \param transform The Transform component
+ * \return The path follow force
+ */
+ vec2 path_follow(AI & ai, const Rigidbody & rigidbody, const Transform & transform);
+};
+
+} // namespace crepe
diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp
index 549c35d..d61ba35 100644
--- a/src/crepe/system/AnimatorSystem.cpp
+++ b/src/crepe/system/AnimatorSystem.cpp
@@ -23,7 +23,7 @@ void AnimatorSystem::update() {
int last_frame = ctx.row;
- int cycle_end = (ctx.cycle_end == -1) ? a.max_rows : ctx.cycle_end;
+ 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;
diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp
index ddba268..b1aa0f8 100644
--- a/src/crepe/system/AudioSystem.cpp
+++ b/src/crepe/system/AudioSystem.cpp
@@ -30,8 +30,7 @@ void AudioSystem::diff_update(AudioSource & component, Sound & resource) {
context.stop(component.voice);
return;
}
- if (component.play_on_awake)
- component.oneshot_play = true;
+ if (component.play_on_awake) component.oneshot_play = true;
}
if (!component.active) return;
diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt
index 6b2e099..0e2db76 100644
--- a/src/crepe/system/CMakeLists.txt
+++ b/src/crepe/system/CMakeLists.txt
@@ -8,6 +8,7 @@ target_sources(crepe PUBLIC
AudioSystem.cpp
AnimatorSystem.cpp
InputSystem.cpp
+ AISystem.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -19,4 +20,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
AudioSystem.h
AnimatorSystem.h
InputSystem.h
+ AISystem.h
)
diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp
index aaa8bdf..a710ae2 100644
--- a/src/crepe/system/InputSystem.cpp
+++ b/src/crepe/system/InputSystem.cpp
@@ -1,6 +1,8 @@
#include "../api/Button.h"
#include "../manager/ComponentManager.h"
#include "../manager/EventManager.h"
+#include "facade/SDLContext.h"
+#include "util/Log.h"
#include "InputSystem.h"
@@ -9,7 +11,8 @@ using namespace crepe;
void InputSystem::update() {
ComponentManager & mgr = this->mediator.component_manager;
EventManager & event_mgr = this->mediator.event_manager;
- std::vector<SDLContext::EventData> event_list = SDLContext::get_instance().get_events();
+ SDLContext & context = this->mediator.sdl_context;
+ std::vector<SDLContext::EventData> event_list = context.get_events();
RefVector<Button> buttons = mgr.get_components_by_type<Button>();
RefVector<Camera> cameras = mgr.get_components_by_type<Camera>();
OptionalRef<Camera> curr_cam_ref;
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index 26f2c85..51340fb 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -10,7 +10,9 @@
#include "../api/Sprite.h"
#include "../api/Transform.h"
#include "../facade/SDLContext.h"
+#include "../facade/Texture.h"
#include "../manager/ComponentManager.h"
+#include "../manager/ResourceManager.h"
#include "RenderSystem.h"
@@ -72,6 +74,8 @@ bool RenderSystem::render_particle(const Sprite & sprite, const SDLContext::Came
ComponentManager & mgr = this->mediator.component_manager;
SDLContext & ctx = this->mediator.sdl_context;
+ ResourceManager & resource_manager = this->mediator.resource_manager;
+ Texture & res = resource_manager.get<Texture>(sprite.source);
vector<reference_wrapper<ParticleEmitter>> emitters
= mgr.get_components_by_id<ParticleEmitter>(sprite.game_object_id);
@@ -88,6 +92,7 @@ bool RenderSystem::render_particle(const Sprite & sprite, const SDLContext::Came
ctx.draw(SDLContext::RenderContext{
.sprite = sprite,
+ .texture = res,
.cam = cam,
.pos = p.position,
.angle = p.angle,
@@ -100,8 +105,12 @@ bool RenderSystem::render_particle(const Sprite & sprite, const SDLContext::Came
void RenderSystem::render_normal(const Sprite & sprite, const SDLContext::CameraValues & cam,
const Transform & tm) {
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{
.sprite = sprite,
+ .texture = res,
.cam = cam,
.pos = tm.position,
.angle = tm.rotation,
diff --git a/src/crepe/util/OptionalRef.h b/src/crepe/util/OptionalRef.h
index 3201667..1b2cb3f 100644
--- a/src/crepe/util/OptionalRef.h
+++ b/src/crepe/util/OptionalRef.h
@@ -25,7 +25,7 @@ public:
*/
OptionalRef<T> & operator=(T & ref);
/**
- * \brief Retrieve this reference
+ * \brief Retrieve this reference (cast)
*
* \returns Internal reference if it is set
*
@@ -33,6 +33,14 @@ public:
*/
operator T &() const;
/**
+ * \brief Retrieve this reference (member access)
+ *
+ * \returns Internal reference if it is set
+ *
+ * \throws std::runtime_error if this function is called while the reference it not set
+ */
+ T * operator->() const;
+ /**
* \brief Check if this reference is not empty
*
* \returns `true` if reference is set, or `false` if it is not
diff --git a/src/crepe/util/OptionalRef.hpp b/src/crepe/util/OptionalRef.hpp
index 4608c9e..5e36b3a 100644
--- a/src/crepe/util/OptionalRef.hpp
+++ b/src/crepe/util/OptionalRef.hpp
@@ -19,6 +19,13 @@ OptionalRef<T>::operator T &() const {
}
template <typename T>
+T * OptionalRef<T>::operator->() const {
+ if (this->ref == nullptr)
+ throw std::runtime_error("OptionalRef: attempt to dereference nullptr");
+ return this->ref;
+}
+
+template <typename T>
OptionalRef<T> & OptionalRef<T>::operator=(T & ref) {
this->ref = &ref;
return *this;
diff --git a/src/example/AITest.cpp b/src/example/AITest.cpp
new file mode 100644
index 0000000..f4efc9f
--- /dev/null
+++ b/src/example/AITest.cpp
@@ -0,0 +1,86 @@
+#include <crepe/api/AI.h>
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/Camera.h>
+#include <crepe/api/Color.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/LoopManager.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Scene.h>
+#include <crepe/api/Script.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/api/Texture.h>
+#include <crepe/manager/Mediator.h>
+#include <crepe/types.h>
+
+using namespace crepe;
+using namespace std;
+
+class Script1 : public Script {
+ bool shutdown(const ShutDownEvent & event) {
+ // Very dirty way of shutting down the game
+ throw "ShutDownEvent";
+ return true;
+ }
+
+ bool mousemove(const MouseMoveEvent & event) {
+ /*RefVector<AI> aivec = this->get_components<AI>();
+ AI & ai = aivec.front().get();
+ ai.flee_target
+ = vec2{static_cast<float>(event.mouse_x), static_cast<float>(event.mouse_y)};*/
+ return true;
+ }
+
+ void init() {
+ subscribe<ShutDownEvent>(
+ [this](const ShutDownEvent & ev) -> bool { return this->shutdown(ev); });
+ subscribe<MouseMoveEvent>(
+ [this](const MouseMoveEvent & ev) -> bool { return this->mousemove(ev); });
+ }
+};
+
+class Scene1 : public Scene {
+public:
+ void load_scene() override {
+ Mediator & mediator = this->mediator;
+ ComponentManager & mgr = mediator.component_manager;
+
+ GameObject game_object1 = mgr.new_object("", "", vec2{0, 0}, 0, 1);
+ GameObject game_object2 = mgr.new_object("", "", vec2{0, 0}, 0, 1);
+
+ Texture img = Texture("asset/texture/test_ap43.png");
+ game_object1.add_component<Sprite>(img, Sprite::Data{
+ .color = Color::MAGENTA,
+ .flip = Sprite::FlipSettings{false, false},
+ .sorting_in_layer = 1,
+ .order_in_layer = 1,
+ .size = {0, 195},
+ });
+ AI & ai = game_object1.add_component<AI>(3000);
+ // ai.arrive_on();
+ // ai.flee_on();
+ ai.path_follow_on();
+ ai.make_oval_path(500, 1000, {0, -1000}, 1.5708, true);
+ ai.make_oval_path(1000, 500, {0, 500}, 4.7124, false);
+ game_object1.add_component<Rigidbody>(Rigidbody::Data{
+ .mass = 0.1f,
+ .max_linear_velocity = {40, 40},
+ });
+ game_object1.add_component<BehaviorScript>().set_script<Script1>();
+
+ game_object2.add_component<Camera>(ivec2{1080, 720}, vec2{5000, 5000},
+ Camera::Data{
+ .bg_color = Color::WHITE,
+ .zoom = 1,
+ });
+ }
+
+ string get_name() const override { return "Scene1"; }
+};
+
+int main() {
+ LoopManager engine;
+ engine.add_scene<Scene1>();
+ engine.start();
+
+ return 0;
+}
diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt
index 73fc512..187ed46 100644
--- a/src/example/CMakeLists.txt
+++ b/src/example/CMakeLists.txt
@@ -16,7 +16,7 @@ function(add_example target_name)
add_dependencies(examples ${target_name})
endfunction()
-add_example(savemgr)
add_example(rendering_particle)
add_example(game)
add_example(button)
+add_example(AITest)
diff --git a/src/example/rendering_particle.cpp b/src/example/rendering_particle.cpp
index 29d475d..bd4ef95 100644
--- a/src/example/rendering_particle.cpp
+++ b/src/example/rendering_particle.cpp
@@ -1,3 +1,4 @@
+#include "api/Asset.h"
#include <crepe/Component.h>
#include <crepe/api/Animator.h>
#include <crepe/api/Camera.h>
@@ -7,11 +8,11 @@
#include <crepe/api/ParticleEmitter.h>
#include <crepe/api/Rigidbody.h>
#include <crepe/api/Sprite.h>
-#include <crepe/api/Texture.h>
#include <crepe/api/Transform.h>
#include <crepe/manager/ComponentManager.h>
#include <crepe/manager/Mediator.h>
#include <crepe/types.h>
+#include <iostream>
using namespace crepe;
using namespace std;
@@ -41,13 +42,15 @@ using namespace std;
class TestScene : public Scene {
public:
void load_scene() {
+
+ cout << "TestScene" << endl;
Mediator & mediator = this->mediator;
ComponentManager & mgr = mediator.component_manager;
GameObject game_object = mgr.new_object("", "", vec2{0, 0}, 0, 1);
Color color(255, 255, 255, 255);
- auto img = Texture("asset/spritesheet/pokemon_spritesheet.png");
+ Asset img{"asset/spritesheet/spritesheet_test.png"};
Sprite & test_sprite = game_object.add_component<Sprite>(
img, Sprite::Data{
@@ -57,16 +60,11 @@ public:
.order_in_layer = 2,
.size = {0, 100},
.angle_offset = 0,
- .position_offset = {100, 0},
+ .position_offset = {0, 0},
});
- auto & anim = game_object.add_component<Animator>(test_sprite, 4, 4,
- Animator::Data{
- .fps = 1,
- .looping = false,
- });
- anim.set_anim(2);
- anim.active = false;
+ //auto & anim = game_object.add_component<Animator>(test_sprite,ivec2{32, 64}, uvec2{4,1}, Animator::Data{});
+ //anim.set_anim(0);
auto & cam = game_object.add_component<Camera>(ivec2{1280, 720}, vec2{400, 400},
Camera::Data{
diff --git a/src/example/savemgr.cpp b/src/example/savemgr.cpp
deleted file mode 100644
index 65c4a34..0000000
--- a/src/example/savemgr.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/** \file
- *
- * Standalone example for usage of the save manager
- */
-
-#include <cassert>
-#include <crepe/api/Config.h>
-#include <crepe/api/SaveManager.h>
-#include <crepe/util/Log.h>
-#include <crepe/util/Proxy.h>
-
-using namespace crepe;
-
-// unrelated setup code
-int _ = []() {
- // make sure all log messages get printed
- auto & cfg = Config::get_instance();
- cfg.log.level = Log::Level::TRACE;
-
- return 0; // satisfy compiler
-}();
-
-int main() {
- const char * key = "mygame.test";
-
- SaveManager & mgr = SaveManager::get_instance();
-
- dbg_logf("has key = {}", mgr.has(key));
- ValueBroker<int> prop = mgr.get<int>(key, 0);
- Proxy<int> val = mgr.get<int>(key, 0);
-
- dbg_logf("val = {}", mgr.get<int>(key).get());
- prop.set(1);
- dbg_logf("val = {}", mgr.get<int>(key).get());
- val = 2;
- dbg_logf("val = {}", mgr.get<int>(key).get());
- mgr.set<int>(key, 3);
- dbg_logf("val = {}", mgr.get<int>(key).get());
-
- dbg_logf("has key = {}", mgr.has(key));
- assert(true == mgr.has(key));
-
- return 0;
-}
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index ef7b5c6..7196404 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -19,5 +19,7 @@ target_sources(test_main PUBLIC
ScriptEventTest.cpp
ScriptSceneTest.cpp
Profiling.cpp
+ SaveManagerTest.cpp
+ ScriptSaveManagerTest.cpp
ScriptECSTest.cpp
)
diff --git a/src/test/InputTest.cpp b/src/test/InputTest.cpp
index 73eaab3..4c05e56 100644
--- a/src/test/InputTest.cpp
+++ b/src/test/InputTest.cpp
@@ -24,6 +24,7 @@ class InputTest : public ::testing::Test {
public:
Mediator mediator;
ComponentManager mgr{mediator};
+ SDLContext sdl_context{mediator};
InputSystem input_system{mediator};
diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp
index 1409c4f..9112a3f 100644
--- a/src/test/ParticleTest.cpp
+++ b/src/test/ParticleTest.cpp
@@ -1,10 +1,10 @@
+#include "api/Asset.h"
#include <crepe/Particle.h>
#include <crepe/api/Config.h>
#include <crepe/api/GameObject.h>
#include <crepe/api/ParticleEmitter.h>
#include <crepe/api/Rigidbody.h>
#include <crepe/api/Sprite.h>
-#include <crepe/api/Texture.h>
#include <crepe/api/Transform.h>
#include <crepe/manager/ComponentManager.h>
#include <crepe/system/ParticleSystem.h>
@@ -30,7 +30,7 @@ public:
GameObject game_object = mgr.new_object("", "", vec2{0, 0}, 0, 0);
Color color(0, 0, 0, 0);
- auto s1 = Texture("asset/texture/img.png");
+ auto s1 = Asset("asset/texture/img.png");
Sprite & test_sprite = game_object.add_component<Sprite>(
s1, Sprite::Data{
.color = color,
diff --git a/src/test/Profiling.cpp b/src/test/Profiling.cpp
index c753bca..cc4c637 100644
--- a/src/test/Profiling.cpp
+++ b/src/test/Profiling.cpp
@@ -1,9 +1,11 @@
-#include "manager/Mediator.h"
-#include "system/ParticleSystem.h"
-#include "system/PhysicsSystem.h"
-#include "system/RenderSystem.h"
#include <chrono>
#include <cmath>
+#include <crepe/api/Asset.h>
+#include <crepe/manager/Mediator.h>
+#include <crepe/manager/ResourceManager.h>
+#include <crepe/system/ParticleSystem.h>
+#include <crepe/system/PhysicsSystem.h>
+#include <crepe/system/RenderSystem.h>
#include <gtest/gtest.h>
#define private public
@@ -54,6 +56,8 @@ public:
const std::chrono::microseconds duration = 16000us;
Mediator m;
+ SDLContext sdl_context{m};
+ ResourceManager resman{m};
ComponentManager mgr{m};
// Add system used for profling tests
CollisionSystem collision_sys{m};
@@ -167,15 +171,15 @@ TEST_F(DISABLED_ProfilingTest, Profiling_2) {
gameobject.add_component<BoxCollider>(vec2{0, 0}, vec2{1, 1});
gameobject.add_component<BehaviorScript>().set_script<TestScript>();
- auto img = Texture("asset/texture/square.png");
Sprite & test_sprite = gameobject.add_component<Sprite>(
- img, Sprite::Data{
- .color = {0, 0, 0, 0},
- .flip = {.flip_x = false, .flip_y = false},
- .sorting_in_layer = 1,
- .order_in_layer = 1,
- .size = {.y = 500},
- });
+ Asset{"asset/texture/square.png"},
+ Sprite::Data{
+ .color = {0, 0, 0, 0},
+ .flip = {.flip_x = false, .flip_y = false},
+ .sorting_in_layer = 1,
+ .order_in_layer = 1,
+ .size = {.y = 500},
+ });
}
this->game_object_count++;
@@ -205,15 +209,15 @@ TEST_F(DISABLED_ProfilingTest, Profiling_3) {
});
gameobject.add_component<BoxCollider>(vec2{0, 0}, vec2{1, 1});
gameobject.add_component<BehaviorScript>().set_script<TestScript>();
- auto img = Texture("asset/texture/square.png");
Sprite & test_sprite = gameobject.add_component<Sprite>(
- img, Sprite::Data{
- .color = {0, 0, 0, 0},
- .flip = {.flip_x = false, .flip_y = false},
- .sorting_in_layer = 1,
- .order_in_layer = 1,
- .size = {.y = 500},
- });
+ Asset{"asset/texture/square.png"},
+ Sprite::Data{
+ .color = {0, 0, 0, 0},
+ .flip = {.flip_x = false, .flip_y = false},
+ .sorting_in_layer = 1,
+ .order_in_layer = 1,
+ .size = {.y = 500},
+ });
auto & test = gameobject.add_component<ParticleEmitter>(ParticleEmitter::Data{
.max_particles = 10,
.emission_rate = 100,
diff --git a/src/test/RenderSystemTest.cpp b/src/test/RenderSystemTest.cpp
index 205f534..b4519cb 100644
--- a/src/test/RenderSystemTest.cpp
+++ b/src/test/RenderSystemTest.cpp
@@ -1,3 +1,6 @@
+#include "api/Asset.h"
+#include "facade/SDLContext.h"
+#include "manager/ResourceManager.h"
#include "types.h"
#include <functional>
#include <gtest/gtest.h>
@@ -11,7 +14,6 @@
#include <crepe/api/Color.h>
#include <crepe/api/GameObject.h>
#include <crepe/api/Sprite.h>
-#include <crepe/api/Texture.h>
#include <crepe/manager/ComponentManager.h>
#include <crepe/system/RenderSystem.h>
@@ -25,6 +27,8 @@ class RenderSystemTest : public Test {
public:
ComponentManager mgr{m};
+ SDLContext ctx{m};
+ ResourceManager resource_manager{m};
RenderSystem sys{m};
GameObject entity1 = this->mgr.new_object("name");
GameObject entity2 = this->mgr.new_object("name");
@@ -32,10 +36,10 @@ public:
GameObject entity4 = this->mgr.new_object("name");
void SetUp() override {
- auto s1 = Texture("asset/texture/img.png");
- auto s2 = Texture("asset/texture/img.png");
- auto s3 = Texture("asset/texture/img.png");
- auto s4 = Texture("asset/texture/img.png");
+ auto s1 = Asset("asset/texture/img.png");
+ auto s2 = Asset("asset/texture/img.png");
+ auto s3 = Asset("asset/texture/img.png");
+ auto s4 = Asset("asset/texture/img.png");
auto & sprite1
= entity1.add_component<Sprite>(s1, Sprite::Data{
.color = Color(0, 0, 0, 0),
@@ -45,7 +49,6 @@ public:
.size = {10, 10},
});
- ASSERT_NE(sprite1.texture.texture.get(), nullptr);
EXPECT_EQ(sprite1.data.order_in_layer, 5);
EXPECT_EQ(sprite1.data.sorting_in_layer, 5);
auto & sprite2
@@ -55,7 +58,6 @@ public:
.sorting_in_layer = 2,
.order_in_layer = 1,
});
- ASSERT_NE(sprite2.texture.texture.get(), nullptr);
EXPECT_EQ(sprite2.data.sorting_in_layer, 2);
EXPECT_EQ(sprite2.data.order_in_layer, 1);
@@ -66,7 +68,6 @@ public:
.sorting_in_layer = 1,
.order_in_layer = 2,
});
- ASSERT_NE(sprite3.texture.texture.get(), nullptr);
EXPECT_EQ(sprite3.data.sorting_in_layer, 1);
EXPECT_EQ(sprite3.data.order_in_layer, 2);
@@ -77,27 +78,12 @@ public:
.sorting_in_layer = 1,
.order_in_layer = 1,
});
- ASSERT_NE(sprite4.texture.texture.get(), nullptr);
EXPECT_EQ(sprite4.data.sorting_in_layer, 1);
EXPECT_EQ(sprite4.data.order_in_layer, 1);
}
};
-TEST_F(RenderSystemTest, expected_throws) {
- GameObject entity1 = this->mgr.new_object("NAME");
-
- // no texture img
- EXPECT_ANY_THROW({
- auto test = Texture("");
- auto & sprite1 = entity1.add_component<Sprite>(
- test, Sprite::Data{
- .color = Color(0, 0, 0, 0),
- .flip = Sprite::FlipSettings{false, false},
- .sorting_in_layer = 1,
- .order_in_layer = 1,
- });
- });
-
+TEST_F(RenderSystemTest, NoCamera) {
// No camera
EXPECT_ANY_THROW({ this->sys.update(); });
}
@@ -185,7 +171,7 @@ TEST_F(RenderSystemTest, Color) {
Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f});
auto & sprite = this->mgr.get_components_by_id<Sprite>(entity1.id).front().get();
- ASSERT_NE(sprite.texture.texture.get(), nullptr);
+ //ASSERT_NE(sprite.texture.texture.get(), nullptr);
sprite.data.color = Color::GREEN;
EXPECT_EQ(sprite.data.color.r, Color::GREEN.r);
diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp
index 44a5921..965eeab 100644
--- a/src/test/ResourceManagerTest.cpp
+++ b/src/test/ResourceManagerTest.cpp
@@ -1,3 +1,4 @@
+#include "manager/Mediator.h"
#include <gtest/gtest.h>
#define private public
@@ -30,7 +31,9 @@ public:
public:
const unsigned instance;
- TestResource(const Asset & src) : Resource(src), instance(this->instances++) {}
+ TestResource(const Asset & src, Mediator & mediator)
+ : Resource(src, mediator),
+ instance(this->instances++) {}
~TestResource() { this->instances--; }
bool operator==(const TestResource & other) const {
return this->instance == other.instance;
diff --git a/src/test/SaveManagerTest.cpp b/src/test/SaveManagerTest.cpp
new file mode 100644
index 0000000..0209f6a
--- /dev/null
+++ b/src/test/SaveManagerTest.cpp
@@ -0,0 +1,40 @@
+#include <gtest/gtest.h>
+
+#include <crepe/ValueBroker.h>
+#include <crepe/facade/DB.h>
+#include <crepe/manager/SaveManager.h>
+
+using namespace std;
+using namespace crepe;
+using namespace testing;
+
+class SaveManagerTest : public Test {
+ Mediator m;
+ class TestSaveManager : public SaveManager {
+ using SaveManager::SaveManager;
+
+ // in-memory database for testing
+ DB db{};
+ virtual DB & get_db() override { return this->db; }
+ };
+
+public:
+ TestSaveManager mgr{m};
+};
+
+TEST_F(SaveManagerTest, ReadWrite) {
+ ASSERT_FALSE(mgr.has("foo"));
+ mgr.set<string>("foo", "bar");
+ ASSERT_TRUE(mgr.has("foo"));
+
+ string value = mgr.get<string>("foo");
+ EXPECT_EQ(value, "bar");
+}
+
+TEST_F(SaveManagerTest, DefaultValue) {
+ ValueBroker value = mgr.get<int>("foo", 3);
+
+ ASSERT_EQ(value.get(), 3);
+ value.set(5);
+ ASSERT_EQ(value.get(), 5);
+}
diff --git a/src/test/ScriptSaveManagerTest.cpp b/src/test/ScriptSaveManagerTest.cpp
new file mode 100644
index 0000000..64403c4
--- /dev/null
+++ b/src/test/ScriptSaveManagerTest.cpp
@@ -0,0 +1,35 @@
+#include <gtest/gtest.h>
+
+// stupid hack to allow access to private/protected members under test
+#define private public
+#define protected public
+
+#include <crepe/facade/DB.h>
+#include <crepe/manager/SaveManager.h>
+
+#include "ScriptTest.h"
+
+using namespace std;
+using namespace crepe;
+using namespace testing;
+
+class ScriptSaveManagerTest : public ScriptTest {
+public:
+ class TestSaveManager : public SaveManager {
+ using SaveManager::SaveManager;
+
+ // in-memory database for testing
+ DB db{};
+ virtual DB & get_db() override { return this->db; }
+ };
+
+ TestSaveManager save_mgr{mediator};
+};
+
+TEST_F(ScriptSaveManagerTest, GetSaveManager) {
+ MyScript & script = this->script;
+
+ SaveManager & mgr = script.get_save_manager();
+
+ EXPECT_EQ(&mgr, &save_mgr);
+}
diff --git a/src/test/Vector2Test.cpp b/src/test/Vector2Test.cpp
index 17bca41..1e21af9 100644
--- a/src/test/Vector2Test.cpp
+++ b/src/test/Vector2Test.cpp
@@ -382,3 +382,151 @@ TEST_F(Vector2Test, NotEquals) {
EXPECT_FALSE(long_vec1 != long_vec1);
EXPECT_TRUE(long_vec1 != long_vec2);
}
+
+TEST_F(Vector2Test, Truncate) {
+ Vector2<int> vec = {3, 4};
+ vec.truncate(3);
+ EXPECT_EQ(vec.x, 0);
+ EXPECT_EQ(vec.y, 0);
+
+ Vector2<double> vec2 = {3.0, 4.0};
+ vec2.truncate(3.0);
+ EXPECT_FLOAT_EQ(vec2.x, 1.8);
+ EXPECT_FLOAT_EQ(vec2.y, 2.4);
+
+ Vector2<long> vec3 = {3, 4};
+ vec3.truncate(3);
+ EXPECT_EQ(vec3.x, 0);
+ EXPECT_EQ(vec3.y, 0);
+
+ Vector2<float> vec4 = {3.0f, 4.0f};
+ vec4.truncate(3.0f);
+ EXPECT_FLOAT_EQ(vec4.x, 1.8f);
+ EXPECT_FLOAT_EQ(vec4.y, 2.4f);
+}
+
+TEST_F(Vector2Test, Normalize) {
+ Vector2<int> vec = {3, 4};
+ vec.normalize();
+ EXPECT_EQ(vec.x, 0);
+ EXPECT_EQ(vec.y, 0);
+
+ Vector2<double> vec2 = {3.0, 4.0};
+ vec2.normalize();
+ EXPECT_FLOAT_EQ(vec2.x, 0.6);
+ EXPECT_FLOAT_EQ(vec2.y, 0.8);
+
+ Vector2<long> vec3 = {3, 4};
+ vec3.normalize();
+ EXPECT_EQ(vec3.x, 0);
+ EXPECT_EQ(vec3.y, 0);
+
+ Vector2<float> vec4 = {3.0f, 4.0f};
+ vec4.normalize();
+ EXPECT_FLOAT_EQ(vec4.x, 0.6f);
+ EXPECT_FLOAT_EQ(vec4.y, 0.8f);
+}
+
+TEST_F(Vector2Test, Length) {
+ Vector2<int> vec = {3, 4};
+ EXPECT_EQ(vec.length(), 5);
+
+ Vector2<double> vec2 = {3.0, 4.0};
+ EXPECT_FLOAT_EQ(vec2.length(), 5.0);
+
+ Vector2<long> vec3 = {3, 4};
+ EXPECT_EQ(vec3.length(), 5);
+
+ Vector2<float> vec4 = {3.0f, 4.0f};
+ EXPECT_FLOAT_EQ(vec4.length(), 5.0f);
+}
+
+TEST_F(Vector2Test, LengthSquared) {
+ Vector2<int> vec = {3, 4};
+ EXPECT_EQ(vec.length_squared(), 25);
+
+ Vector2<double> vec2 = {3.0, 4.0};
+ EXPECT_FLOAT_EQ(vec2.length_squared(), 25.0);
+
+ Vector2<long> vec3 = {3, 4};
+ EXPECT_EQ(vec3.length_squared(), 25);
+
+ Vector2<float> vec4 = {3.0f, 4.0f};
+ EXPECT_FLOAT_EQ(vec4.length_squared(), 25.0f);
+}
+
+TEST_F(Vector2Test, Dot) {
+ Vector2<int> vec1 = {3, 4};
+ Vector2<int> vec2 = {5, 6};
+ EXPECT_EQ(vec1.dot(vec2), 39);
+
+ Vector2<double> vec3 = {3.0, 4.0};
+ Vector2<double> vec4 = {5.0, 6.0};
+ EXPECT_FLOAT_EQ(vec3.dot(vec4), 39.0);
+
+ Vector2<long> vec5 = {3, 4};
+ Vector2<long> vec6 = {5, 6};
+ EXPECT_EQ(vec5.dot(vec6), 39);
+
+ Vector2<float> vec7 = {3.0f, 4.0f};
+ Vector2<float> vec8 = {5.0f, 6.0f};
+ EXPECT_FLOAT_EQ(vec7.dot(vec8), 39.0f);
+}
+
+TEST_F(Vector2Test, Distance) {
+ Vector2<int> vec1 = {1, 1};
+ Vector2<int> vec2 = {4, 5};
+ EXPECT_EQ(vec1.distance(vec2), 5);
+
+ Vector2<double> vec3 = {1.0, 1.0};
+ Vector2<double> vec4 = {4.0, 5.0};
+ EXPECT_FLOAT_EQ(vec3.distance(vec4), 5.0);
+
+ Vector2<long> vec5 = {1, 1};
+ Vector2<long> vec6 = {4, 5};
+ EXPECT_EQ(vec5.distance(vec6), 5);
+
+ Vector2<float> vec7 = {1.0f, 1.0f};
+ Vector2<float> vec8 = {4.0f, 5.0f};
+ EXPECT_FLOAT_EQ(vec7.distance(vec8), 5.0f);
+}
+
+TEST_F(Vector2Test, DistanceSquared) {
+ Vector2<int> vec1 = {3, 4};
+ Vector2<int> vec2 = {5, 6};
+ EXPECT_EQ(vec1.distance_squared(vec2), 8);
+
+ Vector2<double> vec3 = {3.0, 4.0};
+ Vector2<double> vec4 = {5.0, 6.0};
+ EXPECT_FLOAT_EQ(vec3.distance_squared(vec4), 8.0);
+
+ Vector2<long> vec5 = {3, 4};
+ Vector2<long> vec6 = {5, 6};
+ EXPECT_EQ(vec5.distance_squared(vec6), 8);
+
+ Vector2<float> vec7 = {3.0f, 4.0f};
+ Vector2<float> vec8 = {5.0f, 6.0f};
+ EXPECT_FLOAT_EQ(vec7.distance_squared(vec8), 8.0f);
+}
+
+TEST_F(Vector2Test, Perpendicular) {
+ Vector2<int> vec = {3, 4};
+ Vector2<int> result = vec.perpendicular();
+ EXPECT_EQ(result.x, -4);
+ EXPECT_EQ(result.y, 3);
+
+ Vector2<double> vec2 = {3.0, 4.0};
+ Vector2<double> result2 = vec2.perpendicular();
+ EXPECT_FLOAT_EQ(result2.x, -4.0);
+ EXPECT_FLOAT_EQ(result2.y, 3.0);
+
+ Vector2<long> vec3 = {3, 4};
+ Vector2<long> result3 = vec3.perpendicular();
+ EXPECT_EQ(result3.x, -4);
+ EXPECT_EQ(result3.y, 3);
+
+ Vector2<float> vec4 = {3.0f, 4.0f};
+ Vector2<float> result4 = vec4.perpendicular();
+ EXPECT_FLOAT_EQ(result4.x, -4.0f);
+ EXPECT_FLOAT_EQ(result4.y, 3.0f);
+}