aboutsummaryrefslogtreecommitdiff
path: root/src/crepe
diff options
context:
space:
mode:
authorWBoerenkamps <wrj.boerenkamps@student.avans.nl>2024-11-24 22:12:31 +0100
committerWBoerenkamps <wrj.boerenkamps@student.avans.nl>2024-11-24 22:12:31 +0100
commit24e3f0ea7c5ce90af1276dca49644b5e1ac799f6 (patch)
tree465dd020c1700511a1697267268b3d975caefd48 /src/crepe
parent6287d4e9068d8bd27a9e62643f54adb69e84befd (diff)
parentbe5ccbe24086d5d4fb407f268c649dcbc36eda6b (diff)
merge
Diffstat (limited to 'src/crepe')
-rw-r--r--src/crepe/Asset.cpp16
-rw-r--r--src/crepe/Asset.h41
-rw-r--r--src/crepe/CMakeLists.txt2
-rw-r--r--src/crepe/Component.h5
-rw-r--r--src/crepe/ComponentManager.cpp3
-rw-r--r--src/crepe/ComponentManager.h9
-rw-r--r--src/crepe/ComponentManager.hpp9
-rw-r--r--src/crepe/Particle.cpp2
-rw-r--r--src/crepe/Particle.h11
-rw-r--r--src/crepe/api/Asset.cpp54
-rw-r--r--src/crepe/api/Asset.h84
-rw-r--r--src/crepe/api/BehaviorScript.h7
-rw-r--r--src/crepe/api/BehaviorScript.hpp12
-rw-r--r--src/crepe/api/CMakeLists.txt6
-rw-r--r--src/crepe/api/Color.cpp37
-rw-r--r--src/crepe/api/Color.h49
-rw-r--r--src/crepe/api/Config.h27
-rw-r--r--src/crepe/api/Event.h29
-rw-r--r--src/crepe/api/EventHandler.cpp3
-rw-r--r--src/crepe/api/EventHandler.h27
-rw-r--r--src/crepe/api/EventHandler.hpp12
-rw-r--r--src/crepe/api/EventManager.cpp78
-rw-r--r--src/crepe/api/EventManager.h186
-rw-r--r--src/crepe/api/EventManager.hpp112
-rw-r--r--src/crepe/api/GameObject.cpp8
-rw-r--r--src/crepe/api/GameObject.h3
-rw-r--r--src/crepe/api/IKeyListener.cpp67
-rw-r--r--src/crepe/api/IKeyListener.h44
-rw-r--r--src/crepe/api/IMouseListener.cpp77
-rw-r--r--src/crepe/api/IMouseListener.h54
-rw-r--r--src/crepe/api/KeyCodes.h263
-rw-r--r--src/crepe/api/LoopManager.cpp2
-rw-r--r--src/crepe/api/LoopManager.h27
-rw-r--r--src/crepe/api/LoopManager.hpp5
-rw-r--r--src/crepe/api/LoopTimer.cpp2
-rw-r--r--src/crepe/api/LoopTimer.h4
-rw-r--r--src/crepe/api/ParticleEmitter.h8
-rw-r--r--src/crepe/api/Rigidbody.cpp2
-rw-r--r--src/crepe/api/Rigidbody.h10
-rw-r--r--src/crepe/api/Scene.cpp7
-rw-r--r--src/crepe/api/Scene.h34
-rw-r--r--src/crepe/api/SceneManager.cpp2
-rw-r--r--src/crepe/api/SceneManager.h16
-rw-r--r--src/crepe/api/SceneManager.hpp14
-rw-r--r--src/crepe/api/Script.cpp15
-rw-r--r--src/crepe/api/Script.h131
-rw-r--r--src/crepe/api/Script.hpp35
-rw-r--r--src/crepe/api/Sprite.h3
-rw-r--r--src/crepe/api/Texture.cpp15
-rw-r--r--src/crepe/api/Texture.h12
-rw-r--r--src/crepe/api/Transform.cpp2
-rw-r--r--src/crepe/api/Transform.h7
-rw-r--r--src/crepe/api/Vector2.cpp33
-rw-r--r--src/crepe/api/Vector2.h55
-rw-r--r--src/crepe/api/Vector2.hpp118
-rw-r--r--src/crepe/facade/DB.cpp4
-rw-r--r--src/crepe/facade/DB.h4
-rw-r--r--src/crepe/facade/SDLContext.cpp305
-rw-r--r--src/crepe/facade/SDLContext.h39
-rw-r--r--src/crepe/facade/Sound.cpp2
-rw-r--r--src/crepe/facade/Sound.h2
-rw-r--r--src/crepe/system/AnimatorSystem.cpp5
-rw-r--r--src/crepe/system/ParticleSystem.cpp22
-rw-r--r--src/crepe/system/PhysicsSystem.cpp8
-rw-r--r--src/crepe/system/RenderSystem.cpp88
-rw-r--r--src/crepe/system/RenderSystem.h52
-rw-r--r--src/crepe/system/ScriptSystem.cpp34
-rw-r--r--src/crepe/system/ScriptSystem.h11
-rw-r--r--src/crepe/types.h23
-rw-r--r--src/crepe/util/CMakeLists.txt2
-rw-r--r--src/crepe/util/Log.h10
-rw-r--r--src/crepe/util/OptionalRef.h58
-rw-r--r--src/crepe/util/OptionalRef.hpp37
-rw-r--r--src/crepe/util/Proxy.h2
-rw-r--r--src/crepe/util/Proxy.hpp6
75 files changed, 1361 insertions, 1249 deletions
diff --git a/src/crepe/Asset.cpp b/src/crepe/Asset.cpp
deleted file mode 100644
index 9c41ecb..0000000
--- a/src/crepe/Asset.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <filesystem>
-
-#include "Asset.h"
-
-using namespace crepe;
-using namespace std;
-
-// FIXME: restore this
-// src(std::filesystem::canonical(src))
-Asset::Asset(const std::string & src) : src(src) {
- this->file = std::ifstream(this->src, std::ios::in | std::ios::binary);
-}
-
-istream & Asset::get_stream() { return this->file; }
-
-const string & Asset::get_canonical() const { return this->src; }
diff --git a/src/crepe/Asset.h b/src/crepe/Asset.h
deleted file mode 100644
index 9051c5e..0000000
--- a/src/crepe/Asset.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include <fstream>
-#include <iostream>
-#include <string>
-
-namespace crepe {
-
-/**
- * \brief Asset location helper
- *
- * This class is used to locate and canonicalize paths to game asset files, and should *always*
- * be used when retrieving files from disk.
- */
-class Asset {
-public:
- /**
- * \param src Unique identifier to asset
- */
- Asset(const std::string & src);
-
-public:
- /**
- * \brief Get an input stream to the contents of this asset
- * \return Input stream with file contents
- */
- std::istream & get_stream();
- /**
- * \brief Get the canonical path to this asset
- * \return Canonical path to this asset
- */
- const std::string & get_canonical() const;
-
-private:
- //! Canonical path to asset
- const std::string src;
- //! File handle (stream)
- std::ifstream file;
-};
-
-} // namespace crepe
diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt
index 3b05742..7e176e7 100644
--- a/src/crepe/CMakeLists.txt
+++ b/src/crepe/CMakeLists.txt
@@ -1,5 +1,4 @@
target_sources(crepe PUBLIC
- Asset.cpp
Particle.cpp
ComponentManager.cpp
Component.cpp
@@ -7,7 +6,6 @@ target_sources(crepe PUBLIC
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
- Asset.h
ComponentManager.h
ComponentManager.hpp
Component.h
diff --git a/src/crepe/Component.h b/src/crepe/Component.h
index 5279fb3..dc17721 100644
--- a/src/crepe/Component.h
+++ b/src/crepe/Component.h
@@ -27,6 +27,11 @@ protected:
//! Only the ComponentManager can create components
friend class ComponentManager;
+ Component(const Component &) = delete;
+ Component(Component &&) = delete;
+ virtual Component & operator=(const Component &) = delete;
+ virtual Component & operator=(Component &&) = delete;
+
public:
virtual ~Component() = default;
diff --git a/src/crepe/ComponentManager.cpp b/src/crepe/ComponentManager.cpp
index e310577..e4de027 100644
--- a/src/crepe/ComponentManager.cpp
+++ b/src/crepe/ComponentManager.cpp
@@ -26,8 +26,7 @@ void ComponentManager::delete_all_components() {
}
GameObject ComponentManager::new_object(const string & name, const string & tag,
- const Vector2 & position, double rotation,
- double scale) {
+ const vec2 & position, double rotation, double scale) {
GameObject object{*this, this->next_id, name, tag, position, rotation, scale};
this->next_id++;
return object;
diff --git a/src/crepe/ComponentManager.h b/src/crepe/ComponentManager.h
index 2107453..1cb0b5f 100644
--- a/src/crepe/ComponentManager.h
+++ b/src/crepe/ComponentManager.h
@@ -5,9 +5,8 @@
#include <unordered_map>
#include <vector>
-#include "api/Vector2.h"
-
#include "Component.h"
+#include "types.h"
namespace crepe {
@@ -44,7 +43,7 @@ public:
* \note This method automatically assigns a new entity ID
*/
GameObject new_object(const std::string & name, const std::string & tag = "",
- const Vector2 & position = {0, 0}, double rotation = 0,
+ const vec2 & position = {0, 0}, double rotation = 0,
double scale = 1);
protected:
@@ -112,7 +111,7 @@ public:
* \return A vector of all components of the specific type and id
*/
template <typename T>
- std::vector<std::reference_wrapper<T>> get_components_by_id(game_object_id_t id) const;
+ RefVector<T> get_components_by_id(game_object_id_t id) const;
/**
* \brief Get all components of a specific type
*
@@ -122,7 +121,7 @@ public:
* \return A vector of all components of the specific type
*/
template <typename T>
- std::vector<std::reference_wrapper<T>> get_components_by_type() const;
+ RefVector<T> get_components_by_type() const;
private:
/**
diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/ComponentManager.hpp
index be99cac..4d5eaf4 100644
--- a/src/crepe/ComponentManager.hpp
+++ b/src/crepe/ComponentManager.hpp
@@ -81,15 +81,14 @@ void ComponentManager::delete_components() {
}
template <typename T>
-std::vector<std::reference_wrapper<T>>
-ComponentManager::get_components_by_id(game_object_id_t id) const {
+RefVector<T> ComponentManager::get_components_by_id(game_object_id_t id) const {
using namespace std;
// Determine the type of T (this is used as the key of the unordered_map<>)
type_index type = typeid(T);
// Create an empty vector<>
- vector<reference_wrapper<T>> component_vector;
+ RefVector<T> component_vector;
if (this->components.find(type) == this->components.end()) return component_vector;
@@ -114,14 +113,14 @@ ComponentManager::get_components_by_id(game_object_id_t id) const {
}
template <typename T>
-std::vector<std::reference_wrapper<T>> ComponentManager::get_components_by_type() const {
+RefVector<T> ComponentManager::get_components_by_type() const {
using namespace std;
// Determine the type of T (this is used as the key of the unordered_map<>)
type_index type = typeid(T);
// Create an empty vector<>
- vector<reference_wrapper<T>> component_vector;
+ RefVector<T> component_vector;
// Find the type (in the unordered_map<>)
if (this->components.find(type) == this->components.end()) return component_vector;
diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp
index 1068cbf..485a0d4 100644
--- a/src/crepe/Particle.cpp
+++ b/src/crepe/Particle.cpp
@@ -2,7 +2,7 @@
using namespace crepe;
-void Particle::reset(uint32_t lifespan, const Vector2 & position, const Vector2 & velocity,
+void Particle::reset(uint32_t lifespan, const vec2 & position, const vec2 & velocity,
double angle) {
// Initialize the particle state
this->time_in_life = 0;
diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h
index 19859fe..d0397c9 100644
--- a/src/crepe/Particle.h
+++ b/src/crepe/Particle.h
@@ -2,7 +2,7 @@
#include <cstdint>
-#include "api/Vector2.h"
+#include "types.h"
namespace crepe {
@@ -18,11 +18,11 @@ class Particle {
public:
//! Position of the particle in 2D space.
- Vector2 position;
+ vec2 position;
//! Velocity vector indicating the speed and direction of the particle.
- Vector2 velocity;
+ vec2 velocity;
//! Accumulated force affecting the particle over time.
- Vector2 force_over_time;
+ vec2 force_over_time;
//! Total lifespan of the particle in milliseconds.
uint32_t lifespan;
//! Active state of the particle; true if it is in use, false otherwise.
@@ -43,8 +43,7 @@ public:
* \param velocity The initial velocity of the particle.
* \param angle The angle of the particle's trajectory or orientation.
*/
- void reset(uint32_t lifespan, const Vector2 & position, const Vector2 & velocity,
- double angle);
+ void reset(uint32_t lifespan, const vec2 & position, const vec2 & velocity, double angle);
/**
* \brief Updates the particle's state.
*
diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp
new file mode 100644
index 0000000..e148367
--- /dev/null
+++ b/src/crepe/api/Asset.cpp
@@ -0,0 +1,54 @@
+#include <filesystem>
+#include <stdexcept>
+#include <whereami.h>
+
+#include "api/Config.h"
+
+#include "Asset.h"
+
+using namespace crepe;
+using namespace std;
+
+Asset::Asset(const string & src) : src(find_asset(src)) {}
+Asset::Asset(const char * src) : src(find_asset(src)) {}
+
+const string & Asset::get_path() const noexcept { return this->src; }
+
+string Asset::find_asset(const string & src) const {
+ auto & cfg = Config::get_instance();
+ string & root_pattern = cfg.asset.root_pattern;
+
+ // if root_pattern is empty, find_asset must return all paths as-is
+ if (root_pattern.empty()) return src;
+
+ // absolute paths do not need to be resolved, only canonicalized
+ filesystem::path path = src;
+ if (path.is_absolute()) return filesystem::canonical(path);
+
+ // find directory matching root_pattern
+ filesystem::path root = this->whereami();
+ while (1) {
+ if (filesystem::exists(root / root_pattern)) break;
+ if (!root.has_parent_path())
+ throw runtime_error(format("Asset: Cannot find root pattern ({})", root_pattern));
+ root = root.parent_path();
+ }
+
+ // join path to root (base directory) and canonicalize
+ return filesystem::canonical(root / path);
+}
+
+string Asset::whereami() const noexcept {
+ string path;
+ size_t path_length = wai_getExecutablePath(NULL, 0, NULL);
+ path.resize(path_length + 1); // wai writes null byte
+ wai_getExecutablePath(path.data(), path_length, NULL);
+ path.resize(path_length);
+ return path;
+}
+
+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());
+};
diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h
new file mode 100644
index 0000000..bfd0ac7
--- /dev/null
+++ b/src/crepe/api/Asset.h
@@ -0,0 +1,84 @@
+#pragma once
+
+#include <string>
+
+namespace crepe {
+
+/**
+ * \brief Asset location helper
+ *
+ * This class is used to locate game asset files, and should *always* be used
+ * instead of reading file paths directly.
+ */
+class Asset {
+public:
+ /**
+ * \param src Unique identifier to asset
+ */
+ Asset(const std::string & src);
+ /**
+ * \param src Unique identifier to asset
+ */
+ Asset(const char * src);
+
+public:
+ /**
+ * \brief Get the path to this asset
+ * \return path to this asset
+ */
+ const std::string & get_path() const noexcept;
+
+ /**
+ * \brief Comparison operator
+ * \param other Possibly different instance of \c Asset to test equality against
+ * \return True if \c this and \c other are equal
+ */
+ bool operator==(const Asset & other) const noexcept;
+
+private:
+ //! path to asset
+ const std::string src;
+
+private:
+ /**
+ * \brief Locate asset path, or throw exception if it cannot be found
+ *
+ * This function resolves asset locations relative to crepe::Config::root_pattern if it is
+ * set and \p src is a relative path. If \p src is an absolute path, it is canonicalized.
+ * This function only returns if the file can be found.
+ *
+ * \param src Arbitrary path to resource file
+ *
+ * \returns \p src if crepe::Config::root_pattern is empty
+ * \returns Canonical path to \p src
+ *
+ * \throws std::runtime_error if root_pattern cannot be found
+ * \throws std::filesystem::filesystem_error if the resolved path does not exist
+ * \throws std::filesystem::filesystem_error if the path cannot be canonicalized
+ */
+ std::string find_asset(const std::string & src) const;
+ /**
+ * \returns The path to the current executable
+ */
+ std::string whereami() const noexcept;
+};
+
+} // namespace crepe
+
+namespace std {
+
+//! Hash helper struct
+template <>
+struct hash<const crepe::Asset> {
+ /**
+ * \brief Hash operator for crepe::Asset
+ *
+ * This function hashes a crepe::Asset instance, allowing it to be used as a key in an \c
+ * std::unordered_map.
+ *
+ * \returns Hash value
+ */
+ size_t operator()(const crepe::Asset & asset) const noexcept;
+};
+
+} // namespace std
diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h
index 9d85d4c..d556fe5 100644
--- a/src/crepe/api/BehaviorScript.h
+++ b/src/crepe/api/BehaviorScript.h
@@ -39,11 +39,14 @@ public:
* \brief Set the concrete script of this component
*
* \tparam T Concrete script type (derived from \c crepe::Script)
+ * \tparam Args Arguments for concrete script constructor
+ *
+ * \param args Arguments for concrete script constructor (forwarded using perfect forwarding)
*
* \returns Reference to BehaviorScript component (`*this`)
*/
- template <class T>
- BehaviorScript & set_script();
+ template <class T, typename... Args>
+ BehaviorScript & set_script(Args &&... args);
protected:
//! Script instance
diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp
index d80321d..bd59337 100644
--- a/src/crepe/api/BehaviorScript.hpp
+++ b/src/crepe/api/BehaviorScript.hpp
@@ -9,13 +9,17 @@
namespace crepe {
-template <class T>
-BehaviorScript & BehaviorScript::set_script() {
+template <class T, typename... Args>
+BehaviorScript & BehaviorScript::set_script(Args &&... args) {
dbg_trace();
static_assert(std::is_base_of<Script, T>::value);
- Script * s = new T();
+ Script * s = new T(std::forward<Args>(args)...);
+
s->game_object_id = this->game_object_id;
- s->component_manager_ref = &this->component_manager;
+ s->active = this->active;
+ s->component_manager = this->component_manager;
+ s->event_manager = EventManager::get_instance();
+
this->script = std::unique_ptr<Script>(s);
return *this;
}
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index a185ca6..50c51ed 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -12,9 +12,7 @@ target_sources(crepe PUBLIC
SaveManager.cpp
Config.cpp
Metadata.cpp
- Scene.cpp
SceneManager.cpp
- Vector2.cpp
Camera.cpp
Animator.cpp
EventManager.cpp
@@ -22,7 +20,9 @@ target_sources(crepe PUBLIC
IMouseListener.cpp
LoopManager.cpp
LoopTimer.cpp
+ Asset.cpp
EventHandler.cpp
+ Script.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -36,6 +36,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
Rigidbody.h
Sprite.h
Vector2.h
+ Vector2.hpp
Color.h
Texture.h
AssetManager.h
@@ -56,4 +57,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
IMouseListener.h
LoopManager.h
LoopTimer.h
+ Asset.h
)
diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp
index 9e5f187..29bd77a 100644
--- a/src/crepe/api/Color.cpp
+++ b/src/crepe/api/Color.cpp
@@ -2,32 +2,11 @@
using namespace crepe;
-Color Color::white = Color(255, 255, 255, 0);
-Color Color::red = Color(255, 0, 0, 0);
-Color Color::green = Color(0, 255, 0, 0);
-Color Color::blue = Color(0, 0, 255, 0);
-Color Color::black = Color(0, 0, 0, 0);
-Color Color::cyan = Color(0, 255, 255, 0);
-Color Color::yellow = Color(255, 255, 0, 0);
-Color Color::magenta = Color(255, 0, 255, 0);
-
-Color::Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) {
- this->a = alpha;
- this->r = red;
- this->g = green;
- this->b = blue;
-};
-
-const Color & Color::get_white() { return Color::white; };
-
-const Color & Color::get_red() { return Color::red; };
-const Color & Color::get_green() { return Color::green; };
-const Color & Color::get_blue() { return Color::blue; };
-
-const Color & Color::get_black() { return Color::black; };
-
-const Color & Color::get_cyan() { return Color::cyan; };
-
-const Color & Color::get_yellow() { return Color::yellow; };
-
-const Color & Color::get_magenta() { return Color::magenta; };
+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};
diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h
index aa47bf4..84edb5c 100644
--- a/src/crepe/api/Color.h
+++ b/src/crepe/api/Color.h
@@ -4,41 +4,20 @@
namespace crepe {
-// TODO: make Color a struct w/o constructors/destructors
-class Color {
-
- // FIXME: can't these colors be defined as a `static constexpr const Color`
- // instead?
-
-public:
- Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha);
- static const Color & get_white();
- static const Color & get_red();
- static const Color & get_green();
- static const Color & get_blue();
- static const Color & get_cyan();
- static const Color & get_magenta();
- static const Color & get_yellow();
- static const Color & get_black();
-
-private:
- // TODO: why are these private!?
- uint8_t r;
- uint8_t g;
- uint8_t b;
- uint8_t a;
-
- static Color white;
- static Color red;
- static Color green;
- static Color blue;
- static Color cyan;
- static Color magenta;
- static Color yellow;
- static Color black;
-
-private:
- friend class SDLContext;
+struct Color {
+ uint8_t r = 0x00;
+ uint8_t g = 0x00;
+ uint8_t b = 0x00;
+ uint8_t a = 0xff;
+
+ static const Color WHITE;
+ static const Color RED;
+ static const Color GREEN;
+ static const Color BLUE;
+ static const Color CYAN;
+ static const Color MAGENTA;
+ static const Color YELLOW;
+ static const Color BLACK;
};
} // namespace crepe
diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h
index 3ab877a..0c9d116 100644
--- a/src/crepe/api/Config.h
+++ b/src/crepe/api/Config.h
@@ -11,19 +11,18 @@ namespace crepe {
* modified *before* execution is handed over from the game programmer to the engine (i.e. the
* main loop is started).
*/
-class Config {
+class Config final {
public:
//! Retrieve handle to global Config instance
static Config & get_instance();
private:
Config() = default;
-
- // singleton
- Config(const Config &) = delete;
- Config(Config &&) = delete;
- Config & operator=(const Config &) = delete;
- Config & operator=(Config &&) = delete;
+ ~Config() = default;
+ Config(const Config &) = default;
+ Config(Config &&) = default;
+ Config & operator=(const Config &) = default;
+ Config & operator=(Config &&) = default;
public:
//! Logging-related settings
@@ -62,6 +61,20 @@ public:
*/
double gravity = 1;
} physics;
+
+ //! Asset loading options
+ struct {
+ /**
+ * \brief Pattern to match for Asset base directory
+ *
+ * All non-absolute paths resolved using \c Asset will be made relative to
+ * the first parent directory relative to the calling executable where
+ * appending this pattern results in a path that exists. If this string is
+ * empty, path resolution is disabled, and Asset will return all paths
+ * as-is.
+ */
+ std::string root_pattern = ".crepe-root";
+ } asset;
};
} // namespace crepe
diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h
index ef6a791..bac8701 100644
--- a/src/crepe/api/Event.h
+++ b/src/crepe/api/Event.h
@@ -1,23 +1,24 @@
#pragma once
+// TODO discussing the location of these events
#include <string>
#include "KeyCodes.h"
+namespace crepe {
+
/**
* \brief Base class for all event types in the system.
*/
-class Event {
-public:
-};
+class Event {};
/**
* \brief Event triggered when a key is pressed.
*/
class KeyPressEvent : public Event {
public:
- //! Number of times the key press is repeated (e.g., for long presses).
- int repeat = 0;
+ //! false if first time press, true if key is repeated
+ bool repeat = false;
//! The key that was pressed.
Keycode key = Keycode::NONE;
@@ -96,11 +97,7 @@ public:
/**
* \brief Event triggered during a collision between objects.
*/
-class CollisionEvent : public Event {
-public:
- //! Data describing the collision (currently not implemented).
- // Collision collisionData;
-};
+class CollisionEvent : public Event {};
/**
* \brief Event triggered when text is submitted, e.g., from a text input.
@@ -118,14 +115,4 @@ class ShutDownEvent : public Event {
public:
};
-class MouseScrollEvent : public Event {
-public:
- //! X-coordinate of the mouse position at the time of the event.
- int scroll_x = 0;
-
- //! Y-coordinate of the mouse position at the time of the event.
- int scroll_y = 0;
-
- int direction = 0;
-};
-
+}
diff --git a/src/crepe/api/EventHandler.cpp b/src/crepe/api/EventHandler.cpp
index 186ec9c..4dc232f 100644
--- a/src/crepe/api/EventHandler.cpp
+++ b/src/crepe/api/EventHandler.cpp
@@ -2,5 +2,4 @@
using namespace crepe;
-// Implementation of IEventHandlerWrapper::exec
-bool IEventHandlerWrapper::exec(const Event & e) { return call(e); }
+bool IEventHandlerWrapper::exec(const Event & e) { return this->call(e); }
diff --git a/src/crepe/api/EventHandler.h b/src/crepe/api/EventHandler.h
index db51d04..ef659fd 100644
--- a/src/crepe/api/EventHandler.h
+++ b/src/crepe/api/EventHandler.h
@@ -13,6 +13,8 @@ namespace crepe {
* indicating whether the event is handled.
*
* \tparam EventType The type of event this handler will handle.
+ *
+ * Returning \c false from an event handler results in the event being propogated to other listeners for the same event type, while returning \c true stops propogation altogether.
*/
template <typename EventType>
using EventHandler = std::function<bool(const EventType & e)>;
@@ -22,7 +24,7 @@ using EventHandler = std::function<bool(const EventType & e)>;
* \brief An abstract base class for event handler wrappers.
*
* This class provides the interface for handling events. Derived classes must implement the
- * `call()` method to process events and the `get_type()` method to return the handler's type.
+ * `call()` method to process events
*/
class IEventHandlerWrapper {
public:
@@ -41,15 +43,6 @@ public:
*/
bool exec(const Event & e);
- /**
- * \brief Get the type of the event handler.
- *
- * This method returns the type of the event handler as a string.
- *
- * \return A string representing the handler's type.
- */
- virtual std::string get_type() const = 0;
-
private:
/**
* \brief The method responsible for handling the event.
@@ -94,20 +87,8 @@ private:
* \return A boolean value indicating whether the event is handled.
*/
bool call(const Event & e) override;
-
- /**
- * \brief Returns the type of the handler.
- *
- * This method returns a string representing the type of the event handler.
- *
- * \return The handler type as a string.
- */
- std::string get_type() const override;
-
//! The event handler function.
- EventHandler<EventType> m_handler;
- //! The type name of the handler function.
- const std::string m_handler_type;
+ EventHandler<EventType> handler;
};
} // namespace crepe
diff --git a/src/crepe/api/EventHandler.hpp b/src/crepe/api/EventHandler.hpp
index 9c47da2..050e57e 100644
--- a/src/crepe/api/EventHandler.hpp
+++ b/src/crepe/api/EventHandler.hpp
@@ -1,3 +1,4 @@
+#pragma once
#include <typeindex>
@@ -8,19 +9,12 @@ namespace crepe {
// Implementation of EventHandlerWrapper constructor
template <typename EventType>
EventHandlerWrapper<EventType>::EventHandlerWrapper(const EventHandler<EventType> & handler)
- : m_handler(handler),
- m_handler_type(m_handler.target_type().name()) {}
+ : handler(handler) {}
// Implementation of EventHandlerWrapper::call
template <typename EventType>
bool EventHandlerWrapper<EventType>::call(const Event & e) {
- return m_handler(static_cast<const EventType &>(e));
-}
-
-// Implementation of EventHandlerWrapper::get_type
-template <typename EventType>
-std::string EventHandlerWrapper<EventType>::get_type() const {
- return m_handler_type;
+ return this->handler(static_cast<const EventType &>(e));
}
} //namespace crepe
diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp
index b465e89..20f0dd3 100644
--- a/src/crepe/api/EventManager.cpp
+++ b/src/crepe/api/EventManager.cpp
@@ -1,6 +1,7 @@
#include "EventManager.h"
using namespace crepe;
+using namespace std;
EventManager & EventManager::get_instance() {
static EventManager instance;
@@ -8,53 +9,38 @@ EventManager & EventManager::get_instance() {
}
void EventManager::dispatch_events() {
- using HandlersMap
- = std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>;
- using HandlersVec = std::vector<std::unique_ptr<IEventHandlerWrapper>>;
-
- for (auto event_it = this->events_queue.begin(); event_it != this->events_queue.end();) {
- std::unique_ptr<Event> & event = std::get<0>(*event_it);
- int channel = std::get<1>(*event_it);
- std::type_index event_type = std::get<2>(*event_it);
-
- bool event_handled = false;
-
- if (channel) {
- auto handlers_it = subscribers_by_event_id.find(event_type);
- if (handlers_it != subscribers_by_event_id.end()) {
- HandlersMap & handlers_map = handlers_it->second;
- auto handlers = handlers_map.find(channel);
- if (handlers != handlers_map.end()) {
- HandlersVec & callbacks = handlers->second;
- for (auto handler_it = callbacks.begin(); handler_it != callbacks.end();
- ++handler_it) {
- if ((*handler_it)->exec(*event)) {
- event_it = events_queue.erase(event_it);
- event_handled = true;
- break;
- }
- }
- }
- }
- } else {
- // Handle event for all channels
- auto handlers_it = this->subscribers.find(event_type);
- if (handlers_it != this->subscribers.end()) {
- HandlersVec & handlers = handlers_it->second;
- for (auto handler_it = handlers.begin(); handler_it != handlers.end();
- ++handler_it) {
- // remove event from queue since and continue when callback returns true
- if ((*handler_it)->exec(*event)) {
- event_it = this->events_queue.erase(event_it);
- event_handled = true;
- break;
- }
- }
- }
- }
+ for (auto & event : this->events_queue) {
+ this->handle_event(event.type, event.channel, *event.event.get());
+ }
+ this->events_queue.clear();
+}
+
+void EventManager::handle_event(type_index type, event_channel_t channel, const Event & data) {
+ auto handlers_it = this->subscribers.find(type);
+ if (handlers_it == this->subscribers.end()) return;
+
+ vector<CallbackEntry> & handlers = handlers_it->second;
+ for (auto & handler : handlers) {
+ bool check_channel = handler.channel != CHANNEL_ALL || channel != CHANNEL_ALL;
+ if (check_channel && handler.channel != channel) continue;
+
+ bool handled = handler.callback->exec(data);
+ if (handled) return;
+ }
+}
+
+void EventManager::clear() {
+ this->subscribers.clear();
+ this->events_queue.clear();
+}
- if (!event_handled) {
- ++event_it;
+void EventManager::unsubscribe(subscription_t id) {
+ for (auto & [event_type, handlers] : this->subscribers) {
+ for (auto it = handlers.begin(); it != handlers.end(); it++) {
+ // find listener with subscription id
+ if ((*it).id != id) continue;
+ it = handlers.erase(it);
+ return;
}
}
}
diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h
index 92273df..1a33023 100644
--- a/src/crepe/api/EventManager.h
+++ b/src/crepe/api/EventManager.h
@@ -1,8 +1,6 @@
#pragma once
-#include <functional>
#include <memory>
-#include <type_traits>
#include <typeindex>
#include <unordered_map>
#include <vector>
@@ -12,98 +10,152 @@
namespace crepe {
+//! Event listener unique ID
+typedef size_t subscription_t;
+
+/**
+ * \brief Event channel
+ *
+ * Events can be sent to a specific channel, which prevents listeners on other channels from
+ * being called. The default channel is EventManager::CHANNEL_ALL, which calls all listeners.
+ */
+typedef size_t event_channel_t;
+
/**
* \class EventManager
- * \brief The EventManager class is responsible for managing the subscription, triggering,
- * and queueing of events. It handles events and dispatches them to appropriate subscribers.
+ * \brief Manages event subscriptions, triggers, and queues, enabling decoupled event handling.
+ *
+ * The `EventManager` acts as a centralized event system. It allows for registering callbacks
+ * for specific event types, triggering events synchronously, queueing events for later
+ * processing, and managing subscriptions via unique identifiers.
*/
class EventManager {
public:
+ static constexpr const event_channel_t CHANNEL_ALL = -1;
+
/**
- * \brief Get the singleton instance of the EventManager.
- *
- * This method returns the unique instance of the EventManager, creating it on the first call.
- *
- * \return Reference to the EventManager instance.
- */
+ * \brief Get the singleton instance of the EventManager.
+ *
+ * This method returns the unique instance of the EventManager, creating it if it
+ * doesn't already exist. Ensures only one instance is active in the program.
+ *
+ * \return Reference to the singleton instance of the EventManager.
+ */
static EventManager & get_instance();
/**
- * \brief Subscribe to an event.
- *
- * This method allows the registration of a callback for a specific event type and channel.
- *
- * \tparam EventType The type of the event to subscribe to.
- * \param callback The callback function to invoke when the event is triggered.
- * \param channel The channel number to subscribe to (default is 0).
- */
+ * \brief Subscribe to a specific event type.
+ *
+ * Registers a callback for a given event type and optional channel. Each callback
+ * is assigned a unique subscription ID that can be used for later unsubscription.
+ *
+ * \tparam EventType The type of the event to subscribe to.
+ * \param callback The callback function to be invoked when the event is triggered.
+ * \param channel The channel number to subscribe to (default is CHANNEL_ALL, which listens to all channels).
+ * \return A unique subscription ID associated with the registered callback.
+ */
template <typename EventType>
- void subscribe(EventHandler<EventType> && callback, int channel = 0);
+ subscription_t subscribe(const EventHandler<EventType> & callback,
+ event_channel_t channel = CHANNEL_ALL);
/**
- * \brief Unsubscribe from an event.
- *
- * This method removes a previously registered callback from an event.
- *
- * \tparam EventType The type of the event to unsubscribe from.
- * \param callback The callback function to remove from the subscription list.
- * \param channel The event ID to unsubscribe from.
- */
- template <typename EventType>
- void unsubscribe(const EventHandler<EventType> &, int channel = 0);
+ * \brief Unsubscribe a previously registered callback.
+ *
+ * Removes a callback from the subscription list based on its unique subscription ID.
+ *
+ * \param event_id The unique subscription ID of the callback to remove.
+ */
+ void unsubscribe(subscription_t event_id);
/**
- * \brief Trigger an event.
- *
- * This method invokes the appropriate callback(s) for the specified event.
- *
- * \tparam EventType The type of the event to trigger.
- * \param event The event data to pass to the callback.
- * \param channel The channel from which to trigger the event (default is 0).
- */
+ * \brief Trigger an event immediately.
+ *
+ * Synchronously invokes all registered callbacks for the given event type on the specified channel.
+ *
+ * \tparam EventType The type of the event to trigger.
+ * \param event The event instance to pass to the callbacks.
+ * \param channel The channel to trigger the event on (default is CHANNEL_ALL, which triggers on all channels).
+ */
template <typename EventType>
- void trigger_event(const EventType & event, int channel = 0);
+ void trigger_event(const EventType & event = {}, event_channel_t channel = CHANNEL_ALL);
/**
- * \brief Queue an event for later processing.
- *
- * This method adds an event to the event queue, which will be processed in the
- * dispatch_events function.
- *
- * \tparam EventType The type of the event to queue.
- * \param event The event to queue.
- * \param channel The channel number for the event (default is 0).
- */
+ * \brief Queue an event for later processing.
+ *
+ * Adds an event to the event queue to be processed during the next call to `dispatch_events`.
+ *
+ * \tparam EventType The type of the event to queue.
+ * \param event The event instance to queue.
+ * \param channel The channel to associate with the event (default is CHANNEL_ALL).
+ */
template <typename EventType>
- void queue_event(EventType && event, int channel = 0);
+ void queue_event(const EventType & event = {}, event_channel_t channel = CHANNEL_ALL);
/**
- * \brief Dispatch all queued events.
- *
- * This method processes all events in the event queue and triggers the corresponding
- * callbacks for each event.
- */
+ * \brief Process all queued events.
+ *
+ * Iterates through the event queue and triggers callbacks for each queued event.
+ * Events are removed from the queue once processed.
+ */
void dispatch_events();
+ /**
+ * \brief Clear all subscriptions.
+ *
+ * Removes all registered event handlers and clears the subscription list.
+ */
+ void clear();
+
private:
/**
- * \brief Default constructor for the EventManager.
- *
- * This constructor is private to enforce the singleton pattern.
- */
+ * \brief Default constructor for the EventManager.
+ *
+ * Constructor is private to enforce the singleton pattern.
+ */
EventManager() = default;
- //! The queue of events to be processed.
- std::vector<std::tuple<std::unique_ptr<Event>, int, std::type_index>> events_queue;
- //! Registered event handlers.
- std::unordered_map<std::type_index, std::vector<std::unique_ptr<IEventHandlerWrapper>>>
- subscribers;
- //! Event handlers indexed by event ID.
- std::unordered_map<
- std::type_index,
- std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>>
- subscribers_by_event_id;
+ /**
+ * \struct QueueEntry
+ * \brief Represents an entry in the event queue.
+ */
+ struct QueueEntry {
+ std::unique_ptr<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.
+ };
+
+ /**
+ * \brief Internal event handler
+ *
+ * This function processes a single event, and is used to process events both during
+ * EventManager::dispatch_events and inside EventManager::trigger_event
+ *
+ * \param type \c typeid of concrete Event class
+ * \param channel Event channel
+ * \param data Event data
+ */
+ void handle_event(std::type_index type, event_channel_t channel, const Event & data);
+
+ /**
+ * \struct CallbackEntry
+ * \brief Represents a registered event handler callback.
+ */
+ struct CallbackEntry {
+ std::unique_ptr<IEventHandlerWrapper> callback; ///< The callback function wrapper.
+ event_channel_t channel = CHANNEL_ALL; ///< The channel this callback listens to.
+ subscription_t id = -1; ///< Unique subscription ID.
+ };
+
+ //! The queue of events to be processed during dispatch.
+ std::vector<QueueEntry> events_queue;
+
+ //! A map of event type to registered callbacks.
+ std::unordered_map<std::type_index, std::vector<CallbackEntry>> subscribers;
+
+ //! Counter to generate unique subscription IDs.
+ subscription_t subscription_counter = 0;
};
} // namespace crepe
+
#include "EventManager.hpp"
diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/api/EventManager.hpp
index b20b88f..a5f4556 100644
--- a/src/crepe/api/EventManager.hpp
+++ b/src/crepe/api/EventManager.hpp
@@ -1,110 +1,36 @@
+#pragma once
+
#include "EventManager.h"
namespace crepe {
template <typename EventType>
-void EventManager::subscribe(EventHandler<EventType> && callback, int channel) {
- using HandlersMap
- = std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>;
- using HandlersVec = std::vector<std::unique_ptr<IEventHandlerWrapper>>;
-
+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);
-
- if (channel) {
- HandlersMap & handlers_map = this->subscribers_by_event_id[event_type];
- auto handlers = handlers_map.find(channel);
- if (handlers != handlers_map.end()) {
- handlers->second.emplace_back(std::move(handler));
- } else {
- handlers_map[channel].emplace_back(std::move(handler));
- }
- } else {
- HandlersVec & handlers = this->subscribers[event_type];
- handlers.emplace_back(std::move(handler));
- }
+ std::vector<CallbackEntry> & handlers = this->subscribers[event_type];
+ handlers.emplace_back(CallbackEntry{
+ .callback = std::move(handler), .channel = channel, .id = subscription_counter});
+ return subscription_counter;
}
template <typename EventType>
-void EventManager::queue_event(EventType && event, int channel) {
- std::type_index event_type = std::type_index(typeid(EventType));
-
- auto event_ptr = std::make_unique<EventType>(std::forward<EventType>(event));
-
- std::tuple<std::unique_ptr<Event>, int, std::type_index> tuple(std::move(event_ptr),
- channel, event_type);
- this->events_queue.push_back(std::move(tuple));
+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");
+ this->events_queue.push_back(QueueEntry{
+ .event = std::make_unique<EventType>(event),
+ .channel = channel,
+ .type = typeid(EventType),
+ });
}
template <typename EventType>
-void EventManager::trigger_event(const EventType & event, int channel) {
- using HandlersMap
- = std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>;
- using HandlersVec = std::vector<std::unique_ptr<IEventHandlerWrapper>>;
-
- std::type_index event_type = std::type_index(typeid(EventType));
-
- if (channel > 0) {
- HandlersMap & handlers_map = this->subscribers_by_event_id[event_type];
- auto handlers_it = handlers_map.find(channel);
-
- if (handlers_it != handlers_map.end()) {
- HandlersVec & handlers = handlers_it->second;
- for (auto it = handlers.begin(); it != handlers.end(); ++it) {
- // stops when callback returns true
- if ((*it)->exec(event)) {
- break;
- }
- }
- }
- } else {
- HandlersVec & handlers = this->subscribers[event_type];
- for (auto it = handlers.begin(); it != handlers.end(); ++it) {
- // stops when callback returns true
- if ((*it)->exec(event)) {
- break;
- }
- }
- }
-}
-
-template <typename EventType>
-void EventManager::unsubscribe(const EventHandler<EventType> & callback, int channel) {
- using HandlersMap
- = std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>;
- using HandlersVec = std::vector<std::unique_ptr<IEventHandlerWrapper>>;
-
- std::type_index event_type(typeid(EventType));
- std::string handler_name = callback.target_type().name();
-
- if (channel) {
- auto subscriber_list = this->subscribers_by_event_id.find(event_type);
- if (subscriber_list != this->subscribers_by_event_id.end()) {
- HandlersMap & handlers_map = subscriber_list->second;
- auto handlers = handlers_map.find(channel);
- if (handlers != handlers_map.end()) {
- HandlersVec & callbacks = handlers->second;
- for (auto it = callbacks.begin(); it != callbacks.end(); ++it) {
- if ((*it)->get_type() == handler_name) {
- it = callbacks.erase(it);
- return;
- }
- }
- }
- }
- } else {
- auto handlers_it = this->subscribers.find(event_type);
- if (handlers_it != this->subscribers.end()) {
- HandlersVec & handlers = handlers_it->second;
- for (auto it = handlers.begin(); it != handlers.end(); ++it) {
- if ((*it)->get_type() == handler_name) {
- it = handlers.erase(it);
- return;
- }
- }
- }
- }
+void EventManager::trigger_event(const EventType & event, event_channel_t channel) {
+ this->handle_event(typeid(EventType), channel, event);
}
} // namespace crepe
diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp
index 287e81d..3c36a21 100644
--- a/src/crepe/api/GameObject.cpp
+++ b/src/crepe/api/GameObject.cpp
@@ -9,7 +9,7 @@ using namespace std;
GameObject::GameObject(ComponentManager & component_manager, game_object_id_t id,
const std::string & name, const std::string & tag,
- const Vector2 & position, double rotation, double scale)
+ const vec2 & position, double rotation, double scale)
: id(id),
component_manager(component_manager) {
@@ -23,12 +23,10 @@ void GameObject::set_parent(const GameObject & parent) {
ComponentManager & mgr = this->component_manager;
// Set parent on own Metadata component
- vector<reference_wrapper<Metadata>> this_metadata
- = mgr.get_components_by_id<Metadata>(this->id);
+ RefVector<Metadata> this_metadata = mgr.get_components_by_id<Metadata>(this->id);
this_metadata.at(0).get().parent = parent.id;
// Add own id to children list of parent's Metadata component
- vector<reference_wrapper<Metadata>> parent_metadata
- = mgr.get_components_by_id<Metadata>(parent.id);
+ RefVector<Metadata> parent_metadata = mgr.get_components_by_id<Metadata>(parent.id);
parent_metadata.at(0).get().children.push_back(this->id);
}
diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h
index 34ef8bb..fcb8d9a 100644
--- a/src/crepe/api/GameObject.h
+++ b/src/crepe/api/GameObject.h
@@ -2,7 +2,6 @@
#include <string>
-#include "Vector2.h"
#include "types.h"
namespace crepe {
@@ -30,7 +29,7 @@ private:
* \param scale The scale of the GameObject
*/
GameObject(ComponentManager & component_manager, game_object_id_t id,
- const std::string & name, const std::string & tag, const Vector2 & position,
+ 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/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp
index f5426be..8642655 100644
--- a/src/crepe/api/IKeyListener.cpp
+++ b/src/crepe/api/IKeyListener.cpp
@@ -2,65 +2,18 @@
using namespace crepe;
-// Constructor with default channel
-IKeyListener::IKeyListener()
- : channel(0),
- active(true),
- event_manager(EventManager::get_instance()) {
- this->subscribe_events();
-}
-
// Constructor with specified channel
-IKeyListener::IKeyListener(int channel)
- : channel(channel),
- active(true),
- event_manager(EventManager::get_instance()) {
- this->subscribe_events();
+IKeyListener::IKeyListener(event_channel_t channel)
+ : event_manager(EventManager::get_instance()) {
+ this->press_id = event_manager.subscribe<KeyPressEvent>(
+ [this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel);
+ this->release_id = event_manager.subscribe<KeyReleaseEvent>(
+ [this](const KeyReleaseEvent & event) { return this->on_key_released(event); },
+ channel);
}
// Destructor, unsubscribe events
-IKeyListener::~IKeyListener() { this->unsubscribe_events(); }
-
-// Subscribe to key events
-void IKeyListener::subscribe_events() {
- key_pressed_handler
- = [this](const KeyPressEvent & event) { return this->on_key_pressed(event); };
- key_released_handler
- = [this](const KeyReleaseEvent & event) { return this->on_key_released(event); };
-
- event_manager.subscribe<KeyPressEvent>(std::move(this->key_pressed_handler),
- this->channel);
- event_manager.subscribe<KeyReleaseEvent>(std::move(this->key_released_handler),
- this->channel);
-}
-
-// Unsubscribe from key events
-void IKeyListener::unsubscribe_events() {
- event_manager.unsubscribe<KeyPressEvent>(this->key_pressed_handler, this->channel);
- event_manager.unsubscribe<KeyReleaseEvent>(this->key_released_handler, this->channel);
-}
-
-// Activate key listening
-void IKeyListener::activate_keys() {
- if (this->active) {
- return;
- }
- this->active = true;
- this->subscribe_events();
-}
-
-// Deactivate key listening
-void IKeyListener::deactivate_keys() {
- if (!this->active) {
- return;
- }
- this->active = false;
- this->unsubscribe_events();
-}
-
-// Set a new channel for key events
-void IKeyListener::set_channel(int channel) {
- this->unsubscribe_events();
- this->channel = channel;
- this->subscribe_events();
+IKeyListener::~IKeyListener() {
+ event_manager.unsubscribe(this->press_id);
+ event_manager.unsubscribe(this->release_id);
}
diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h
index d492387..328a4c2 100644
--- a/src/crepe/api/IKeyListener.h
+++ b/src/crepe/api/IKeyListener.h
@@ -16,11 +16,11 @@ public:
* \brief Constructs an IKeyListener with a specified channel.
* \param channel The channel ID for event handling.
*/
- IKeyListener(int channel);
- IKeyListener();
+ IKeyListener(event_channel_t channel = EventManager::CHANNEL_ALL);
virtual ~IKeyListener();
IKeyListener(const IKeyListener &) = delete;
IKeyListener & operator=(const IKeyListener &) = delete;
+ IKeyListener & operator=(IKeyListener &&) = delete;
IKeyListener(IKeyListener &&) = delete;
/**
@@ -37,42 +37,12 @@ public:
*/
virtual bool on_key_released(const KeyReleaseEvent & event) = 0;
- /**
- * \brief Activates key listening.
- */
- void activate_keys();
-
- /**
- * \brief Deactivates key listening.
- */
- void deactivate_keys();
-
- /**
- * \brief Sets the channel ID for event handling.
- * \param channel The channel ID to set.
- */
- void set_channel(int channel);
-
-protected:
- /**
- * \brief Subscribes to key events.
- */
- void subscribe_events();
-
- /**
- * \brief Unsubscribes from key events.
- */
- void unsubscribe_events();
-
private:
- //! Indicates whether key listening is active.
- bool active = true;
- //! Channel ID for event handling.
- int channel = 0;
- //! Key press event handler.
- EventHandler<KeyPressEvent> key_pressed_handler;
- //!< Key release event handler.
- EventHandler<KeyReleaseEvent> key_released_handler;
+ //! Key press event id
+ subscription_t press_id = -1;
+ //! Key release event id
+ subscription_t release_id = -1;
+ //! EventManager reference
EventManager & event_manager;
};
diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp
index 5ee2814..989aeb3 100644
--- a/src/crepe/api/IMouseListener.cpp
+++ b/src/crepe/api/IMouseListener.cpp
@@ -2,63 +2,28 @@
using namespace crepe;
-IMouseListener::IMouseListener(int channel)
- : event_manager(EventManager::get_instance()),
- channel(channel) {
- this->subscribe_events();
+IMouseListener::IMouseListener(event_channel_t channel)
+ : event_manager(EventManager::get_instance()) {
+ this->click_id = event_manager.subscribe<MouseClickEvent>(
+ [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); },
+ channel);
+
+ this->press_id = event_manager.subscribe<MousePressEvent>(
+ [this](const MousePressEvent & event) { return this->on_mouse_pressed(event); },
+ channel);
+
+ this->release_id = event_manager.subscribe<MouseReleaseEvent>(
+ [this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); },
+ channel);
+
+ this->move_id = event_manager.subscribe<MouseMoveEvent>(
+ [this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }, channel);
}
-IMouseListener::IMouseListener() : event_manager(EventManager::get_instance()) {
- this->subscribe_events();
-}
-
-IMouseListener::~IMouseListener() { this->unsubscribe_events(); }
-
-void IMouseListener::subscribe_events() {
- // Define handler lambdas and subscribe them
- mouse_click_handler
- = [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); };
- mouse_press_handler
- = [this](const MousePressEvent & event) { return this->on_mouse_pressed(event); };
- mouse_release_handler
- = [this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); };
- mouse_move_handler
- = [this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); };
-
- // Subscribe event handlers (no need for std::move)
- event_manager.subscribe<MouseClickEvent>(std::move(mouse_click_handler), this->channel);
- event_manager.subscribe<MousePressEvent>(std::move(mouse_press_handler), this->channel);
- event_manager.subscribe<MouseReleaseEvent>(std::move(mouse_release_handler),
- this->channel);
- event_manager.subscribe<MouseMoveEvent>(std::move(mouse_move_handler), this->channel);
-}
-
-void IMouseListener::unsubscribe_events() {
+IMouseListener::~IMouseListener() {
// Unsubscribe event handlers
- event_manager.unsubscribe<MouseClickEvent>(mouse_click_handler, this->channel);
- event_manager.unsubscribe<MousePressEvent>(mouse_press_handler, this->channel);
- event_manager.unsubscribe<MouseReleaseEvent>(mouse_release_handler, this->channel);
- event_manager.unsubscribe<MouseMoveEvent>(mouse_move_handler, this->channel);
-}
-
-void IMouseListener::activate_mouse() {
- if (this->active) {
- return;
- }
- this->subscribe_events();
- this->active = true;
-}
-
-void IMouseListener::deactivate_mouse() {
- if (!this->active) {
- return;
- }
- this->unsubscribe_events();
- this->active = false;
-}
-
-void IMouseListener::set_channel(int channel) {
- this->unsubscribe_events();
- this->channel = channel;
- this->subscribe_events();
+ event_manager.unsubscribe(this->click_id);
+ event_manager.unsubscribe(this->press_id);
+ event_manager.unsubscribe(this->release_id);
+ event_manager.unsubscribe(this->move_id);
}
diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h
index c315c35..15e1619 100644
--- a/src/crepe/api/IMouseListener.h
+++ b/src/crepe/api/IMouseListener.h
@@ -12,15 +12,15 @@ namespace crepe {
*/
class IMouseListener {
public:
- IMouseListener();
/**
* \brief Constructs an IMouseListener with a specified channel.
* \param channel The channel ID for event handling.
*/
- IMouseListener(int channel);
+ IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL);
virtual ~IMouseListener();
- IMouseListener(const IMouseListener &) = delete;
IMouseListener & operator=(const IMouseListener &) = delete;
+ IMouseListener(const IMouseListener &) = delete;
+ IMouseListener & operator=(const IMouseListener &&) = delete;
IMouseListener(IMouseListener &&) = delete;
/**
@@ -56,46 +56,16 @@ public:
*/
virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0;
- /**
- * \brief Activates mouse listening.
- */
- void activate_mouse();
-
- /**
- * \brief Deactivates mouse listening.
- */
- void deactivate_mouse();
-
- /**
- * \brief Sets the channel ID for event handling.
- * \param channel The channel ID to set.
- */
- void set_channel(int channel);
-
-protected:
- /**
- * \brief Subscribes to mouse events on the specified channel.
- */
- void subscribe_events();
-
- /**
- * \brief Unsubscribes from mouse events on the specified channel.
- */
- void unsubscribe_events();
-
private:
- //! Indicates whether mouse listening is active.
- bool active = true;
- //! Channel ID for event handling.
- int channel = 0;
- //! Mouse click event handler.
- EventHandler<MouseClickEvent> mouse_click_handler;
- //! Mouse press event handler.
- EventHandler<MousePressEvent> mouse_press_handler;
- //! Mouse release event handler.
- EventHandler<MouseReleaseEvent> mouse_release_handler;
- //! Mouse move event handler.
- EventHandler<MouseMoveEvent> mouse_move_handler;
+ //! Mouse click event id
+ subscription_t click_id = -1;
+ //! Mouse press event id
+ subscription_t press_id = -1;
+ //! Mouse release event id
+ subscription_t release_id = -1;
+ //! Mouse move event id
+ subscription_t move_id = -1;
+ //! EventManager reference
EventManager & event_manager;
};
diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h
index feed0b2..9e173e0 100644
--- a/src/crepe/api/KeyCodes.h
+++ b/src/crepe/api/KeyCodes.h
@@ -1,170 +1,93 @@
#pragma once
-//! \enum MouseButton
-//! \brief Enumeration for mouse button inputs, including standard and extended buttons.
+//! Enumeration for mouse button inputs, including standard and extended buttons.
enum class MouseButton {
- //! No mouse button input.
- NONE = 0,
- //! Left mouse button.
- LEFT_MOUSE = 1,
- //! Right mouse button.
- RIGHT_MOUSE = 2,
- //! Middle mouse button (scroll wheel press).
- MIDDLE_MOUSE = 3,
- //! First extended mouse button.
- X1_MOUSE = 4,
- //! Second extended mouse button.
- X2_MOUSE = 5,
- //! Scroll wheel upward movement.
- SCROLL_UP = 6,
- //! Scroll wheel downward movement.
- SCROLL_DOWN = 7
+ NONE = 0, //!< No mouse button input.
+ LEFT_MOUSE = 1, //!< Left mouse button.
+ RIGHT_MOUSE = 2, //!< Right mouse button.
+ MIDDLE_MOUSE = 3, //!< Middle mouse button (scroll wheel press).
+ X1_MOUSE = 4, //!< First extended mouse button.
+ X2_MOUSE = 5, //!< Second extended mouse button.
+ SCROLL_UP = 6, //!< Scroll wheel upward movement.
+ SCROLL_DOWN = 7, //!< Scroll wheel downward movement.
};
-//! \enum Keycode
-//! \brief Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys.
-enum class Keycode : int {
- //! No key input.
- NONE = 0,
- //! Spacebar.
- SPACE = 32,
- //! Apostrophe (').
- APOSTROPHE = 39,
- //! Comma (,).
- COMMA = 44,
- //! Minus (-).
- MINUS = 45,
- //! Period (.).
- PERIOD = 46,
- //! Slash (/).
- SLASH = 47,
- //! Digit 0.
- D0 = 48,
- //! Digit 1.
- D1 = 49,
- //! Digit 2.
- D2 = 50,
- //! Digit 3.
- D3 = 51,
- //! Digit 4.
- D4 = 52,
- //! Digit 5.
- D5 = 53,
- //! Digit 6.
- D6 = 54,
- //! Digit 7.
- D7 = 55,
- //! Digit 8.
- D8 = 56,
- //! Digit 9.
- D9 = 57,
- //! Semicolon (;).
- SEMICOLON = 59,
- //! Equal sign (=).
- EQUAL = 61,
- //! Key 'A'.
- A = 65,
- //! Key 'B'.
- B = 66,
- //! Key 'C'.
- C = 67,
- //! Key 'D'.
- D = 68,
- //! Key 'E'.
- E = 69,
- //! Key 'F'.
- F = 70,
- //! Key 'G'.
- G = 71,
- //! Key 'H'.
- H = 72,
- //! Key 'I'.
- I = 73,
- //! Key 'J'.
- J = 74,
- //! Key 'K'.
- K = 75,
- //! Key 'L'.
- L = 76,
- //! Key 'M'.
- M = 77,
- //! Key 'N'.
- N = 78,
- //! Key 'O'.
- O = 79,
- //! Key 'P'.
- P = 80,
- //! Key 'Q'.
- Q = 81,
- //! Key 'R'.
- R = 82,
- //! Key 'S'.
- S = 83,
- //! Key 'T'.
- T = 84,
- //! Key 'U'.
- U = 85,
- //! Key 'V'.
- V = 86,
- //! Key 'W'.
- W = 87,
- //! Key 'X'.
- X = 88,
- //! Key 'Y'.
- Y = 89,
- //! Key 'Z'.
- Z = 90,
- //! Left bracket ([).
- LEFT_BRACKET = 91,
- //! Backslash (\).
- BACKSLASH = 92,
- //! Right bracket (]).
- RIGHT_BRACKET = 93,
- //! Grave accent (`).
- GRAVE_ACCENT = 96,
- //! Non-US key #1.
- WORLD1 = 161,
- //! Non-US key #2.
- WORLD2 = 162,
- //! Escape key.
- ESCAPE = 256,
- //! Enter key.
- ENTER = 257,
- //! Tab key.
- TAB = 258,
- //! Backspace key.
- BACKSPACE = 259,
- //! Insert key.
- INSERT = 260,
- //! Delete key.
- DELETE = 261,
- //! Right arrow key.
- RIGHT = 262,
- //! Left arrow key.
- LEFT = 263,
- //! Down arrow key.
- DOWN = 264,
- //! Up arrow key.
- UP = 265,
- //! Page Up key.
- PAGE_UP = 266,
- //! Page Down key.
- PAGE_DOWN = 267,
- //! Home key.
- HOME = 268,
- //! End key.
- END = 269,
- //! Caps Lock key.
- CAPS_LOCK = 280,
- //! Scroll Lock key.
- SCROLL_LOCK = 281,
- //! Num Lock key.
- NUM_LOCK = 282,
- //! Print Screen key.
- PRINT_SCREEN = 283,
- //! Pause key.
- PAUSE = 284,
- //! Function keys (F1-F25).
+//! Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys.
+enum class Keycode {
+ NONE = 0, //!< No key input.
+ SPACE = 32, //!< Spacebar.
+ APOSTROPHE = 39, //!< Apostrophe (').
+ COMMA = 44, //!< Comma (,).
+ MINUS = 45, //!< Minus (-).
+ PERIOD = 46, //!< Period (.).
+ SLASH = 47, //!< Slash (/).
+ D0 = 48, //!< Digit 0.
+ D1 = 49, //!< Digit 1.
+ D2 = 50, //!< Digit 2.
+ D3 = 51, //!< Digit 3.
+ D4 = 52, //!< Digit 4.
+ D5 = 53, //!< Digit 5.
+ D6 = 54, //!< Digit 6.
+ D7 = 55, //!< Digit 7.
+ D8 = 56, //!< Digit 8.
+ D9 = 57, //!< Digit 9.
+ SEMICOLON = 59, //!< Semicolon (;).
+ EQUAL = 61, //!< Equal sign (=).
+ A = 65, //!< Key 'A'.
+ B = 66, //!< Key 'B'.
+ C = 67, //!< Key 'C'.
+ D = 68, //!< Key 'D'.
+ E = 69, //!< Key 'E'.
+ F = 70, //!< Key 'F'.
+ G = 71, //!< Key 'G'.
+ H = 72, //!< Key 'H'.
+ I = 73, //!< Key 'I'.
+ J = 74, //!< Key 'J'.
+ K = 75, //!< Key 'K'.
+ L = 76, //!< Key 'L'.
+ M = 77, //!< Key 'M'.
+ N = 78, //!< Key 'N'.
+ O = 79, //!< Key 'O'.
+ P = 80, //!< Key 'P'.
+ Q = 81, //!< Key 'Q'.
+ R = 82, //!< Key 'R'.
+ S = 83, //!< Key 'S'.
+ T = 84, //!< Key 'T'.
+ U = 85, //!< Key 'U'.
+ V = 86, //!< Key 'V'.
+ W = 87, //!< Key 'W'.
+ X = 88, //!< Key 'X'.
+ Y = 89, //!< Key 'Y'.
+ Z = 90, //!< Key 'Z'.
+ LEFT_BRACKET = 91, //!< Left bracket ([).
+ BACKSLASH = 92, //!< Backslash (\).
+ RIGHT_BRACKET = 93, //!< Right bracket (]).
+ GRAVE_ACCENT = 96, //!< Grave accent (`).
+ WORLD1 = 161, //!< Non-US key #1.
+ WORLD2 = 162, //!< Non-US key #2.
+ ESCAPE = 256, //!< Escape key.
+ ENTER = 257, //!< Enter key.
+ TAB = 258, //!< Tab key.
+ BACKSPACE = 259, //!< Backspace key.
+ INSERT = 260, //!< Insert key.
+ DELETE = 261, //!< Delete key.
+ RIGHT = 262, //!< Right arrow key.
+ LEFT = 263, //!< Left arrow key.
+ DOWN = 264, //!< Down arrow key.
+ UP = 265, //!< Up arrow key.
+ PAGE_UP = 266, //!< Page Up key.
+ PAGE_DOWN = 267, //!< Page Down key.
+ HOME = 268, //!< Home key.
+ END = 269, //!< End key.
+ CAPS_LOCK = 280, //!< Caps Lock key.
+ SCROLL_LOCK = 281, //!< Scroll Lock key.
+ NUM_LOCK = 282, //!< Num Lock key.
+ PRINT_SCREEN = 283, //!< Print Screen key.
+ PAUSE = 284, //!< Pause key.
+ /**
+ * \name Function keys (F1-F25).
+ * \{
+ */
F1 = 290,
F2 = 291,
F3 = 292,
@@ -190,7 +113,11 @@ enum class Keycode : int {
F23 = 312,
F24 = 313,
F25 = 314,
- //! Keypad digits and operators.
+ /// \}
+ /**
+ * \name Keypad digits and operators.
+ * \{
+ */
KP0 = 320,
KP1 = 321,
KP2 = 322,
@@ -208,7 +135,11 @@ enum class Keycode : int {
KP_ADD = 334,
KP_ENTER = 335,
KP_EQUAL = 336,
- //! Modifier keys.
+ /// \}
+ /**
+ * \name Modifier keys.
+ * \{
+ */
LEFT_SHIFT = 340,
LEFT_CONTROL = 341,
LEFT_ALT = 342,
@@ -217,6 +148,6 @@ enum class Keycode : int {
RIGHT_CONTROL = 345,
RIGHT_ALT = 346,
RIGHT_SUPER = 347,
- //! Menu key.
- MENU = 348
+ /// \}
+ MENU = 348, //!< Menu key.
};
diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp
index af306c0..9599943 100644
--- a/src/crepe/api/LoopManager.cpp
+++ b/src/crepe/api/LoopManager.cpp
@@ -59,7 +59,7 @@ void LoopManager::loop() {
void LoopManager::setup() {
this->game_running = true;
LoopTimer::get_instance().start();
- LoopTimer::get_instance().set_fps(60);
+ LoopTimer::get_instance().set_fps(200);
}
void LoopManager::render() {
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
index b18c9d1..13e6dac 100644
--- a/src/crepe/api/LoopManager.h
+++ b/src/crepe/api/LoopManager.h
@@ -4,19 +4,27 @@
#include "../ComponentManager.h"
#include "../system/System.h"
+#include "api/SceneManager.h"
namespace crepe {
+/**
+ * \brief Main game loop manager
+ *
+ * This class is responsible for managing the game loop, including initialization and updating.
+ */
class LoopManager {
public:
void start();
+ LoopManager();
+
/**
- * \brief Set game running variable
+ * \brief Add a new concrete scene to the scene manager
*
- * \param running running (false = game shutdown, true = game running)
+ * \tparam T Type of concrete scene
*/
- void set_running(bool running);
- LoopManager();
+ template <typename T>
+ void add_scene();
private:
/**
@@ -59,7 +67,14 @@ private:
* This function updates physics and game logic based on LoopTimer's fixed_delta_time.
*/
void fixed_update();
-
+
+ /**
+ * \brief Set game running variable
+ *
+ * \param running running (false = game shutdown, true = game running)
+ */
+ void set_running(bool running);
+
/**
* \brief Function for executing render-related systems.
*
@@ -72,6 +87,8 @@ private:
private:
//! Component manager instance
ComponentManager component_manager{};
+ //! Scene manager instance
+ SceneManager scene_manager{component_manager};
private:
/**
diff --git a/src/crepe/api/LoopManager.hpp b/src/crepe/api/LoopManager.hpp
index 0b14fdb..9cf470b 100644
--- a/src/crepe/api/LoopManager.hpp
+++ b/src/crepe/api/LoopManager.hpp
@@ -11,6 +11,11 @@
namespace crepe {
template <class T>
+void LoopManager::add_scene() {
+ this->scene_manager.add_scene<T>();
+}
+
+template <class T>
T & LoopManager::get_system() {
using namespace std;
static_assert(is_base_of<System, T>::value,
diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp
index a9800b7..15a0e3a 100644
--- a/src/crepe/api/LoopTimer.cpp
+++ b/src/crepe/api/LoopTimer.cpp
@@ -47,7 +47,7 @@ double LoopTimer::get_fixed_delta_time() const { return this->fixed_delta_time.c
void LoopTimer::set_fps(int fps) {
this->fps = fps;
// target time per frame in seconds
- this->frame_target_time = std::chrono::seconds(1) / fps;
+ this->frame_target_time = std::chrono::duration<double>(1.0) / fps;
}
int LoopTimer::get_fps() const { return this->fps; }
diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h
index f277d7b..9393439 100644
--- a/src/crepe/api/LoopTimer.h
+++ b/src/crepe/api/LoopTimer.h
@@ -130,9 +130,9 @@ private:
//! Delta time for the current frame in seconds
std::chrono::duration<double> delta_time{0.0};
//! Target time per frame in seconds
- std::chrono::duration<double> frame_target_time = std::chrono::seconds(1) / fps;
+ std::chrono::duration<double> frame_target_time = std::chrono::duration<double>(1.0) / fps;
//! Fixed delta time for fixed updates in seconds
- std::chrono::duration<double> fixed_delta_time = std::chrono::seconds(1) / 50;
+ std::chrono::duration<double> fixed_delta_time = std::chrono::duration<double>(1.0) / 50.0;
//! Total elapsed game time in seconds
std::chrono::duration<double> elapsed_time{0.0};
//! Total elapsed time for fixed updates in seconds
diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h
index 33112e1..b83fd61 100644
--- a/src/crepe/api/ParticleEmitter.h
+++ b/src/crepe/api/ParticleEmitter.h
@@ -4,7 +4,7 @@
#include "Component.h"
#include "Particle.h"
-#include "Vector2.h"
+#include "types.h"
namespace crepe {
@@ -30,7 +30,7 @@ public:
//! boundary height (midpoint is emitter location)
double height = 0.0;
//! boundary offset from particle emitter location
- Vector2 offset;
+ vec2 offset;
//! reset on exit or stop velocity and set max postion
bool reset_on_exit = false;
};
@@ -43,7 +43,7 @@ public:
*/
struct Data {
//! position of the emitter
- Vector2 position;
+ vec2 position;
//! maximum number of particles
const unsigned int max_particles = 0;
//! rate of particle emission per update (Lowest value = 0.001 any lower is ignored)
@@ -61,7 +61,7 @@ public:
//! end Lifespan of particle
double end_lifespan = 0.0;
//! force over time (physics)
- Vector2 force_over_time;
+ vec2 force_over_time;
//! particle boundary
Boundary boundary;
//! collection of particles
diff --git a/src/crepe/api/Rigidbody.cpp b/src/crepe/api/Rigidbody.cpp
index 6b87695..576ca45 100644
--- a/src/crepe/api/Rigidbody.cpp
+++ b/src/crepe/api/Rigidbody.cpp
@@ -6,7 +6,7 @@ crepe::Rigidbody::Rigidbody(game_object_id_t id, const Data & data)
: Component(id),
data(data) {}
-void crepe::Rigidbody::add_force_linear(const Vector2 & force) {
+void crepe::Rigidbody::add_force_linear(const vec2 & force) {
this->data.linear_velocity += force;
}
diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h
index 3e5c7a3..3b0588f 100644
--- a/src/crepe/api/Rigidbody.h
+++ b/src/crepe/api/Rigidbody.h
@@ -2,7 +2,7 @@
#include "../Component.h"
-#include "Vector2.h"
+#include "types.h"
namespace crepe {
@@ -56,11 +56,11 @@ public:
//! Changes if physics apply
BodyType body_type = BodyType::DYNAMIC;
//! linear velocity of object
- Vector2 linear_velocity;
+ vec2 linear_velocity;
//! maximum linear velocity of object
- Vector2 max_linear_velocity;
+ vec2 max_linear_velocity;
//! linear damping of object
- Vector2 linear_damping;
+ vec2 linear_damping;
//! angular velocity of object
double angular_velocity = 0.0;
//! max angular velocity of object
@@ -90,7 +90,7 @@ public:
*
* \param force Vector2 that is added to the linear force.
*/
- void add_force_linear(const Vector2 & force);
+ void add_force_linear(const vec2 & force);
/**
* \brief add a angular force to the Rigidbody.
*
diff --git a/src/crepe/api/Scene.cpp b/src/crepe/api/Scene.cpp
deleted file mode 100644
index 88aa82d..0000000
--- a/src/crepe/api/Scene.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "Scene.h"
-
-using namespace crepe;
-
-Scene::Scene(ComponentManager & mgr, const std::string & name)
- : component_manager(mgr),
- name(name) {}
diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h
index 0e516b6..f6fdb2a 100644
--- a/src/crepe/api/Scene.h
+++ b/src/crepe/api/Scene.h
@@ -2,25 +2,53 @@
#include <string>
+#include "../util/OptionalRef.h"
+
namespace crepe {
class SceneManager;
class ComponentManager;
+/**
+ * \brief Represents a Scene
+ *
+ * This class represents a Scene. The Scene class is only used as an interface for the game
+ * programmer.
+ */
class Scene {
protected:
- Scene(ComponentManager & mgr, const std::string & name);
+ // NOTE: This must be the only constructor on Scene, see "Late references" below
+ Scene() = default;
+ //! SceneManager instances Scene
friend class SceneManager;
public:
virtual ~Scene() = default;
public:
+ //! Load the scene
virtual void load_scene() = 0;
- const std::string name;
+ /**
+ * \brief Get the scene's name
+ * \return The scene's name
+ */
+ virtual std::string get_name() const = 0;
protected:
- ComponentManager & component_manager;
+ /**
+ * \name Late references
+ *
+ * These references are set by SceneManager immediately after calling the constructor of Scene.
+ *
+ * \note Scene must have a constructor without arguments so the game programmer doesn't need to
+ * manually add `using Scene::Scene` to their concrete scene class, if they want to add a
+ * constructor with arguments (e.g. for passing references to their own concrete Scene classes).
+ *
+ * \{
+ */
+ //! Reference to the ComponentManager
+ OptionalRef<ComponentManager> component_manager;
+ //! \}
};
} // namespace crepe
diff --git a/src/crepe/api/SceneManager.cpp b/src/crepe/api/SceneManager.cpp
index 7fb5cb0..1f783ad 100644
--- a/src/crepe/api/SceneManager.cpp
+++ b/src/crepe/api/SceneManager.cpp
@@ -18,7 +18,7 @@ void SceneManager::load_next_scene() {
auto it = find_if(this->scenes.begin(), this->scenes.end(),
[&next_scene = this->next_scene](unique_ptr<Scene> & scene) {
- return scene->name == next_scene;
+ return scene.get()->get_name() == next_scene;
});
// next scene not found
diff --git a/src/crepe/api/SceneManager.h b/src/crepe/api/SceneManager.h
index e854794..f6f62cd 100644
--- a/src/crepe/api/SceneManager.h
+++ b/src/crepe/api/SceneManager.h
@@ -1,7 +1,6 @@
#pragma once
#include <memory>
-#include <queue>
#include <vector>
#include "Scene.h"
@@ -10,8 +9,15 @@ namespace crepe {
class ComponentManager;
+/**
+ * \brief Manages scenes
+ *
+ * This class manages scenes. It can add new scenes and load them. It also manages the current scene
+ * and the next scene.
+ */
class SceneManager {
public:
+ //! \param mgr Reference to the ComponentManager
SceneManager(ComponentManager & mgr);
public:
@@ -19,10 +25,9 @@ public:
* \brief Add a new concrete scene to the scene manager
*
* \tparam T Type of concrete scene
- * \param name Name of new scene
*/
- template <typename T>
- void add_scene(const std::string & name);
+ template <typename T, typename... Args>
+ void add_scene(Args &&... args);
/**
* \brief Set the next scene
*
@@ -35,8 +40,11 @@ public:
void load_next_scene();
private:
+ //! Vector of concrete scenes (added by add_scene())
std::vector<std::unique_ptr<Scene>> scenes;
+ //! Next scene to load
std::string next_scene;
+ //! Reference to the ComponentManager
ComponentManager & component_manager;
};
diff --git a/src/crepe/api/SceneManager.hpp b/src/crepe/api/SceneManager.hpp
index 714f690..5c8e417 100644
--- a/src/crepe/api/SceneManager.hpp
+++ b/src/crepe/api/SceneManager.hpp
@@ -4,17 +4,21 @@
namespace crepe {
-template <typename T>
-void SceneManager::add_scene(const std::string & name) {
+template <typename T, typename... Args>
+void SceneManager::add_scene(Args &&... args) {
using namespace std;
static_assert(is_base_of<Scene, T>::value, "T must be derived from Scene");
- Scene * scene = new T(this->component_manager, name);
- this->scenes.emplace_back(unique_ptr<Scene>(scene));
+ Scene * scene = new T(std::forward<Args>(args)...);
+ unique_ptr<Scene> unique_scene(scene);
+
+ unique_scene->component_manager = this->component_manager;
+
+ this->scenes.emplace_back(std::move(unique_scene));
// The first scene added, is the one that will be loaded at the beginning
if (next_scene.empty()) {
- next_scene = name;
+ next_scene = scene->get_name();
}
}
diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp
new file mode 100644
index 0000000..fcbe4c7
--- /dev/null
+++ b/src/crepe/api/Script.cpp
@@ -0,0 +1,15 @@
+#include "Script.h"
+
+using namespace crepe;
+
+Script::~Script() {
+ EventManager & evmgr = this->event_manager;
+ for (auto id : this->listeners) {
+ evmgr.unsubscribe(id);
+ }
+}
+
+template <>
+void Script::subscribe(const EventHandler<CollisionEvent> & callback) {
+ this->subscribe_internal(callback, this->game_object_id);
+}
diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h
index 2b70379..a0870cb 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -3,6 +3,9 @@
#include <vector>
#include "../types.h"
+#include "../util/OptionalRef.h"
+
+#include "EventManager.h"
namespace crepe {
@@ -16,13 +19,23 @@ class ComponentManager;
* This class is used as a base class for user-defined scripts that can be added to game
* objects using the \c BehaviorScript component.
*
- * \note Additional *events* (like Unity's OnDisable and OnEnable) should be implemented as
+ * \info Additional *events* (like Unity's OnDisable and OnEnable) should be implemented as
* member or lambda methods in derivative user script classes and registered in \c init().
+ *
+ * \warning Concrete scripts are allowed do create a custom constructor, but the utility
+ * functions should not be called inside the constructor as they rely on late references that
+ * are only available after the constructor returns.
+ *
+ * \see feature_script
*/
class Script {
protected:
/**
- * \brief Script initialization function
+ * \name Interface functions
+ * \{
+ */
+ /**
+ * \brief Script initialization function (empty by default)
*
* This function is called during the ScriptSystem::update() routine *before*
* Script::update() if it (a) has not yet been called and (b) the \c BehaviorScript component
@@ -30,24 +43,31 @@ protected:
*/
virtual void init() {}
/**
- * \brief Script update function
+ * \brief Script update function (empty by default)
*
* This function is called during the ScriptSystem::update() routine if the \c BehaviorScript
* component holding this script instance is active.
*/
virtual void update() {}
+ //! \}
+
//! ScriptSystem calls \c init() and \c update()
friend class crepe::ScriptSystem;
protected:
/**
- * \brief Get single component of type \c T on this game object (utility)
+ * \name Utility functions
+ * \{
+ */
+
+ /**
+ * \brief Get single component of type \c T on this game object
*
* \tparam T Type of component
*
* \returns Reference to component
*
- * \throws nullptr if this game object does not have a component matching type \c T
+ * \throws std::runtime_error if this game object does not have a component with type \c T
*/
template <typename T>
T & get_component() const;
@@ -55,34 +75,117 @@ protected:
// cause compile-time errors
/**
- * \brief Get all components of type \c T on this game object (utility)
+ * \brief Get all components of type \c T on this game object
*
* \tparam T Type of component
*
* \returns List of component references
*/
template <typename T>
- std::vector<std::reference_wrapper<T>> get_components() const;
+ RefVector<T> get_components() const;
+
+ /**
+ * \brief Log a message using Log::logf
+ *
+ * \tparam Args Log::logf parameters
+ * \param args Log::logf parameters
+ */
+ template <typename... Args>
+ void logf(Args &&... args);
+
+ /**
+ * \brief Subscribe to an event with an explicit channel
+ * \see EventManager::subscribe
+ */
+ template <typename EventType>
+ void subscribe(const EventHandler<EventType> & callback, event_channel_t channel);
+ /**
+ * \brief Subscribe to an event on EventManager::CHANNEL_ALL
+ * \see EventManager::subscribe
+ */
+ template <typename EventType>
+ void subscribe(const EventHandler<EventType> & callback);
+
+ //! \}
+
+private:
+ /**
+ * \brief Internal subscribe function
+ *
+ * This function exists so certain template specializations of Script::subscribe can be
+ * explicitly deleted, and does the following:
+ * - Wrap the user-provided callback in a check that tests if the parent BehaviorScript
+ * component is still active
+ * - Store the subscriber handle returned by the event manager so this listener is
+ * automatically unsubscribed at the end of this Script instance's life
+ *
+ * \tparam EventType concrete Event class
+ * \param callback User-provided callback function
+ * \param channel Event channel (may have been overridden by template specializations)
+ */
+ template <typename EventType>
+ void subscribe_internal(const EventHandler<EventType> & callback, event_channel_t channel);
protected:
- // NOTE: Script must have a constructor without arguments so the game programmer doesn't need
- // to manually add `using Script::Script` to their concrete script class.
+ // NOTE: This must be the only constructor on Script, see "Late references" below
Script() = default;
//! Only \c BehaviorScript instantiates Script
friend class BehaviorScript;
+public:
+ // std::unique_ptr destroys script
+ virtual ~Script();
+
+private:
+ Script(const Script &) = delete;
+ Script(Script &&) = delete;
+ Script & operator=(const Script &) = delete;
+ Script & operator=(Script &&) = delete;
+
private:
- // These references are set by BehaviorScript immediately after calling the constructor of
- // Script.
- game_object_id_t game_object_id = -1;
- ComponentManager * component_manager_ref = nullptr;
- // TODO: use OptionalRef instead of pointer
+ /**
+ * \name Late references
+ *
+ * These references are set by BehaviorScript immediately after calling the constructor of
+ * Script.
+ *
+ * \note Script must have a constructor without arguments so the game programmer doesn't need
+ * to manually add `using Script::Script` to their concrete script class if they want to
+ * implement a non-default constructor (e.g. for passing references to their own concrete
+ * Script classes).
+ *
+ * \{
+ */
+ //! Game object ID of game object parent BehaviorScript is attached to
+ game_object_id_t game_object_id;
+ //! Reference to parent component
+ OptionalRef<bool> active;
+ //! Reference to component manager instance
+ OptionalRef<ComponentManager> component_manager;
+ //! Reference to event manager instance
+ OptionalRef<EventManager> event_manager;
+ //! \}
private:
//! Flag to indicate if \c init() has been called already
bool initialized = false;
+ //! List of subscribed events
+ std::vector<subscription_t> listeners;
};
+/**
+ * \brief Subscribe to CollisionEvent 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 CollisionEvent events that apply to the
+ * current GameObject the parent BehaviorScript is attached to.
+ */
+template <>
+void Script::subscribe(const EventHandler<CollisionEvent> & callback);
+template <>
+void Script::subscribe(const EventHandler<CollisionEvent> & 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 a064a90..a2463bf 100644
--- a/src/crepe/api/Script.hpp
+++ b/src/crepe/api/Script.hpp
@@ -10,7 +10,7 @@ namespace crepe {
template <typename T>
T & Script::get_component() const {
using namespace std;
- vector<reference_wrapper<T>> all_components = this->get_components<T>();
+ 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()));
@@ -19,10 +19,39 @@ T & Script::get_component() const {
}
template <typename T>
-std::vector<std::reference_wrapper<T>> Script::get_components() const {
- auto & mgr = *this->component_manager_ref;
+RefVector<T> Script::get_components() const {
+ ComponentManager & mgr = this->component_manager;
return mgr.get_components_by_id<T>(this->game_object_id);
}
+template <typename... Args>
+void Script::logf(Args &&... args) {
+ Log::logf(std::forward<Args>(args)...);
+}
+
+template <typename EventType>
+void Script::subscribe_internal(const EventHandler<EventType> & callback,
+ event_channel_t channel) {
+ EventManager & mgr = this->event_manager;
+ subscription_t listener = mgr.subscribe<EventType>(
+ [this, callback](const EventType & data) -> bool {
+ bool & active = this->active;
+ if (!active) return false;
+ return callback(data);
+ },
+ channel);
+ this->listeners.push_back(listener);
+}
+
+template <typename EventType>
+void Script::subscribe(const EventHandler<EventType> & callback, event_channel_t channel) {
+ this->subscribe_internal(callback, channel);
+}
+
+template <typename EventType>
+void Script::subscribe(const EventHandler<EventType> & callback) {
+ this->subscribe_internal(callback, EventManager::CHANNEL_ALL);
+}
+
} // namespace crepe
diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h
index 0192793..74a55d4 100644
--- a/src/crepe/api/Sprite.h
+++ b/src/crepe/api/Sprite.h
@@ -2,8 +2,9 @@
#include <memory>
+#include "../Component.h"
+
#include "Color.h"
-#include "Component.h"
#include "Texture.h"
namespace crepe {
diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp
index de0d0ea..264d7b1 100644
--- a/src/crepe/api/Texture.cpp
+++ b/src/crepe/api/Texture.cpp
@@ -9,14 +9,9 @@
using namespace crepe;
using namespace std;
-Texture::Texture(unique_ptr<Asset> res) {
+Texture::Texture(const Asset & src) {
dbg_trace();
- this->load(std::move(res));
-}
-
-Texture::Texture(const char * src) {
- dbg_trace();
- this->load(make_unique<Asset>(src));
+ this->load(src);
}
Texture::~Texture() {
@@ -24,9 +19,9 @@ Texture::~Texture() {
this->texture.reset();
}
-void Texture::load(unique_ptr<Asset> res) {
+void Texture::load(const Asset & res) {
SDLContext & ctx = SDLContext::get_instance();
- this->texture = std::move(ctx.texture_from_path(res->get_canonical()));
+ this->texture = ctx.texture_from_path(res.get_path());
}
int Texture::get_width() const {
@@ -35,5 +30,5 @@ int Texture::get_width() const {
}
int Texture::get_height() const {
if (this->texture == nullptr) return 0;
- return SDLContext::get_instance().get_width(*this);
+ return SDLContext::get_instance().get_height(*this);
}
diff --git a/src/crepe/api/Texture.h b/src/crepe/api/Texture.h
index 6965223..b4f7d07 100644
--- a/src/crepe/api/Texture.h
+++ b/src/crepe/api/Texture.h
@@ -25,16 +25,10 @@ class Texture {
public:
/**
- * \brief Constructs a Texture from a file path.
- * \param src Path to the image file to be loaded as a texture.
- */
- Texture(const char * src);
-
- /**
* \brief Constructs a Texture from an Asset resource.
- * \param res Unique pointer to an Asset resource containing texture data.
+ * \param src Asset with texture data to load.
*/
- Texture(std::unique_ptr<Asset> res);
+ Texture(const Asset & src);
/**
* \brief Destroys the Texture instance, freeing associated resources.
@@ -59,7 +53,7 @@ private:
* \brief Loads the texture from an Asset resource.
* \param res Unique pointer to an Asset resource to load the texture from.
*/
- void load(std::unique_ptr<Asset> res);
+ void load(const Asset & res);
private:
//! The texture of the class from the library
diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp
index cd944bd..a85b792 100644
--- a/src/crepe/api/Transform.cpp
+++ b/src/crepe/api/Transform.cpp
@@ -4,7 +4,7 @@
using namespace crepe;
-Transform::Transform(game_object_id_t id, const Vector2 & point, double rotation, double scale)
+Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, double scale)
: Component(id),
position(point),
rotation(rotation),
diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h
index 18aa293..6243a93 100644
--- a/src/crepe/api/Transform.h
+++ b/src/crepe/api/Transform.h
@@ -1,8 +1,7 @@
#pragma once
-#include "api/Vector2.h"
-
#include "Component.h"
+#include "types.h"
namespace crepe {
@@ -15,7 +14,7 @@ namespace crepe {
class Transform : public Component {
public:
//! Translation (shift)
- Vector2 position = {0, 0};
+ vec2 position = {0, 0};
//! Rotation, in degrees
double rotation = 0;
//! Multiplication factor
@@ -28,7 +27,7 @@ protected:
* \param rotation The rotation of the GameObject
* \param scale The scale of the GameObject
*/
- Transform(game_object_id_t id, const Vector2 & point, double rotation, double scale);
+ Transform(game_object_id_t id, const vec2 & point, double rotation, double scale);
/**
* There is always exactly one transform component per entity
* \return 1
diff --git a/src/crepe/api/Vector2.cpp b/src/crepe/api/Vector2.cpp
deleted file mode 100644
index 30b968e..0000000
--- a/src/crepe/api/Vector2.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "Vector2.h"
-
-using namespace crepe;
-
-Vector2 Vector2::operator-(const Vector2 & other) const { return {x - other.x, y - other.y}; }
-
-Vector2 Vector2::operator+(const Vector2 & other) const { return {x + other.x, y + other.y}; }
-
-Vector2 Vector2::operator*(double scalar) const { return {x * scalar, y * scalar}; }
-
-Vector2 & Vector2::operator*=(const Vector2 & other) {
- x *= other.x;
- y *= other.y;
- return *this;
-}
-
-Vector2 & Vector2::operator+=(const Vector2 & other) {
- x += other.x;
- y += other.y;
- return *this;
-}
-
-Vector2 & Vector2::operator+=(double other) {
- x += other;
- y += other;
- return *this;
-}
-
-Vector2 Vector2::operator-() const { return {-x, -y}; }
-
-bool Vector2::operator==(const Vector2 & other) const { return x == other.x && y == other.y; }
-
-bool Vector2::operator!=(const Vector2 & other) const { return !(*this == other); }
diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h
index 2fb6136..c278c87 100644
--- a/src/crepe/api/Vector2.h
+++ b/src/crepe/api/Vector2.h
@@ -3,38 +3,71 @@
namespace crepe {
//! 2D vector
+template <class T>
struct Vector2 {
//! X component of the vector
- double x = 0;
+ T x = 0;
//! Y component of the vector
- double y = 0;
+ T y = 0;
//! Subtracts another vector from this vector and returns the result.
- Vector2 operator-(const Vector2 & other) const;
+ Vector2 operator-(const Vector2<T> & other) const;
+
+ //! Subtracts a scalar value from both components of this vector and returns the result.
+ Vector2 operator-(T scalar) const;
//! Adds another vector to this vector and returns the result.
- Vector2 operator+(const Vector2 & other) const;
+ Vector2 operator+(const Vector2<T> & other) const;
+
+ //! Adds a scalar value to both components of this vector and returns the result.
+ Vector2 operator+(T scalar) const;
+
+ //! Multiplies this vector by another vector element-wise and returns the result.
+ Vector2 operator*(const Vector2<T> & other) const;
//! Multiplies this vector by a scalar and returns the result.
- Vector2 operator*(double scalar) const;
+ Vector2 operator*(T scalar) const;
- //! Multiplies this vector by another vector element-wise and updates this vector.
- Vector2 & operator*=(const Vector2 & other);
+ //! Divides this vector by another vector element-wise and returns the result.
+ Vector2 operator/(const Vector2<T> & other) const;
+
+ //! Divides this vector by a scalar and returns the result.
+ Vector2 operator/(T scalar) const;
//! Adds another vector to this vector and updates this vector.
- Vector2 & operator+=(const Vector2 & other);
+ Vector2 & operator+=(const Vector2<T> & other);
//! Adds a scalar value to both components of this vector and updates this vector.
- Vector2 & operator+=(double other);
+ Vector2 & operator+=(T other);
+
+ //! Subtracts another vector from this vector and updates this vector.
+ Vector2 & operator-=(const Vector2<T> & other);
+
+ //! Subtracts a scalar value from both components of this vector and updates this vector.
+ Vector2 & operator-=(T other);
+
+ //! Multiplies this vector by another vector element-wise and updates this vector.
+ Vector2 & operator*=(const Vector2<T> & other);
+
+ //! Multiplies this vector by a scalar and updates this vector.
+ Vector2 & operator*=(T other);
+
+ //! Divides this vector by another vector element-wise and updates this vector.
+ Vector2 & operator/=(const Vector2<T> & other);
+
+ //! Divides this vector by a scalar and updates this vector.
+ Vector2 & operator/=(T other);
//! Returns the negation of this vector.
Vector2 operator-() const;
//! Checks if this vector is equal to another vector.
- bool operator==(const Vector2 & other) const;
+ bool operator==(const Vector2<T> & other) const;
//! Checks if this vector is not equal to another vector.
- bool operator!=(const Vector2 & other) const;
+ bool operator!=(const Vector2<T> & other) const;
};
} // namespace crepe
+
+#include "Vector2.hpp"
diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp
new file mode 100644
index 0000000..cad15f8
--- /dev/null
+++ b/src/crepe/api/Vector2.hpp
@@ -0,0 +1,118 @@
+#pragma once
+
+#include "Vector2.h"
+
+namespace crepe {
+
+template <class T>
+Vector2<T> Vector2<T>::operator-(const Vector2<T> & other) const {
+ return {x - other.x, y - other.y};
+}
+
+template <class T>
+Vector2<T> Vector2<T>::operator-(T scalar) const {
+ return {x - scalar, y - scalar};
+}
+
+template <class T>
+Vector2<T> Vector2<T>::operator+(const Vector2<T> & other) const {
+ return {x + other.x, y + other.y};
+}
+
+template <class T>
+Vector2<T> Vector2<T>::operator+(T scalar) const {
+ return {x + scalar, y + scalar};
+}
+
+template <class T>
+Vector2<T> Vector2<T>::operator*(const Vector2<T> & other) const {
+ return {x * other.x, y * other.y};
+}
+
+template <class T>
+Vector2<T> Vector2<T>::operator*(T scalar) const {
+ return {x * scalar, y * scalar};
+}
+
+template <class T>
+Vector2<T> Vector2<T>::operator/(const Vector2<T> & other) const {
+ return {x / other.x, y / other.y};
+}
+
+template <class T>
+Vector2<T> Vector2<T>::operator/(T scalar) const {
+ return {x / scalar, y / scalar};
+}
+
+template <class T>
+Vector2<T> & Vector2<T>::operator+=(const Vector2<T> & other) {
+ x += other.x;
+ y += other.y;
+ return *this;
+}
+
+template <class T>
+Vector2<T> & Vector2<T>::operator+=(T other) {
+ x += other;
+ y += other;
+ return *this;
+}
+
+template <class T>
+Vector2<T> & Vector2<T>::operator-=(const Vector2<T> & other) {
+ x -= other.x;
+ y -= other.y;
+ return *this;
+}
+
+template <class T>
+Vector2<T> & Vector2<T>::operator-=(T other) {
+ x -= other;
+ y -= other;
+ return *this;
+}
+
+template <class T>
+Vector2<T> & Vector2<T>::operator*=(const Vector2<T> & other) {
+ x *= other.x;
+ y *= other.y;
+ return *this;
+}
+
+template <class T>
+Vector2<T> & Vector2<T>::operator*=(T other) {
+ x *= other;
+ y *= other;
+ return *this;
+}
+
+template <class T>
+Vector2<T> & Vector2<T>::operator/=(const Vector2<T> & other) {
+ x /= other.x;
+ y /= other.y;
+ return *this;
+}
+
+template <class T>
+Vector2<T> & Vector2<T>::operator/=(T other) {
+ x /= other;
+ y /= other;
+ return *this;
+}
+
+template <class T>
+Vector2<T> Vector2<T>::operator-() const {
+ return {-x, -y};
+}
+
+template <class T>
+bool Vector2<T>::operator==(const Vector2<T> & other) const {
+ return x == other.x && y == other.y;
+}
+
+template <class T>
+bool Vector2<T>::operator!=(const Vector2<T> & other) const {
+ return !(*this == other);
+}
+
+} // namespace crepe
diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp
index d5d19dc..95cf606 100644
--- a/src/crepe/facade/DB.cpp
+++ b/src/crepe/facade/DB.cpp
@@ -18,8 +18,8 @@ DB::DB(const string & path) {
this->db = {db, [](libdb::DB * db) { db->close(db, 0); }};
// load or create database file
- ret = this->db->open(this->db.get(), NULL, path.c_str(), NULL, libdb::DB_BTREE, DB_CREATE,
- 0);
+ const char * file = path.empty() ? NULL : path.c_str();
+ ret = this->db->open(this->db.get(), NULL, file, NULL, libdb::DB_BTREE, DB_CREATE, 0);
if (ret != 0) throw runtime_error(format("db->open: {}", libdb::db_strerror(ret)));
// create cursor
diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h
index 629b0eb..115c0f1 100644
--- a/src/crepe/facade/DB.h
+++ b/src/crepe/facade/DB.h
@@ -22,8 +22,10 @@ class DB {
public:
/**
* \param path The path of the database (created if nonexistant)
+ *
+ * \note If \p path is empty, the database is entirely in-memory
*/
- DB(const std::string & path);
+ DB(const std::string & path = "");
virtual ~DB() = default;
public:
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index 3fb7f05..ff7c9f0 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -1,5 +1,6 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
+#include <SDL2/SDL_keycode.h>
#include <SDL2/SDL_rect.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_surface.h>
@@ -7,15 +8,15 @@
#include <cmath>
#include <cstddef>
#include <functional>
-#include <iostream>
#include <memory>
+#include <stdexcept>
#include <string>
#include <array>
+#include "../api/Camera.h"
#include "../api/Sprite.h"
#include "../api/Texture.h"
#include "../api/Transform.h"
-#include "../api/EventManager.h"
#include "../util/Log.h"
@@ -33,29 +34,22 @@ SDLContext::SDLContext() {
dbg_trace();
// FIXME: read window defaults from config manager
- if (SDL_Init(SDL_INIT_VIDEO) < 0) {
- // FIXME: throw exception
- std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
- return;
+ if (SDL_Init(SDL_INIT_VIDEO) != 0) {
+ throw runtime_error(format("SDLContext: SDL_Init error: {}", SDL_GetError()));
}
SDL_Window * tmp_window
= SDL_CreateWindow("Crepe Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
this->viewport.w, this->viewport.h, 0);
if (!tmp_window) {
- // FIXME: throw exception
- std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
- return;
+ throw runtime_error(format("SDLContext: SDL_Window error: {}", SDL_GetError()));
}
this->game_window = {tmp_window, [](SDL_Window * window) { SDL_DestroyWindow(window); }};
SDL_Renderer * tmp_renderer
= SDL_CreateRenderer(this->game_window.get(), -1, SDL_RENDERER_ACCELERATED);
if (!tmp_renderer) {
- // FIXME: throw exception
- std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError()
- << std::endl;
- SDL_DestroyWindow(this->game_window.get());
- return;
+ throw runtime_error(
+ format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError()));
}
this->game_renderer
@@ -63,9 +57,7 @@ SDLContext::SDLContext() {
int img_flags = IMG_INIT_PNG;
if (!(IMG_Init(img_flags) & img_flags)) {
- // FIXME: throw exception
- std::cout << "SDL_image could not initialize! SDL_image Error: " << IMG_GetError()
- << std::endl;
+ throw runtime_error("SDLContext: SDL_image could not initialize!");
}
}
@@ -82,211 +74,67 @@ SDLContext::~SDLContext() {
SDL_Quit();
}
-void SDLContext::handle_events(bool &running) {
- EventManager& event_manager = EventManager::get_instance();
- SDL_Event event;
-
- while (SDL_PollEvent(&event)) {
- switch (event.type) {
- case SDL_QUIT:
- running = false;
- event_manager.trigger_event(ShutDownEvent{});
- break;
- case SDL_KEYDOWN:
- event_manager.trigger_event<KeyPressEvent>(KeyPressEvent{
- .repeat = event.key.repeat,
- .key = this->sdl_to_keycode(event.key.keysym.scancode)
- });
- break;
-
- case SDL_KEYUP:
- event_manager.trigger_event<KeyReleaseEvent>(KeyReleaseEvent{
- .key = this->sdl_to_keycode(event.key.keysym.scancode),
- });
- break;
+void SDLContext::handle_events(bool & running) {
+ //TODO: wouter i need events
+ /*
+ SDL_Event event;
+ SDL_PollEvent(&event);
+ switch (event.type) {
+ case SDL_QUIT:
+ running = false;
+ break;
+ case SDL_KEYDOWN:
+ triggerEvent(KeyPressedEvent(getCustomKey(event.key.keysym.sym)));
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ int x, y;
+ SDL_GetMouseState(&x, &y);
+ triggerEvent(MousePressedEvent(x, y));
+ break;
+ }
+ */
+}
- case SDL_MOUSEBUTTONDOWN:
- {
- int x, y;
- SDL_GetMouseState(&x, &y);
- event_manager.trigger_event<MousePressEvent>(MousePressEvent{
- .mouse_x = x,
- .mouse_y = y,
- .button = this->sdl_to_mousebutton(event.button.button)
- });
- }
- break;
+void SDLContext::clear_screen() { SDL_RenderClear(this->game_renderer.get()); }
+void SDLContext::present_screen() { SDL_RenderPresent(this->game_renderer.get()); }
- case SDL_MOUSEBUTTONUP:
- {
- int x, y;
- SDL_GetMouseState(&x, &y);
- event_manager.trigger_event<MouseReleaseEvent>(MouseReleaseEvent{
- .mouse_x = x,
- .mouse_y = y,
- .button = this->sdl_to_mousebutton(event.button.button)
- });
- }
- break;
+SDL_Rect SDLContext::get_src_rect(const Sprite & sprite) const {
+ return SDL_Rect{
+ .x = sprite.sprite_rect.x,
+ .y = sprite.sprite_rect.y,
+ .w = sprite.sprite_rect.w,
+ .h = sprite.sprite_rect.h,
+ };
+}
+SDL_Rect SDLContext::get_dst_rect(const Sprite & sprite, const vec2 & pos,
+ const double & scale, const Camera & cam) const {
- case SDL_MOUSEMOTION:
- event_manager.trigger_event<MouseMoveEvent>(MouseMoveEvent{
- .mouse_x = event.motion.x,
- .mouse_y = event.motion.y,
- .rel_x = event.motion.xrel,
- .rel_y = event.motion.yrel
- });
- break;
+ double adjusted_w = sprite.sprite_rect.w * scale * cam.zoom;
+ double adjusted_h = sprite.sprite_rect.h * scale * cam.zoom;
+ double adjusted_x = (pos.x - cam.x) * cam.zoom - adjusted_w / 2;
+ double adjusted_y = (pos.y - cam.y) * cam.zoom - adjusted_h / 2;
- case SDL_MOUSEWHEEL:
- event_manager.trigger_event<MouseScrollEvent>(MouseScrollEvent{
- .scroll_x = event.wheel.x,
- .scroll_y = event.wheel.y,
- .direction = (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED ? -1 : 1)
- });
- break;
- }
- }
+ return SDL_Rect{
+ .x = static_cast<int>(adjusted_x),
+ .y = static_cast<int>(adjusted_y),
+ .w = static_cast<int>(adjusted_w),
+ .h = static_cast<int>(adjusted_h),
+ };
}
+void SDLContext::draw_particle(const Sprite & sprite, const vec2 & pos, const double & angle,
+ const double & scale, const Camera & camera) {
+ SDL_RendererFlip render_flip
+ = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x)
+ | (SDL_FLIP_VERTICAL * sprite.flip.flip_y));
-Keycode SDLContext::sdl_to_keycode(SDL_Keycode sdl_key) {
- static const std::array<Keycode, SDL_NUM_SCANCODES> LOOKUP_TABLE = [] {
- std::array<Keycode, SDL_NUM_SCANCODES> table{};
- table.fill(Keycode::NONE);
-
- table[SDL_SCANCODE_SPACE] = Keycode::SPACE;
- table[SDL_SCANCODE_APOSTROPHE] = Keycode::APOSTROPHE;
- table[SDL_SCANCODE_COMMA] = Keycode::COMMA;
- table[SDL_SCANCODE_MINUS] = Keycode::MINUS;
- table[SDL_SCANCODE_PERIOD] = Keycode::PERIOD;
- table[SDL_SCANCODE_SLASH] = Keycode::SLASH;
- table[SDL_SCANCODE_0] = Keycode::D0;
- table[SDL_SCANCODE_1] = Keycode::D1;
- table[SDL_SCANCODE_2] = Keycode::D2;
- table[SDL_SCANCODE_3] = Keycode::D3;
- table[SDL_SCANCODE_4] = Keycode::D4;
- table[SDL_SCANCODE_5] = Keycode::D5;
- table[SDL_SCANCODE_6] = Keycode::D6;
- table[SDL_SCANCODE_7] = Keycode::D7;
- table[SDL_SCANCODE_8] = Keycode::D8;
- table[SDL_SCANCODE_9] = Keycode::D9;
- table[SDL_SCANCODE_SEMICOLON] = Keycode::SEMICOLON;
- table[SDL_SCANCODE_EQUALS] = Keycode::EQUAL;
- table[SDL_SCANCODE_A] = Keycode::A;
- table[SDL_SCANCODE_B] = Keycode::B;
- table[SDL_SCANCODE_C] = Keycode::C;
- table[SDL_SCANCODE_D] = Keycode::D;
- table[SDL_SCANCODE_E] = Keycode::E;
- table[SDL_SCANCODE_F] = Keycode::F;
- table[SDL_SCANCODE_G] = Keycode::G;
- table[SDL_SCANCODE_H] = Keycode::H;
- table[SDL_SCANCODE_I] = Keycode::I;
- table[SDL_SCANCODE_J] = Keycode::J;
- table[SDL_SCANCODE_K] = Keycode::K;
- table[SDL_SCANCODE_L] = Keycode::L;
- table[SDL_SCANCODE_M] = Keycode::M;
- table[SDL_SCANCODE_N] = Keycode::N;
- table[SDL_SCANCODE_O] = Keycode::O;
- table[SDL_SCANCODE_P] = Keycode::P;
- table[SDL_SCANCODE_Q] = Keycode::Q;
- table[SDL_SCANCODE_R] = Keycode::R;
- table[SDL_SCANCODE_S] = Keycode::S;
- table[SDL_SCANCODE_T] = Keycode::T;
- table[SDL_SCANCODE_U] = Keycode::U;
- table[SDL_SCANCODE_V] = Keycode::V;
- table[SDL_SCANCODE_W] = Keycode::W;
- table[SDL_SCANCODE_X] = Keycode::X;
- table[SDL_SCANCODE_Y] = Keycode::Y;
- table[SDL_SCANCODE_Z] = Keycode::Z;
- table[SDL_SCANCODE_LEFTBRACKET] = Keycode::LEFT_BRACKET;
- table[SDL_SCANCODE_BACKSLASH] = Keycode::BACKSLASH;
- table[SDL_SCANCODE_RIGHTBRACKET] = Keycode::RIGHT_BRACKET;
- table[SDL_SCANCODE_GRAVE] = Keycode::GRAVE_ACCENT;
- table[SDL_SCANCODE_ESCAPE] = Keycode::ESCAPE;
- table[SDL_SCANCODE_RETURN] = Keycode::ENTER;
- table[SDL_SCANCODE_TAB] = Keycode::TAB;
- table[SDL_SCANCODE_BACKSPACE] = Keycode::BACKSPACE;
- table[SDL_SCANCODE_INSERT] = Keycode::INSERT;
- table[SDL_SCANCODE_DELETE] = Keycode::DELETE;
- table[SDL_SCANCODE_RIGHT] = Keycode::RIGHT;
- table[SDL_SCANCODE_LEFT] = Keycode::LEFT;
- table[SDL_SCANCODE_DOWN] = Keycode::DOWN;
- table[SDL_SCANCODE_UP] = Keycode::UP;
- table[SDL_SCANCODE_PAGEUP] = Keycode::PAGE_UP;
- table[SDL_SCANCODE_PAGEDOWN] = Keycode::PAGE_DOWN;
- table[SDL_SCANCODE_HOME] = Keycode::HOME;
- table[SDL_SCANCODE_END] = Keycode::END;
- table[SDL_SCANCODE_CAPSLOCK] = Keycode::CAPS_LOCK;
- table[SDL_SCANCODE_SCROLLLOCK] = Keycode::SCROLL_LOCK;
- table[SDL_SCANCODE_NUMLOCKCLEAR] = Keycode::NUM_LOCK;
- table[SDL_SCANCODE_PRINTSCREEN] = Keycode::PRINT_SCREEN;
- table[SDL_SCANCODE_PAUSE] = Keycode::PAUSE;
- table[SDL_SCANCODE_F1] = Keycode::F1;
- table[SDL_SCANCODE_F2] = Keycode::F2;
- table[SDL_SCANCODE_F3] = Keycode::F3;
- table[SDL_SCANCODE_F4] = Keycode::F4;
- table[SDL_SCANCODE_F5] = Keycode::F5;
- table[SDL_SCANCODE_F6] = Keycode::F6;
- table[SDL_SCANCODE_F7] = Keycode::F7;
- table[SDL_SCANCODE_F8] = Keycode::F8;
- table[SDL_SCANCODE_F9] = Keycode::F9;
- table[SDL_SCANCODE_F10] = Keycode::F10;
- table[SDL_SCANCODE_F11] = Keycode::F11;
- table[SDL_SCANCODE_F12] = Keycode::F12;
- table[SDL_SCANCODE_KP_0] = Keycode::KP0;
- table[SDL_SCANCODE_KP_1] = Keycode::KP1;
- table[SDL_SCANCODE_KP_2] = Keycode::KP2;
- table[SDL_SCANCODE_KP_3] = Keycode::KP3;
- table[SDL_SCANCODE_KP_4] = Keycode::KP4;
- table[SDL_SCANCODE_KP_5] = Keycode::KP5;
- table[SDL_SCANCODE_KP_6] = Keycode::KP6;
- table[SDL_SCANCODE_KP_7] = Keycode::KP7;
- table[SDL_SCANCODE_KP_8] = Keycode::KP8;
- table[SDL_SCANCODE_KP_9] = Keycode::KP9;
- table[SDL_SCANCODE_LSHIFT] = Keycode::LEFT_SHIFT;
- table[SDL_SCANCODE_LCTRL] = Keycode::LEFT_CONTROL;
- table[SDL_SCANCODE_LALT] = Keycode::LEFT_ALT;
- table[SDL_SCANCODE_LGUI] = Keycode::LEFT_SUPER;
- table[SDL_SCANCODE_RSHIFT] = Keycode::RIGHT_SHIFT;
- table[SDL_SCANCODE_RCTRL] = Keycode::RIGHT_CONTROL;
- table[SDL_SCANCODE_RALT] = Keycode::RIGHT_ALT;
- table[SDL_SCANCODE_RGUI] = Keycode::RIGHT_SUPER;
- table[SDL_SCANCODE_MENU] = Keycode::MENU;
-
- return table;
- }();
-
- if (sdl_key < 0 || sdl_key >= SDL_NUM_SCANCODES) {
- return Keycode::NONE;
- }
-
- return LOOKUP_TABLE[sdl_key];
-}
-MouseButton SDLContext::sdl_to_mousebutton(Uint8 sdl_button) {
- static const std::array<MouseButton, 8> MOUSE_BUTTON_LOOKUP_TABLE = [] {
- std::array<MouseButton, 8> table{};
- table.fill(MouseButton::NONE);
-
- table[SDL_BUTTON_LEFT] = MouseButton::LEFT_MOUSE;
- table[SDL_BUTTON_RIGHT] = MouseButton::RIGHT_MOUSE;
- table[SDL_BUTTON_MIDDLE] = MouseButton::MIDDLE_MOUSE;
- table[SDL_BUTTON_X1] = MouseButton::X1_MOUSE;
- table[SDL_BUTTON_X2] = MouseButton::X2_MOUSE;
-
- return table;
- }();
-
- if (sdl_button >= MOUSE_BUTTON_LOOKUP_TABLE.size()) {
- // Return NONE for invalid or unmapped button
- return MouseButton::NONE;
- }
+ SDL_Rect srcrect = this->get_src_rect(sprite);
+ SDL_Rect dstrect = this->get_dst_rect(sprite, pos, scale, camera);
- return MOUSE_BUTTON_LOOKUP_TABLE[sdl_button];
+ SDL_RenderCopyEx(this->game_renderer.get(), sprite.sprite_image->texture.get(), &srcrect,
+ &dstrect, angle, NULL, render_flip);
}
-void SDLContext::clear_screen() { SDL_RenderClear(this->game_renderer.get()); }
-void SDLContext::present_screen() { SDL_RenderPresent(this->game_renderer.get()); }
void SDLContext::draw(const Sprite & sprite, const Transform & transform, const Camera & cam) {
@@ -294,34 +142,18 @@ void SDLContext::draw(const Sprite & sprite, const Transform & transform, const
= (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x)
| (SDL_FLIP_VERTICAL * sprite.flip.flip_y));
- double adjusted_x = (transform.position.x - cam.x) * cam.zoom;
- double adjusted_y = (transform.position.y - cam.y) * cam.zoom;
- double adjusted_w = sprite.sprite_rect.w * transform.scale * cam.zoom;
- double adjusted_h = sprite.sprite_rect.h * transform.scale * cam.zoom;
-
- SDL_Rect srcrect = {
- .x = sprite.sprite_rect.x,
- .y = sprite.sprite_rect.y,
- .w = sprite.sprite_rect.w,
- .h = sprite.sprite_rect.h,
- };
-
- SDL_Rect dstrect = {
- .x = static_cast<int>(adjusted_x),
- .y = static_cast<int>(adjusted_y),
- .w = static_cast<int>(adjusted_w),
- .h = static_cast<int>(adjusted_h),
- };
+ SDL_Rect srcrect = this->get_src_rect(sprite);
+ SDL_Rect dstrect = this->get_dst_rect(sprite, transform.position, transform.scale, cam);
SDL_RenderCopyEx(this->game_renderer.get(), sprite.sprite_image->texture.get(), &srcrect,
&dstrect, transform.rotation, NULL, render_flip);
}
-void SDLContext::camera(const Camera & cam) {
+void SDLContext::set_camera(const Camera & cam) {
this->viewport.w = static_cast<int>(cam.aspect_width);
this->viewport.h = static_cast<int>(cam.aspect_height);
- this->viewport.x = static_cast<int>(cam.x) - (SCREEN_WIDTH / 2);
- this->viewport.y = static_cast<int>(cam.y) - (SCREEN_HEIGHT / 2);
+ this->viewport.x = static_cast<int>(cam.x) - (this->viewport.w / 2);
+ this->viewport.y = static_cast<int>(cam.y) - (this->viewport.h / 2);
SDL_SetRenderDrawColor(this->game_renderer.get(), cam.bg_color.r, cam.bg_color.g,
cam.bg_color.b, cam.bg_color.a);
@@ -333,9 +165,8 @@ std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>>
SDLContext::texture_from_path(const std::string & path) {
SDL_Surface * tmp = IMG_Load(path.c_str());
- if (tmp == nullptr) {
- tmp = IMG_Load("../asset/texture/ERROR.png");
- }
+ if (tmp == nullptr)
+ throw runtime_error(format("SDLContext: IMG_Load error: {}", SDL_GetError()));
std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> img_surface;
img_surface = {tmp, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }};
@@ -344,7 +175,7 @@ SDLContext::texture_from_path(const std::string & path) {
= SDL_CreateTextureFromSurface(this->game_renderer.get(), img_surface.get());
if (tmp_texture == nullptr) {
- throw runtime_error(format("Texture cannot be load from {}", path));
+ throw runtime_error(format("SDLContext: Texture cannot be load from {}", path));
}
std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> img_texture;
diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h
index dcd7440..5b6b093 100644
--- a/src/crepe/facade/SDLContext.h
+++ b/src/crepe/facade/SDLContext.h
@@ -1,8 +1,10 @@
#pragma once
#include <SDL2/SDL.h>
#include <SDL2/SDL_keycode.h>
+#include <SDL2/SDL_rect.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_video.h>
+#include <cmath>
#include <functional>
#include <memory>
#include <string>
@@ -14,17 +16,13 @@
#include "../api/Event.h"
#include "api/Camera.h"
-// FIXME: this needs to be removed
-const int SCREEN_WIDTH = 640;
-const int SCREEN_HEIGHT = 480;
+#include "types.h"
namespace crepe {
// TODO: SDL_Keycode is defined in a header not distributed with crepe, which means this
// typedef is unusable when crepe is packaged. Wouter will fix this later.
//typedef SDL_Keycode CREPE_KEYCODES;
-
-class Texture;
class LoopManager;
class InputSystem;
/**
@@ -119,9 +117,6 @@ private:
//! Will use the funtions: texture_from_path, get_width,get_height.
friend class Texture;
- //! Will use the funtions: texture_from_path, get_width,get_height.
- friend class Animator;
-
/**
* \brief Loads a texture from a file path.
* \param path Path to the image file.
@@ -155,6 +150,9 @@ private:
*/
void draw(const Sprite & sprite, const Transform & transform, const Camera & camera);
+ void draw_particle(const Sprite & sprite, const vec2 & pos, const double & angle,
+ const double & scale, const Camera & camera);
+
//! Clears the screen, preparing for a new frame.
void clear_screen();
@@ -162,10 +160,31 @@ private:
void present_screen();
/**
- * \brief Sets the current camera for rendering.
+ * \brief sets the background of the camera (will be adjusted in future PR)
* \param camera Reference to the Camera object.
*/
- void camera(const Camera & camera);
+ void set_camera(const Camera & camera);
+
+private:
+ /**
+ * \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 an destination
+ *
+ * \param sprite Reference to the sprite to calculate the rectangle
+ * \param pos the pos in pixel positions
+ * \param scale the multiplier to increase of decrease for the specified sprite
+ * \param cam Reference to the current camera in the scene to calculate the position based
+ * on the camera
+ * \return sdl rectangle to draw a dst image to draw on the screen
+ */
+ SDL_Rect get_dst_rect(const Sprite & sprite, const vec2 & pos, const double & scale,
+ const Camera & cam) const;
private:
//! sdl Window
diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp
index 7aa89a9..4d3abf5 100644
--- a/src/crepe/facade/Sound.cpp
+++ b/src/crepe/facade/Sound.cpp
@@ -16,7 +16,7 @@ Sound::Sound(const char * src) {
this->load(make_unique<Asset>(src));
}
-void Sound::load(unique_ptr<Asset> res) { this->sample.load(res->get_canonical().c_str()); }
+void Sound::load(unique_ptr<Asset> res) { this->sample.load(res->get_path().c_str()); }
void Sound::play() {
SoundContext & ctx = SoundContext::get_instance();
diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h
index 32b6478..4c68f32 100644
--- a/src/crepe/facade/Sound.h
+++ b/src/crepe/facade/Sound.h
@@ -4,7 +4,7 @@
#include <soloud/soloud.h>
#include <soloud/soloud_wav.h>
-#include "../Asset.h"
+#include "../api/Asset.h"
namespace crepe {
diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp
index 9d18873..676e485 100644
--- a/src/crepe/system/AnimatorSystem.cpp
+++ b/src/crepe/system/AnimatorSystem.cpp
@@ -1,6 +1,4 @@
#include <cstdint>
-#include <functional>
-#include <vector>
#include "api/Animator.h"
#include "facade/SDLContext.h"
@@ -13,8 +11,7 @@ using namespace crepe;
void AnimatorSystem::update() {
ComponentManager & mgr = this->component_manager;
- std::vector<std::reference_wrapper<Animator>> animations
- = mgr.get_components_by_type<Animator>();
+ RefVector<Animator> animations = mgr.get_components_by_type<Animator>();
uint64_t tick = SDLContext::get_instance().get_ticks();
for (Animator & a : animations) {
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index 7316309..0e62a57 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -4,7 +4,6 @@
#include "api/ParticleEmitter.h"
#include "api/Transform.h"
-#include "api/Vector2.h"
#include "ComponentManager.h"
#include "ParticleSystem.h"
@@ -14,8 +13,7 @@ using namespace crepe;
void ParticleSystem::update() {
// Get all emitters
ComponentManager & mgr = this->component_manager;
- std::vector<std::reference_wrapper<ParticleEmitter>> emitters
- = mgr.get_components_by_type<ParticleEmitter>();
+ RefVector<ParticleEmitter> emitters = mgr.get_components_by_type<ParticleEmitter>();
for (ParticleEmitter & emitter : emitters) {
// Get transform linked to emitter
@@ -43,17 +41,15 @@ void ParticleSystem::update() {
}
void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & transform) {
- constexpr double DEG_TO_RAD = M_PI / 180.0;
+ constexpr float DEG_TO_RAD = M_PI / 180.0;
- Vector2 initial_position = emitter.data.position + transform.position;
- double random_angle
- = generate_random_angle(emitter.data.min_angle, emitter.data.max_angle);
+ vec2 initial_position = emitter.data.position + transform.position;
+ float random_angle = generate_random_angle(emitter.data.min_angle, emitter.data.max_angle);
- double random_speed
- = generate_random_speed(emitter.data.min_speed, emitter.data.max_speed);
- double angle_radians = random_angle * DEG_TO_RAD;
+ float random_speed = generate_random_speed(emitter.data.min_speed, emitter.data.max_speed);
+ float angle_radians = random_angle * DEG_TO_RAD;
- Vector2 velocity
+ vec2 velocity
= {random_speed * std::cos(angle_radians), random_speed * std::sin(angle_radians)};
for (Particle & particle : emitter.data.particles) {
@@ -78,7 +74,7 @@ int ParticleSystem::calculate_update(int count, double emission) const {
}
void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform & transform) {
- Vector2 offset = emitter.data.boundary.offset + transform.position + emitter.data.position;
+ vec2 offset = emitter.data.boundary.offset + transform.position + emitter.data.position;
double half_width = emitter.data.boundary.width / 2.0;
double half_height = emitter.data.boundary.height / 2.0;
@@ -88,7 +84,7 @@ void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform & t
const double BOTTOM = offset.y + half_height;
for (Particle & particle : emitter.data.particles) {
- const Vector2 & position = particle.position;
+ const vec2 & position = particle.position;
bool within_bounds = (position.x >= LEFT && position.x <= RIGHT && position.y >= TOP
&& position.y <= BOTTOM);
diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp
index 4a7dbfb..514a4b3 100644
--- a/src/crepe/system/PhysicsSystem.cpp
+++ b/src/crepe/system/PhysicsSystem.cpp
@@ -12,10 +12,8 @@ using namespace crepe;
void PhysicsSystem::update() {
ComponentManager & mgr = this->component_manager;
- std::vector<std::reference_wrapper<Rigidbody>> rigidbodies
- = mgr.get_components_by_type<Rigidbody>();
- std::vector<std::reference_wrapper<Transform>> transforms
- = mgr.get_components_by_type<Transform>();
+ RefVector<Rigidbody> rigidbodies = mgr.get_components_by_type<Rigidbody>();
+ RefVector<Transform> transforms = mgr.get_components_by_type<Transform>();
double gravity = Config::get_instance().physics.gravity;
for (Rigidbody & rigidbody : rigidbodies) {
@@ -36,7 +34,7 @@ void PhysicsSystem::update() {
if (rigidbody.data.angular_damping != 0) {
rigidbody.data.angular_velocity *= rigidbody.data.angular_damping;
}
- if (rigidbody.data.linear_damping != Vector2{0, 0}) {
+ if (rigidbody.data.linear_damping != vec2{0, 0}) {
rigidbody.data.linear_velocity *= rigidbody.data.linear_damping;
}
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index fa3d0de..ad510f5 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -1,44 +1,102 @@
+#include <algorithm>
+#include <cassert>
+#include <cmath>
#include <functional>
+#include <iostream>
+#include <stdexcept>
#include <vector>
#include "../ComponentManager.h"
+#include "../api/ParticleEmitter.h"
#include "../api/Sprite.h"
#include "../api/Transform.h"
+#include "../api/Vector2.h"
#include "../facade/SDLContext.h"
-#include "../util/Log.h"
#include "RenderSystem.h"
using namespace crepe;
+using namespace std;
-void RenderSystem::clear_screen() const { SDLContext::get_instance().clear_screen(); }
+void RenderSystem::clear_screen() { this->context.clear_screen(); }
-void RenderSystem::present_screen() const { SDLContext::get_instance().present_screen(); }
+void RenderSystem::present_screen() { this->context.present_screen(); }
void RenderSystem::update_camera() {
ComponentManager & mgr = this->component_manager;
- std::vector<std::reference_wrapper<Camera>> cameras = mgr.get_components_by_type<Camera>();
+ RefVector<Camera> cameras = mgr.get_components_by_type<Camera>();
+
+ if (cameras.size() == 0) throw std::runtime_error("No cameras in current scene");
for (Camera & cam : cameras) {
- SDLContext::get_instance().camera(cam);
- this->curr_cam = &cam;
+ if (!cam.active) continue;
+ this->context.set_camera(cam);
+ this->curr_cam_ref = &cam;
}
}
-void RenderSystem::render_sprites() const {
- ComponentManager & mgr = this->component_manager;
- std::vector<std::reference_wrapper<Sprite>> sprites = mgr.get_components_by_type<Sprite>();
+bool sorting_comparison(const Sprite & a, const Sprite & b) {
+ if (a.sorting_in_layer < b.sorting_in_layer) return true;
+ if (a.sorting_in_layer == b.sorting_in_layer) return a.order_in_layer < b.order_in_layer;
- SDLContext & render = SDLContext::get_instance();
- for (const Sprite & sprite : sprites) {
- auto transforms = mgr.get_components_by_id<Transform>(sprite.game_object_id);
- render.draw(sprite, transforms[0], *curr_cam);
- }
+ return false;
+}
+
+RefVector<Sprite> RenderSystem::sort(RefVector<Sprite> & objs) const {
+ RefVector<Sprite> sorted_objs(objs);
+ std::sort(sorted_objs.begin(), sorted_objs.end(), sorting_comparison);
+
+ return sorted_objs;
}
void RenderSystem::update() {
this->clear_screen();
this->update_camera();
- this->render_sprites();
+ this->render();
this->present_screen();
}
+
+bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) {
+
+ ComponentManager & mgr = this->component_manager;
+
+ vector<reference_wrapper<ParticleEmitter>> emitters
+ = mgr.get_components_by_id<ParticleEmitter>(sprite.game_object_id);
+
+ bool rendering_particles = false;
+
+ for (const ParticleEmitter & em : emitters) {
+ if (!(&em.data.sprite == &sprite)) continue;
+ rendering_particles = true;
+ if (!em.active) continue;
+
+ for (const Particle & p : em.data.particles) {
+ if (!p.active) continue;
+ this->context.draw_particle(sprite, p.position, p.angle, scale,
+ *this->curr_cam_ref);
+ }
+ }
+ return rendering_particles;
+}
+void RenderSystem::render_normal(const Sprite & sprite, const Transform & tm) {
+ this->context.draw(sprite, tm, *this->curr_cam_ref);
+}
+
+void RenderSystem::render() {
+
+ ComponentManager & mgr = this->component_manager;
+ RefVector<Sprite> sprites = mgr.get_components_by_type<Sprite>();
+ RefVector<Sprite> sorted_sprites = this->sort(sprites);
+
+ 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);
+
+ if (rendered_particles) continue;
+
+ this->render_normal(sprite, transform);
+ }
+}
diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h
index 87ec494..30b41cf 100644
--- a/src/crepe/system/RenderSystem.h
+++ b/src/crepe/system/RenderSystem.h
@@ -1,18 +1,24 @@
#pragma once
-#include "api/Camera.h"
+#include <functional>
+#include <vector>
+
+#include "facade/SDLContext.h"
#include "System.h"
+#include <cmath>
namespace crepe {
+class Camera;
+class Sprite;
+
/**
* \class RenderSystem
* \brief Manages rendering operations for all game objects.
*
- * RenderSystem is responsible for rendering sprites, clearing and presenting the screen, and
- * managing the active camera. It functions as a singleton, providing centralized rendering
- * services for the application.
+ * RenderSystem is responsible for rendering, clearing and presenting the screen, and
+ * managing the active camera.
*/
class RenderSystem : public System {
public:
@@ -25,20 +31,44 @@ public:
private:
//! Clears the screen in preparation for rendering.
- void clear_screen() const;
+ void clear_screen();
//! Presents the rendered frame to the display.
- void present_screen() const;
+ void present_screen();
//! Updates the active camera used for rendering.
void update_camera();
- //! Renders all active sprites to the screen.
- void render_sprites() const;
+ //! Renders the whole screen
+ void render();
+
+ /**
+ * \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
+ * \return true if particles have been rendered
+ */
+ bool render_particle(const Sprite & sprite, const double & scale);
+
+ /**
+ * \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
+ */
+ void render_normal(const Sprite & sprite, const Transform & tm);
+
+ /**
+ * \brief sort a vector sprite objects with
+ *
+ * \param objs the vector that will do a sorting algorithm on
+ * \return returns a sorted reference vector
+ */
+ RefVector<Sprite> sort(RefVector<Sprite> & objs) const;
/**
* \todo Include color handling for sprites.
- * \todo Implement particle emitter rendering with sprites.
* \todo Add text rendering using SDL_ttf for text components.
* \todo Implement a text component and a button component.
* \todo Ensure each sprite is checked for active status before rendering.
@@ -48,8 +78,10 @@ private:
private:
//! Pointer to the current active camera for rendering
- Camera * curr_cam = nullptr;
+ Camera * curr_cam_ref = nullptr;
// TODO: needs a better solution
+
+ SDLContext & context = SDLContext::get_instance();
};
} // namespace crepe
diff --git a/src/crepe/system/ScriptSystem.cpp b/src/crepe/system/ScriptSystem.cpp
index c4d724c..20a83f7 100644
--- a/src/crepe/system/ScriptSystem.cpp
+++ b/src/crepe/system/ScriptSystem.cpp
@@ -1,7 +1,3 @@
-#include <forward_list>
-#include <functional>
-#include <vector>
-
#include "../ComponentManager.h"
#include "../api/BehaviorScript.h"
#include "../api/Script.h"
@@ -14,31 +10,19 @@ using namespace crepe;
void ScriptSystem::update() {
dbg_trace();
- forward_list<reference_wrapper<Script>> scripts = this->get_scripts();
-
- for (auto & script_ref : scripts) {
- Script & script = script_ref.get();
- if (!script.initialized) {
- script.init();
- script.initialized = true;
- }
- script.update();
- }
-}
-
-forward_list<reference_wrapper<Script>> ScriptSystem::get_scripts() const {
- forward_list<reference_wrapper<Script>> scripts = {};
ComponentManager & mgr = this->component_manager;
- vector<reference_wrapper<BehaviorScript>> behavior_scripts
- = mgr.get_components_by_type<BehaviorScript>();
+ RefVector<BehaviorScript> behavior_scripts = mgr.get_components_by_type<BehaviorScript>();
- for (auto behavior_script_ref : behavior_scripts) {
- BehaviorScript & behavior_script = behavior_script_ref.get();
+ for (BehaviorScript & behavior_script : behavior_scripts) {
if (!behavior_script.active) continue;
+
Script * script = behavior_script.script.get();
if (script == nullptr) continue;
- scripts.push_front(*script);
- }
- return scripts;
+ if (!script->initialized) {
+ script->init();
+ script->initialized = true;
+ }
+ script->update();
+ }
}
diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h
index deb89cb..936e9ca 100644
--- a/src/crepe/system/ScriptSystem.h
+++ b/src/crepe/system/ScriptSystem.h
@@ -1,7 +1,5 @@
#pragma once
-#include <forward_list>
-
#include "System.h"
namespace crepe {
@@ -25,15 +23,6 @@ public:
* the \c BehaviorScript instance.
*/
void update() override;
-
-private:
- /**
- * \brief Aggregate all active \c BehaviorScript components and return a list
- * of references to their \c Script instances (utility)
- *
- * \returns List of active \c Script instances
- */
- std::forward_list<std::reference_wrapper<Script>> get_scripts() const;
};
} // namespace crepe
diff --git a/src/crepe/types.h b/src/crepe/types.h
index 0d459e8..17f1619 100644
--- a/src/crepe/types.h
+++ b/src/crepe/types.h
@@ -1,9 +1,30 @@
#pragma once
+#include "api/Vector2.h"
+
#include <cstdint>
+#include <functional>
+#include <vector>
namespace crepe {
+//! GameObject ID
typedef uint32_t game_object_id_t;
-}
+//! vector of reference_wrapper
+template <typename T>
+using RefVector = std::vector<std::reference_wrapper<T>>;
+
+//! Default Vector2<int> type
+typedef Vector2<int> ivec2;
+
+//! Default Vector2<unsigned int> type
+typedef Vector2<unsigned int> uvec2;
+
+//! Default Vector2<float> type
+typedef Vector2<float> vec2;
+
+//! Default Vector2<double> type
+typedef Vector2<double> dvec2;
+
+} // namespace crepe
diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt
index 4be738a..94ed906 100644
--- a/src/crepe/util/CMakeLists.txt
+++ b/src/crepe/util/CMakeLists.txt
@@ -9,5 +9,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
Log.hpp
Proxy.h
Proxy.hpp
+ OptionalRef.h
+ OptionalRef.hpp
)
diff --git a/src/crepe/util/Log.h b/src/crepe/util/Log.h
index d55b11e..fc0bb3a 100644
--- a/src/crepe/util/Log.h
+++ b/src/crepe/util/Log.h
@@ -34,11 +34,11 @@ class Log {
public:
//! Log message severity
enum Level {
- TRACE, //< Include (internal) function calls
- DEBUG, //< Include dbg_logf output
- INFO, //< General-purpose messages
- WARNING, //< Non-fatal errors
- ERROR, //< Fatal errors
+ TRACE, //!< Include (internal) function calls
+ DEBUG, //!< Include dbg_logf output
+ INFO, //!< General-purpose messages
+ WARNING, //!< Non-fatal errors
+ ERROR, //!< Fatal errors
};
/**
diff --git a/src/crepe/util/OptionalRef.h b/src/crepe/util/OptionalRef.h
new file mode 100644
index 0000000..3201667
--- /dev/null
+++ b/src/crepe/util/OptionalRef.h
@@ -0,0 +1,58 @@
+#pragma once
+
+namespace crepe {
+
+/**
+ * \brief Optional reference utility
+ *
+ * This class doesn't need to know the full definition of \c T to be used.
+ *
+ * \tparam T Value type
+ */
+template <typename T>
+class OptionalRef {
+public:
+ //! Initialize empty (nonexistant) reference
+ OptionalRef() = default;
+ //! Initialize reference with value
+ OptionalRef(T & ref);
+ /**
+ * \brief Assign new reference
+ *
+ * \param ref Reference to assign
+ *
+ * \return Reference to this (required for operator)
+ */
+ OptionalRef<T> & operator=(T & ref);
+ /**
+ * \brief Retrieve this reference
+ *
+ * \returns Internal reference if it is set
+ *
+ * \throws std::runtime_error if this function is called while the reference it not set
+ */
+ operator T &() const;
+ /**
+ * \brief Check if this reference is not empty
+ *
+ * \returns `true` if reference is set, or `false` if it is not
+ */
+ explicit operator bool() const noexcept;
+
+ /**
+ * \brief Make this reference empty
+ */
+ void clear() noexcept;
+
+private:
+ /**
+ * \brief Reference to the value of type \c T
+ *
+ * \note This raw pointer is *not* managed, and only used as a reference!
+ */
+ T * ref = nullptr;
+};
+
+} // namespace crepe
+
+#include "OptionalRef.hpp"
diff --git a/src/crepe/util/OptionalRef.hpp b/src/crepe/util/OptionalRef.hpp
new file mode 100644
index 0000000..4608c9e
--- /dev/null
+++ b/src/crepe/util/OptionalRef.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <stdexcept>
+
+#include "OptionalRef.h"
+
+namespace crepe {
+
+template <typename T>
+OptionalRef<T>::OptionalRef(T & ref) {
+ this->ref = &ref;
+}
+
+template <typename T>
+OptionalRef<T>::operator T &() 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;
+}
+
+template <typename T>
+OptionalRef<T>::operator bool() const noexcept {
+ return this->ref != nullptr;
+}
+
+template <typename T>
+void OptionalRef<T>::clear() noexcept {
+ this->ref = nullptr;
+}
+
+} // namespace crepe
diff --git a/src/crepe/util/Proxy.h b/src/crepe/util/Proxy.h
index b34f7c6..789144a 100644
--- a/src/crepe/util/Proxy.h
+++ b/src/crepe/util/Proxy.h
@@ -16,6 +16,8 @@ template <typename T>
class Proxy {
public:
//! Set operator
+ Proxy & operator=(Proxy &);
+ //! Set operator
Proxy & operator=(const T &);
//! Get operator
operator const T &();
diff --git a/src/crepe/util/Proxy.hpp b/src/crepe/util/Proxy.hpp
index b9923db..ef2b69f 100644
--- a/src/crepe/util/Proxy.hpp
+++ b/src/crepe/util/Proxy.hpp
@@ -14,6 +14,12 @@ Proxy<T> & Proxy<T>::operator=(const T & val) {
}
template <typename T>
+Proxy<T> & Proxy<T>::operator=(Proxy & proxy) {
+ this->broker.set(T(proxy));
+ return *this;
+}
+
+template <typename T>
Proxy<T>::operator const T &() {
return this->broker.get();
}