aboutsummaryrefslogtreecommitdiff
path: root/src/crepe
diff options
context:
space:
mode:
authorheavydemon21 <nielsstunnebrink1@gmail.com>2024-11-21 20:35:09 +0100
committerheavydemon21 <nielsstunnebrink1@gmail.com>2024-11-21 20:35:09 +0100
commitad0dcad1f11d698abf71bf69fb0927c26298d253 (patch)
tree3e8a501a84682ca2b6c085a987666c9b777bc1c7 /src/crepe
parentbdc81e355e5bee5d2a3e29346ba08f7bc55196ca (diff)
parent115d6f50152dc018073345800ca90b85846ebaa9 (diff)
Merge branch 'master' into niels/decoupling_pixel_and_pos
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.h5
-rw-r--r--src/crepe/ComponentManager.hpp9
-rw-r--r--src/crepe/api/Asset.cpp54
-rw-r--r--src/crepe/api/Asset.h84
-rw-r--r--src/crepe/api/CMakeLists.txt13
-rw-r--r--src/crepe/api/Color.cpp37
-rw-r--r--src/crepe/api/Color.h49
-rw-r--r--src/crepe/api/Config.h14
-rw-r--r--src/crepe/api/Event.h112
-rw-r--r--src/crepe/api/EventHandler.cpp5
-rw-r--r--src/crepe/api/EventHandler.h96
-rw-r--r--src/crepe/api/EventHandler.hpp18
-rw-r--r--src/crepe/api/EventManager.cpp46
-rw-r--r--src/crepe/api/EventManager.h161
-rw-r--r--src/crepe/api/EventManager.hpp36
-rw-r--r--src/crepe/api/GameObject.cpp6
-rw-r--r--src/crepe/api/IKeyListener.cpp19
-rw-r--r--src/crepe/api/IKeyListener.h49
-rw-r--r--src/crepe/api/IMouseListener.cpp29
-rw-r--r--src/crepe/api/IMouseListener.h72
-rw-r--r--src/crepe/api/KeyCodes.h153
-rw-r--r--src/crepe/api/LoopManager.h18
-rw-r--r--src/crepe/api/LoopManager.hpp5
-rw-r--r--src/crepe/api/Scene.cpp4
-rw-r--r--src/crepe/api/Scene.h21
-rw-r--r--src/crepe/api/SceneManager.cpp2
-rw-r--r--src/crepe/api/SceneManager.h14
-rw-r--r--src/crepe/api/SceneManager.hpp6
-rw-r--r--src/crepe/api/Script.h2
-rw-r--r--src/crepe/api/Script.hpp5
-rw-r--r--src/crepe/api/Sprite.h3
-rw-r--r--src/crepe/api/Texture.cpp2
-rw-r--r--src/crepe/facade/DB.cpp4
-rw-r--r--src/crepe/facade/DB.h4
-rw-r--r--src/crepe/facade/SDLContext.cpp99
-rw-r--r--src/crepe/facade/SDLContext.h41
-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.cpp3
-rw-r--r--src/crepe/system/PhysicsSystem.cpp6
-rw-r--r--src/crepe/system/RenderSystem.cpp88
-rw-r--r--src/crepe/system/RenderSystem.h52
-rw-r--r--src/crepe/system/ScriptSystem.cpp13
-rw-r--r--src/crepe/system/ScriptSystem.h6
-rw-r--r--src/crepe/types.h9
-rw-r--r--src/crepe/util/CMakeLists.txt2
-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
55 files changed, 1396 insertions, 256 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.h b/src/crepe/ComponentManager.h
index 2107453..0956d1e 100644
--- a/src/crepe/ComponentManager.h
+++ b/src/crepe/ComponentManager.h
@@ -8,6 +8,7 @@
#include "api/Vector2.h"
#include "Component.h"
+#include "types.h"
namespace crepe {
@@ -112,7 +113,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 +123,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/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/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index f9b370f..d6b6801 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -17,8 +17,13 @@ target_sources(crepe PUBLIC
Vector2.cpp
Camera.cpp
Animator.cpp
+ EventManager.cpp
+ IKeyListener.cpp
+ IMouseListener.cpp
LoopManager.cpp
LoopTimer.cpp
+ Asset.cpp
+ EventHandler.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -43,6 +48,14 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
SceneManager.hpp
Camera.h
Animator.h
+ EventManager.h
+ EventManager.hpp
+ EventHandler.h
+ EventHandler.hpp
+ Event.h
+ IKeyListener.h
+ 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..13eabd1 100644
--- a/src/crepe/api/Config.h
+++ b/src/crepe/api/Config.h
@@ -62,6 +62,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
new file mode 100644
index 0000000..06cf7f3
--- /dev/null
+++ b/src/crepe/api/Event.h
@@ -0,0 +1,112 @@
+// TODO discussing the location of these events
+#pragma once
+
+#include <string>
+
+#include "KeyCodes.h"
+
+/**
+ * \brief Base class for all event types in the system.
+ */
+class Event {};
+
+/**
+ * \brief Event triggered when a key is pressed.
+ */
+class KeyPressEvent : public Event {
+public:
+ //! false if first time press, true if key is repeated
+ bool repeat = false;
+
+ //! The key that was pressed.
+ Keycode key = Keycode::NONE;
+};
+
+/**
+ * \brief Event triggered when a key is released.
+ */
+class KeyReleaseEvent : public Event {
+public:
+ //! The key that was released.
+ Keycode key = Keycode::NONE;
+};
+
+/**
+ * \brief Event triggered when a mouse button is pressed.
+ */
+class MousePressEvent : public Event {
+public:
+ //! X-coordinate of the mouse position at the time of the event.
+ int mouse_x = 0;
+
+ //! Y-coordinate of the mouse position at the time of the event.
+ int mouse_y = 0;
+
+ //! The mouse button that was pressed.
+ MouseButton button = MouseButton::NONE;
+};
+
+/**
+ * \brief Event triggered when a mouse button is clicked (press and release).
+ */
+class MouseClickEvent : public Event {
+public:
+ //! X-coordinate of the mouse position at the time of the event.
+ int mouse_x = 0;
+
+ //! Y-coordinate of the mouse position at the time of the event.
+ int mouse_y = 0;
+
+ //! The mouse button that was clicked.
+ MouseButton button = MouseButton::NONE;
+};
+
+/**
+ * \brief Event triggered when a mouse button is released.
+ */
+class MouseReleaseEvent : public Event {
+public:
+ //! X-coordinate of the mouse position at the time of the event.
+ int mouse_x = 0;
+
+ //! Y-coordinate of the mouse position at the time of the event.
+ int mouse_y = 0;
+
+ //! The mouse button that was released.
+ MouseButton button = MouseButton::NONE;
+};
+
+/**
+ * \brief Event triggered when the mouse is moved.
+ */
+class MouseMoveEvent : public Event {
+public:
+ //! X-coordinate of the mouse position at the time of the event.
+ int mouse_x = 0;
+
+ //! Y-coordinate of the mouse position at the time of the event.
+ int mouse_y = 0;
+};
+
+/**
+ * \brief Event triggered during a collision between objects.
+ */
+class CollisionEvent : public Event {
+public:
+ //! Data describing the collision (currently not implemented).
+ // Collision collisionData;
+};
+
+/**
+ * \brief Event triggered when text is submitted, e.g., from a text input.
+ */
+class TextSubmitEvent : public Event {
+public:
+ //! The submitted text.
+ std::string text = "";
+};
+
+/**
+ * \brief Event triggered to indicate the application is shutting down.
+ */
+class ShutDownEvent : public Event {};
diff --git a/src/crepe/api/EventHandler.cpp b/src/crepe/api/EventHandler.cpp
new file mode 100644
index 0000000..4dc232f
--- /dev/null
+++ b/src/crepe/api/EventHandler.cpp
@@ -0,0 +1,5 @@
+#include "EventHandler.h"
+
+using namespace crepe;
+
+bool IEventHandlerWrapper::exec(const Event & e) { return this->call(e); }
diff --git a/src/crepe/api/EventHandler.h b/src/crepe/api/EventHandler.h
new file mode 100644
index 0000000..ef659fd
--- /dev/null
+++ b/src/crepe/api/EventHandler.h
@@ -0,0 +1,96 @@
+#pragma once
+
+#include <functional>
+#include <string>
+
+#include "Event.h"
+
+namespace crepe {
+/**
+ * \brief A type alias for an event handler function.
+ *
+ * The EventHandler is a std::function that takes an EventType reference and returns a boolean value
+ * 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)>;
+
+/**
+ * \class IEventHandlerWrapper
+ * \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
+ */
+class IEventHandlerWrapper {
+public:
+ /**
+ * \brief Virtual destructor for IEventHandlerWrapper.
+ */
+ virtual ~IEventHandlerWrapper() = default;
+
+ /**
+ * \brief Executes the handler with the given event.
+ *
+ * This method calls the `call()` method of the derived class, passing the event to the handler.
+ *
+ * \param e The event to be processed.
+ * \return A boolean value indicating whether the event is handled.
+ */
+ bool exec(const Event & e);
+
+private:
+ /**
+ * \brief The method responsible for handling the event.
+ *
+ * This method is implemented by derived classes to process the event.
+ *
+ * \param e The event to be processed.
+ * \return A boolean value indicating whether the event is handled.
+ */
+ virtual bool call(const Event & e) = 0;
+};
+
+/**
+ * \class EventHandlerWrapper
+ * \brief A wrapper for event handler functions.
+ *
+ * This class wraps an event handler function of a specific event type. It implements the
+ * `call()` and `get_type()` methods to allow the handler to be executed and its type to be
+ * queried.
+ *
+ * \tparam EventType The type of event this handler will handle.
+ */
+template <typename EventType>
+class EventHandlerWrapper : public IEventHandlerWrapper {
+public:
+ /**
+ * \brief Constructs an EventHandlerWrapper with a given handler.
+ *
+ * The constructor takes an event handler function and stores it in the wrapper.
+ *
+ * \param handler The event handler function.
+ */
+ explicit EventHandlerWrapper(const EventHandler<EventType> & handler);
+
+private:
+ /**
+ * \brief Calls the stored event handler with the event.
+ *
+ * This method casts the event to the appropriate type and calls the handler.
+ *
+ * \param e The event to be handled.
+ * \return A boolean value indicating whether the event is handled.
+ */
+ bool call(const Event & e) override;
+ //! The event handler function.
+ EventHandler<EventType> handler;
+};
+
+} // namespace crepe
+
+#include "EventHandler.hpp"
diff --git a/src/crepe/api/EventHandler.hpp b/src/crepe/api/EventHandler.hpp
new file mode 100644
index 0000000..391dcca
--- /dev/null
+++ b/src/crepe/api/EventHandler.hpp
@@ -0,0 +1,18 @@
+#include <typeindex>
+
+#include "EventHandler.h"
+
+namespace crepe {
+
+// Implementation of EventHandlerWrapper constructor
+template <typename EventType>
+EventHandlerWrapper<EventType>::EventHandlerWrapper(const EventHandler<EventType> & handler)
+ : handler(handler) {}
+
+// Implementation of EventHandlerWrapper::call
+template <typename EventType>
+bool EventHandlerWrapper<EventType>::call(const Event & e) {
+ return this->handler(static_cast<const EventType &>(e));
+}
+
+} //namespace crepe
diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp
new file mode 100644
index 0000000..20f0dd3
--- /dev/null
+++ b/src/crepe/api/EventManager.cpp
@@ -0,0 +1,46 @@
+#include "EventManager.h"
+
+using namespace crepe;
+using namespace std;
+
+EventManager & EventManager::get_instance() {
+ static EventManager instance;
+ return instance;
+}
+
+void EventManager::dispatch_events() {
+ 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();
+}
+
+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
new file mode 100644
index 0000000..348a04d
--- /dev/null
+++ b/src/crepe/api/EventManager.h
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <memory>
+#include <typeindex>
+#include <unordered_map>
+#include <vector>
+
+#include "Event.h"
+#include "EventHandler.h"
+
+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 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 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 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>
+ subscription_t subscribe(const EventHandler<EventType> & callback,
+ event_channel_t channel = CHANNEL_ALL);
+
+ /**
+ * \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 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, event_channel_t channel = CHANNEL_ALL);
+
+ /**
+ * \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(const EventType & event, event_channel_t channel = CHANNEL_ALL);
+
+ /**
+ * \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.
+ *
+ * Constructor is private to enforce the singleton pattern.
+ */
+ EventManager() = default;
+
+ /**
+ * \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
new file mode 100644
index 0000000..a5f4556
--- /dev/null
+++ b/src/crepe/api/EventManager.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "EventManager.h"
+
+namespace crepe {
+
+template <typename EventType>
+subscription_t EventManager::subscribe(const EventHandler<EventType> & callback,
+ event_channel_t channel) {
+ subscription_counter++;
+ std::type_index event_type = typeid(EventType);
+ std::unique_ptr<EventHandlerWrapper<EventType>> handler
+ = std::make_unique<EventHandlerWrapper<EventType>>(callback);
+ std::vector<CallbackEntry> & handlers = this->subscribers[event_type];
+ handlers.emplace_back(CallbackEntry{
+ .callback = std::move(handler), .channel = channel, .id = subscription_counter});
+ return subscription_counter;
+}
+
+template <typename EventType>
+void EventManager::queue_event(const EventType & event, event_channel_t channel) {
+ static_assert(std::is_base_of<Event, EventType>::value,
+ "EventType must derive from Event");
+ 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, 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..4874426 100644
--- a/src/crepe/api/GameObject.cpp
+++ b/src/crepe/api/GameObject.cpp
@@ -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/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp
new file mode 100644
index 0000000..8642655
--- /dev/null
+++ b/src/crepe/api/IKeyListener.cpp
@@ -0,0 +1,19 @@
+#include "IKeyListener.h"
+
+using namespace crepe;
+
+// Constructor with specified channel
+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() {
+ 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
new file mode 100644
index 0000000..328a4c2
--- /dev/null
+++ b/src/crepe/api/IKeyListener.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include "Event.h"
+#include "EventHandler.h"
+#include "EventManager.h"
+
+namespace crepe {
+
+/**
+ * \class IKeyListener
+ * \brief Interface for keyboard event handling in the application.
+ */
+class IKeyListener {
+public:
+ /**
+ * \brief Constructs an IKeyListener with a specified channel.
+ * \param channel The channel ID for event handling.
+ */
+ 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;
+
+ /**
+ * \brief Pure virtual function to handle key press events.
+ * \param event The key press event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_key_pressed(const KeyPressEvent & event) = 0;
+
+ /**
+ * \brief Pure virtual function to handle key release events.
+ * \param event The key release event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_key_released(const KeyReleaseEvent & event) = 0;
+
+private:
+ //! Key press event id
+ subscription_t press_id = -1;
+ //! Key release event id
+ subscription_t release_id = -1;
+ //! EventManager reference
+ EventManager & event_manager;
+};
+
+} // namespace crepe
diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp
new file mode 100644
index 0000000..989aeb3
--- /dev/null
+++ b/src/crepe/api/IMouseListener.cpp
@@ -0,0 +1,29 @@
+#include "IMouseListener.h"
+
+using namespace crepe;
+
+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() {
+ // Unsubscribe event handlers
+ 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
new file mode 100644
index 0000000..15e1619
--- /dev/null
+++ b/src/crepe/api/IMouseListener.h
@@ -0,0 +1,72 @@
+#pragma once
+
+#include "Event.h"
+#include "EventHandler.h"
+#include "EventManager.h"
+
+namespace crepe {
+
+/**
+ * \class IMouseListener
+ * \brief Interface for mouse event handling in the application.
+ */
+class IMouseListener {
+public:
+ /**
+ * \brief Constructs an IMouseListener with a specified channel.
+ * \param channel The channel ID for event handling.
+ */
+ IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL);
+ virtual ~IMouseListener();
+ IMouseListener & operator=(const IMouseListener &) = delete;
+ IMouseListener(const IMouseListener &) = delete;
+ IMouseListener & operator=(const IMouseListener &&) = delete;
+ IMouseListener(IMouseListener &&) = delete;
+
+ /**
+ * \brief Move assignment operator (deleted).
+ */
+ IMouseListener & operator=(IMouseListener &&) = delete;
+
+ /**
+ * \brief Handles a mouse click event.
+ * \param event The mouse click event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_clicked(const MouseClickEvent & event) = 0;
+
+ /**
+ * \brief Handles a mouse press event.
+ * \param event The mouse press event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_pressed(const MousePressEvent & event) = 0;
+
+ /**
+ * \brief Handles a mouse release event.
+ * \param event The mouse release event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_released(const MouseReleaseEvent & event) = 0;
+
+ /**
+ * \brief Handles a mouse move event.
+ * \param event The mouse move event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0;
+
+private:
+ //! 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;
+};
+
+} //namespace crepe
diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h
new file mode 100644
index 0000000..9e173e0
--- /dev/null
+++ b/src/crepe/api/KeyCodes.h
@@ -0,0 +1,153 @@
+#pragma once
+
+//! Enumeration for mouse button inputs, including standard and extended buttons.
+enum class MouseButton {
+ 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.
+};
+
+//! 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,
+ F4 = 293,
+ F5 = 294,
+ F6 = 295,
+ F7 = 296,
+ F8 = 297,
+ F9 = 298,
+ F10 = 299,
+ F11 = 300,
+ F12 = 301,
+ F13 = 302,
+ F14 = 303,
+ F15 = 304,
+ F16 = 305,
+ F17 = 306,
+ F18 = 307,
+ F19 = 308,
+ F20 = 309,
+ F21 = 310,
+ F22 = 311,
+ F23 = 312,
+ F24 = 313,
+ F25 = 314,
+ /// \}
+ /**
+ * \name Keypad digits and operators.
+ * \{
+ */
+ KP0 = 320,
+ KP1 = 321,
+ KP2 = 322,
+ KP3 = 323,
+ KP4 = 324,
+ KP5 = 325,
+ KP6 = 326,
+ KP7 = 327,
+ KP8 = 328,
+ KP9 = 329,
+ KP_DECIMAL = 330,
+ KP_DIVIDE = 331,
+ KP_MULTIPLY = 332,
+ KP_SUBTRACT = 333,
+ KP_ADD = 334,
+ KP_ENTER = 335,
+ KP_EQUAL = 336,
+ /// \}
+ /**
+ * \name Modifier keys.
+ * \{
+ */
+ LEFT_SHIFT = 340,
+ LEFT_CONTROL = 341,
+ LEFT_ALT = 342,
+ LEFT_SUPER = 343,
+ RIGHT_SHIFT = 344,
+ RIGHT_CONTROL = 345,
+ RIGHT_ALT = 346,
+ RIGHT_SUPER = 347,
+ /// \}
+ MENU = 348, //!< Menu key.
+};
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
index f6904be..13e6dac 100644
--- a/src/crepe/api/LoopManager.h
+++ b/src/crepe/api/LoopManager.h
@@ -4,14 +4,28 @@
#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 Add a new concrete scene to the scene manager
+ *
+ * \tparam T Type of concrete scene
+ */
+ template <typename T>
+ void add_scene();
+
private:
/**
* \brief Setup function for one-time initialization.
@@ -53,12 +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.
*
@@ -71,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/Scene.cpp b/src/crepe/api/Scene.cpp
index 88aa82d..849945e 100644
--- a/src/crepe/api/Scene.cpp
+++ b/src/crepe/api/Scene.cpp
@@ -2,6 +2,4 @@
using namespace crepe;
-Scene::Scene(ComponentManager & mgr, const std::string & name)
- : component_manager(mgr),
- name(name) {}
+Scene::Scene(ComponentManager & mgr) : component_manager(mgr) {}
diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h
index 0e516b6..869bf6f 100644
--- a/src/crepe/api/Scene.h
+++ b/src/crepe/api/Scene.h
@@ -7,19 +7,36 @@ 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);
+ //TODO: Use Loek's custom reference class to set ComponentManger via SceneManager instead of via constructor
+ /**
+ * \param mgr Reference to the ComponentManager
+ */
+ Scene(ComponentManager & mgr);
+ //! 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:
+ //! Reference to the ComponentManager
ComponentManager & component_manager;
};
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..45ba668 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);
+ void add_scene();
/**
* \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..94e5946 100644
--- a/src/crepe/api/SceneManager.hpp
+++ b/src/crepe/api/SceneManager.hpp
@@ -5,16 +5,16 @@
namespace crepe {
template <typename T>
-void SceneManager::add_scene(const std::string & name) {
+void SceneManager::add_scene() {
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);
+ Scene * scene = new T(this->component_manager);
this->scenes.emplace_back(unique_ptr<Scene>(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.h b/src/crepe/api/Script.h
index 2b70379..839d937 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -62,7 +62,7 @@ protected:
* \returns List of component references
*/
template <typename T>
- std::vector<std::reference_wrapper<T>> get_components() const;
+ RefVector<T> get_components() const;
protected:
// NOTE: Script must have a constructor without arguments so the game programmer doesn't need
diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp
index a064a90..a85d814 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,9 +19,8 @@ T & Script::get_component() const {
}
template <typename T>
-std::vector<std::reference_wrapper<T>> Script::get_components() const {
+RefVector<T> Script::get_components() const {
auto & mgr = *this->component_manager_ref;
-
return mgr.get_components_by_id<T>(this->game_object_id);
}
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 734a5bb..9be9421 100644
--- a/src/crepe/api/Texture.cpp
+++ b/src/crepe/api/Texture.cpp
@@ -26,7 +26,7 @@ Texture::~Texture() {
void Texture::load(unique_ptr<Asset> res) {
SDLContext & ctx = SDLContext::get_instance();
- this->texture = std::move(ctx.texture_from_path(res->get_canonical()));
+ this->texture = std::move(ctx.texture_from_path(res->get_path()));
}
int Texture::get_width() const {
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 0cb7be9..74af25f 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,13 +8,15 @@
#include <cmath>
#include <cstddef>
#include <functional>
-#include <iostream>
#include <memory>
+#include <stdexcept>
#include <string>
+#include "../api/Camera.h"
#include "../api/Sprite.h"
#include "../api/Texture.h"
#include "../api/Transform.h"
+#include "../api/Vector2.h"
#include "../util/Log.h"
#include "api/Vector2.h"
@@ -31,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
@@ -61,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!");
}
}
@@ -79,7 +73,6 @@ SDLContext::~SDLContext() {
IMG_Quit();
SDL_Quit();
}
-
void SDLContext::handle_events(bool & running) {
//TODO: wouter i need events
/*
@@ -104,37 +97,64 @@ void SDLContext::handle_events(bool & running) {
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) {
-
- SDL_RendererFlip render_flip
- = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x)
- | (SDL_FLIP_VERTICAL * sprite.flip.flip_y));
-
- Vector2 pixel_coord = (transform.position - cam.pos) * cam.scale;
- double pixel_w = sprite.sprite_rect.w * transform.scale * cam.scale.x;
- double pixel_h = sprite.sprite_rect.h * transform.scale * cam.scale.y;
-
- // decides which part of the sprite will be drawn
- SDL_Rect srcrect = {
+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 Vector2 & pos,
+ const double & scale, const Camera & cam) const {
- // decides where the clipped image is drawn
- SDL_Rect dstrect = {
- .x = static_cast<int>(pixel_coord.x),
- .y = static_cast<int>(pixel_coord.y),
- .w = static_cast<int>(pixel_w),
- .h = static_cast<int>(pixel_h),
+ Vector2 pixel_coord = (transform.position - cam.pos) * cam.scale;
+ double pixel_w = sprite.sprite_rect.w * transform.scale * cam.scale.x;
+ double pixel_h = sprite.sprite_rect.h * transform.scale * cam.scale.y;
+
+ double adjusted_x = (pos.x - cam.x) * cam.zoom;
+ double adjusted_y = (pos.y - cam.y) * cam.zoom;
+ double adjusted_w = sprite.sprite_rect.w * scale * cam.zoom;
+ double adjusted_h = sprite.sprite_rect.h * scale * cam.zoom;
+
+ 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 Vector2 & 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));
+
+ SDL_Rect srcrect = this->get_src_rect(sprite);
+ SDL_Rect dstrect = this->get_dst_rect(sprite, pos, scale, camera);
+
+ SDL_RenderCopyEx(this->game_renderer.get(), sprite.sprite_image->texture.get(), &srcrect,
+ &dstrect, angle, NULL, render_flip);
+}
+
+void SDLContext::draw(const Sprite & sprite, const Transform & transform, const Camera & cam) {
+
+ SDL_RendererFlip render_flip
+ = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x)
+ | (SDL_FLIP_VERTICAL * sprite.flip.flip_y));
+
+ 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(Camera & cam) {
+void SDLContext::set_camera(const Camera & cam) {
+
double screen_aspect = cam.screen.x / cam.screen.y;
double viewport_aspect = cam.viewport.x / cam.viewport.y;
@@ -162,9 +182,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); }};
@@ -173,7 +192,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 b69e509..841ffc9 100644
--- a/src/crepe/facade/SDLContext.h
+++ b/src/crepe/facade/SDLContext.h
@@ -1,8 +1,10 @@
#pragma once
#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>
@@ -10,10 +12,7 @@
#include "../api/Sprite.h"
#include "../api/Transform.h"
#include "api/Camera.h"
-
-// FIXME: this needs to be removed
-const int SCREEN_WIDTH = 640;
-const int SCREEN_HEIGHT = 480;
+#include "api/Vector2.h"
namespace crepe {
@@ -21,9 +20,6 @@ namespace crepe {
// typedef is unusable when crepe is packaged. Wouter will fix this later.
typedef SDL_Keycode CREPE_KEYCODES;
-class Texture;
-class LoopManager;
-
/**
* \class SDLContext
* \brief Facade for the SDL library
@@ -91,9 +87,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.
@@ -127,6 +120,9 @@ private:
*/
void draw(const Sprite & sprite, const Transform & transform, const Camera & camera);
+ void draw_particle(const Sprite & sprite, const Vector2 & pos, const double & angle,
+ const double & scale, const Camera & camera);
+
//! Clears the screen, preparing for a new frame.
void clear_screen();
@@ -134,10 +130,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(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 Vector2 & 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 efe0e48..bc94253 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..fcf7522 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -14,8 +14,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
diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp
index 4a7dbfb..bcde431 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) {
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index 7538e20..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 "RenderSystem.h"
using namespace crepe;
+using namespace std;
-void RenderSystem::clear_screen() const { SDLContext::get_instance().clear_screen(); }
-
-void RenderSystem::present_screen() const { SDLContext::get_instance().present_screen(); }
+void RenderSystem::clear_screen() { this->context.clear_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..c33309c 100644
--- a/src/crepe/system/ScriptSystem.cpp
+++ b/src/crepe/system/ScriptSystem.cpp
@@ -1,6 +1,4 @@
-#include <forward_list>
#include <functional>
-#include <vector>
#include "../ComponentManager.h"
#include "../api/BehaviorScript.h"
@@ -14,7 +12,7 @@ using namespace crepe;
void ScriptSystem::update() {
dbg_trace();
- forward_list<reference_wrapper<Script>> scripts = this->get_scripts();
+ RefVector<Script> scripts = this->get_scripts();
for (auto & script_ref : scripts) {
Script & script = script_ref.get();
@@ -26,18 +24,17 @@ void ScriptSystem::update() {
}
}
-forward_list<reference_wrapper<Script>> ScriptSystem::get_scripts() const {
- forward_list<reference_wrapper<Script>> scripts = {};
+RefVector<Script> ScriptSystem::get_scripts() const {
+ RefVector<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();
if (!behavior_script.active) continue;
Script * script = behavior_script.script.get();
if (script == nullptr) continue;
- scripts.push_front(*script);
+ scripts.push_back(*script);
}
return scripts;
diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h
index deb89cb..32e1fcd 100644
--- a/src/crepe/system/ScriptSystem.h
+++ b/src/crepe/system/ScriptSystem.h
@@ -1,9 +1,9 @@
#pragma once
-#include <forward_list>
-
#include "System.h"
+#include "../types.h"
+
namespace crepe {
class Script;
@@ -33,7 +33,7 @@ private:
*
* \returns List of active \c Script instances
*/
- std::forward_list<std::reference_wrapper<Script>> get_scripts() const;
+ RefVector<Script> get_scripts() const;
};
} // namespace crepe
diff --git a/src/crepe/types.h b/src/crepe/types.h
index 0d459e8..914c76c 100644
--- a/src/crepe/types.h
+++ b/src/crepe/types.h
@@ -1,9 +1,16 @@
#pragma once
#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>>;
+
+} // 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/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();
}