aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJAROWMR <jarorutjes07@gmail.com>2024-11-21 20:40:08 +0100
committerJAROWMR <jarorutjes07@gmail.com>2024-11-21 20:40:08 +0100
commitd58a059fd6398527274e77a37b65aa105c8a778b (patch)
tree26d345400739ec685bbe6b26d8d789bbe33b02aa
parentfaa9adb84ad3f675587b52fba79cf44b7bdd2034 (diff)
parent115d6f50152dc018073345800ca90b85846ebaa9 (diff)
merge with master
-rw-r--r--.crepe-root0
-rw-r--r--.gitmodules4
-rw-r--r--asset/texture/ERROR.pngbin0 -> 3581 bytes
-rw-r--r--contributing.md5
-rw-r--r--lib/whereami/CMakeLists.txt38
m---------lib/whereami/lib0
-rw-r--r--mwe/events/include/event.h2
-rw-r--r--readme.md7
-rw-r--r--src/CMakeLists.txt2
-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.txt5
-rw-r--r--src/crepe/api/Config.h14
-rw-r--r--src/crepe/api/Event.h76
-rw-r--r--src/crepe/api/EventHandler.cpp5
-rw-r--r--src/crepe/api/EventHandler.h46
-rw-r--r--src/crepe/api/EventHandler.hpp18
-rw-r--r--src/crepe/api/EventManager.cpp95
-rw-r--r--src/crepe/api/EventManager.h199
-rw-r--r--src/crepe/api/EventManager.hpp146
-rw-r--r--src/crepe/api/GameObject.cpp6
-rw-r--r--src/crepe/api/IKeyListener.cpp56
-rw-r--r--src/crepe/api/IKeyListener.h61
-rw-r--r--src/crepe/api/IMouseListener.cpp79
-rw-r--r--src/crepe/api/IMouseListener.h79
-rw-r--r--src/crepe/api/KeyCodes.h187
-rw-r--r--src/crepe/api/LoopManager.h17
-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.h6
-rw-r--r--src/crepe/api/Script.hpp5
-rw-r--r--src/crepe/api/Texture.cpp4
-rw-r--r--src/crepe/facade/DB.cpp4
-rw-r--r--src/crepe/facade/DB.h4
-rw-r--r--src/crepe/facade/SDLContext.cpp89
-rw-r--r--src/crepe/facade/SDLContext.h35
-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.cpp73
-rw-r--r--src/crepe/system/RenderSystem.h26
-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
-rw-r--r--src/doc/feature/gameobject.dox18
-rw-r--r--src/doc/feature/scene.dox67
-rw-r--r--src/example/CMakeLists.txt9
-rw-r--r--src/example/audio_internal.cpp56
-rw-r--r--src/example/db.cpp30
-rw-r--r--src/example/log.cpp28
-rw-r--r--src/example/proxy.cpp45
-rw-r--r--src/example/rendering_particle.cpp71
-rw-r--r--src/example/script.cpp49
-rw-r--r--src/test/AssetTest.cpp26
-rw-r--r--src/test/CMakeLists.txt6
-rw-r--r--src/test/DBTest.cpp28
-rw-r--r--src/test/EventTest.cpp254
-rw-r--r--src/test/OptionalRefTest.cpp42
-rw-r--r--src/test/ParticleTest.cpp4
-rw-r--r--src/test/SceneManagerTest.cpp8
-rw-r--r--src/test/ValueBrokerTest.cpp63
78 files changed, 1601 insertions, 1015 deletions
diff --git a/.crepe-root b/.crepe-root
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.crepe-root
diff --git a/.gitmodules b/.gitmodules
index 2f64601..bd6e7f7 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -22,3 +22,7 @@
path = lib/libdb
url = https://github.com/berkeleydb/libdb
shallow = true
+[submodule "lib/whereami/whereami"]
+ path = lib/whereami/lib
+ url = https://github.com/gpakosz/whereami
+ shallow = true
diff --git a/asset/texture/ERROR.png b/asset/texture/ERROR.png
new file mode 100644
index 0000000..2af3548
--- /dev/null
+++ b/asset/texture/ERROR.png
Binary files differ
diff --git a/contributing.md b/contributing.md
index 9c95851..77a2908 100644
--- a/contributing.md
+++ b/contributing.md
@@ -15,7 +15,11 @@ that you can click on to open them.
`name/feature` (i.e. `loek/dll-so-poc` or `jaro/class2`)
- The master branch is considered stable, and should always contain a
working/compiling version of the project
+- Pull requests for new code include either automated tests for the new code or
+ an explanation as to why the code can not (reliably) be tested
+<!--
- TODO: tagging / versions
+-->
# Code style
@@ -790,6 +794,7 @@ that you can click on to open them.
}
```
</td></tr></table></details>
+- Do not implement new classes as singletons
## CMakeLists-specific
diff --git a/lib/whereami/CMakeLists.txt b/lib/whereami/CMakeLists.txt
new file mode 100644
index 0000000..96d3a23
--- /dev/null
+++ b/lib/whereami/CMakeLists.txt
@@ -0,0 +1,38 @@
+cmake_minimum_required(VERSION 3.28)
+set(CMAKE_C_STANDARD 11)
+project(whereami C)
+
+include(CMakePackageConfigHelpers)
+
+add_library(whereami SHARED)
+
+target_include_directories(whereami PRIVATE SYSTEM lib/src)
+target_sources(whereami PRIVATE lib/src/whereami.c)
+
+install(
+ TARGETS whereami
+ EXPORT whereamiTargets
+ LIBRARY DESTINATION lib
+ ARCHIVE DESTINATION lib
+ RUNTIME DESTINATION lib
+ INCLUDES DESTINATION include
+)
+install(
+ FILES lib/src/whereami.h
+ DESTINATION include
+)
+write_basic_package_version_file(
+ "${CMAKE_CURRENT_BINARY_DIR}/whereami-config-version.cmake"
+ VERSION 0.0.0
+ COMPATIBILITY AnyNewerVersion
+)
+install(
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/whereami-config-version.cmake"
+ DESTINATION lib/cmake/whereami
+)
+install(
+ EXPORT whereamiTargets
+ FILE whereami-config.cmake
+ DESTINATION lib/cmake/whereami
+)
diff --git a/lib/whereami/lib b/lib/whereami/lib
new file mode 160000
+Subproject dcb52a058dc14530ba9ae05e4339bd3ddfae0e0
diff --git a/mwe/events/include/event.h b/mwe/events/include/event.h
index e1b220b..ee1bf52 100644
--- a/mwe/events/include/event.h
+++ b/mwe/events/include/event.h
@@ -148,7 +148,7 @@ private:
};
class ShutDownEvent : public Event {
public:
- ShutDownEvent() : Event("ShutDownEvent"){};
+ ShutDownEvent() : Event("ShutDownEvent") {};
REGISTER_EVENT_TYPE(ShutDownEvent)
diff --git a/readme.md b/readme.md
index c83853d..d309b30 100644
--- a/readme.md
+++ b/readme.md
@@ -32,6 +32,7 @@ This project uses the following libraries
|`SoLoud`|(latest git `master` version)|
|Google Test (`GTest`)|1.15.2|
|Berkeley DB (`libdb`)|5.3.21|
+|Where Am I?|(latest git `master` version)
> [!NOTE]
> Most of these libraries are likely available from your package manager if you
@@ -49,6 +50,11 @@ $ git submodule update --init --recursive --depth 1
Then, follow these steps for each library you want to install:
+> [!IMPORTANT]
+> A dollar sign prompt (`$`) indicates commands to be run as a regular user,
+> while a hashtag (`#`) is used to denote commands that must be run with
+> privileges (e.g. as root or using `sudo`).
+
1. Change into the library folder (run **one** of these):
```
$ cd lib/googletest
@@ -56,6 +62,7 @@ Then, follow these steps for each library you want to install:
$ cd lib/soloud/contrib
$ cd lib/sdl_image
$ cd lib/sdl_ttf
+ $ cd lib/whereami
```
2. Use CMake to configure the build, run the build and install (run **all** of
these):
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 445a8b2..c3f29da 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -11,6 +11,7 @@ find_package(SDL2 REQUIRED)
find_package(SDL2_image REQUIRED)
find_package(SoLoud REQUIRED)
find_package(GTest REQUIRED)
+find_package(whereami REQUIRED)
find_library(BERKELEY_DB db)
add_library(crepe SHARED)
@@ -25,6 +26,7 @@ target_link_libraries(crepe
PUBLIC SDL2
PUBLIC SDL2_image
PUBLIC ${BERKELEY_DB}
+ PUBLIC whereami
)
add_subdirectory(crepe)
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 07b3a82..830f5bd 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -20,11 +20,12 @@ target_sources(crepe PUBLIC
BoxCollider.cpp
CircleCollider.cpp
EventManager.cpp
- EventHandler.cpp
IKeyListener.cpp
IMouseListener.cpp
LoopManager.cpp
LoopTimer.cpp
+ Asset.cpp
+ EventHandler.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -54,9 +55,11 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
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/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
index bd6a541..8a6b931 100644
--- a/src/crepe/api/Event.h
+++ b/src/crepe/api/Event.h
@@ -1,59 +1,115 @@
+// TODO discussing the location of these events
#pragma once
-#include "KeyCodes.h"
-#include <iostream>
+
#include <string>
-#include <typeindex>
+
#include "system/CollisionSystem.h"
-class Event {
-public:
-};
+#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:
- 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;
};
+/**
+ * \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:
crepe::CollisionSystem::CollisionInfo info;
CollisionEvent(const crepe::CollisionSystem::CollisionInfo& collisionInfo)
: info(collisionInfo) {}
};
+
+/**
+ * \brief Event triggered when text is submitted, e.g., from a text input.
+ */
class TextSubmitEvent : public Event {
public:
+ //! The submitted text.
std::string text = "";
};
-class ShutDownEvent : public Event {
-public:
-};
+
+/**
+ * \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
index 93a116a..4dc232f 100644
--- a/src/crepe/api/EventHandler.cpp
+++ b/src/crepe/api/EventHandler.cpp
@@ -1,2 +1,5 @@
#include "EventHandler.h"
-bool IEventHandlerWrapper::exec(const Event & e) { return call(e); }
+
+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
index 0ab90de..ef659fd 100644
--- a/src/crepe/api/EventHandler.h
+++ b/src/crepe/api/EventHandler.h
@@ -1,9 +1,11 @@
#pragma once
-#include "Event.h"
+
#include <functional>
-#include <iostream>
-#include <typeindex>
+#include <string>
+#include "Event.h"
+
+namespace crepe {
/**
* \brief A type alias for an event handler function.
*
@@ -11,8 +13,9 @@
* 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.
*/
-// TODO: typedef
template <typename EventType>
using EventHandler = std::function<bool(const EventType & e)>;
@@ -21,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:
@@ -40,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.
@@ -81,8 +75,7 @@ public:
*
* \param handler The event handler function.
*/
- explicit EventHandlerWrapper(const EventHandler<EventType> & handler)
- : m_handler(handler), m_handler_type(m_handler.target_type().name()) {}
+ explicit EventHandlerWrapper(const EventHandler<EventType> & handler);
private:
/**
@@ -93,20 +86,11 @@ private:
* \param e The event to be handled.
* \return A boolean value indicating whether the event is handled.
*/
- bool call(const Event & e) override {
- return m_handler(static_cast<const EventType &>(e));
- }
-
- /**
- * \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 { return m_handler_type; }
+ bool call(const Event & e) 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
+
+#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
index e881d49..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,74 +9,38 @@ EventManager & EventManager::get_instance() {
}
void EventManager::dispatch_events() {
- for (std::vector<std::tuple<std::unique_ptr<Event>, int,
- std::type_index>>::iterator 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);
+ for (auto & event : this->events_queue) {
+ this->handle_event(event.type, event.channel, *event.event.get());
+ }
+ this->events_queue.clear();
+}
- bool event_handled = false;
+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;
- if (channel) {
- std::unordered_map<
- std::type_index,
- std::unordered_map<
- int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>>::
- iterator handlers_it
- = subscribers_by_event_id.find(event_type);
- if (handlers_it != subscribers_by_event_id.end()) {
- std::unordered_map<
- int, std::vector<std::unique_ptr<IEventHandlerWrapper>>> &
- handlers_map
- = handlers_it->second;
- std::unordered_map<
- int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>::
- iterator handlers
- = handlers_map.find(channel);
- if (handlers != handlers_map.end()) {
- std::vector<std::unique_ptr<IEventHandlerWrapper>> &
- callbacks
- = handlers->second;
- for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::
- iterator 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
- std::unordered_map<
- std::type_index,
- std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator
- handlers_it
- = this->subscribers.find(event_type);
- if (handlers_it != this->subscribers.end()) {
- std::vector<std::unique_ptr<IEventHandlerWrapper>> & handlers
- = handlers_it->second;
- for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::
- iterator 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;
- }
- }
- }
- }
+ 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 38d2e64..348a04d 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>
@@ -10,113 +8,154 @@
#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 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:
- /**
- * \brief Deleted copy constructor to prevent copying of the EventManager instance.
- */
- EventManager(const EventManager &) = delete;
-
- /**
- * \brief Deleted copy assignment operator to prevent assignment of the EventManager instance.
- */
- const EventManager & operator=(const EventManager &) = delete;
+ 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);
+ * \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);
+ 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);
+ 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 b509097..a5f4556 100644
--- a/src/crepe/api/EventManager.hpp
+++ b/src/crepe/api/EventManager.hpp
@@ -1,140 +1,36 @@
+#pragma once
+
#include "EventManager.h"
+
namespace crepe {
template <typename EventType>
-void EventManager::subscribe(EventHandler<EventType> && callback, int channel) {
+subscription_t EventManager::subscribe(const EventHandler<EventType> & callback,
+ event_channel_t channel) {
+ subscription_counter++;
std::type_index event_type = typeid(EventType);
std::unique_ptr<EventHandlerWrapper<EventType>> handler
= std::make_unique<EventHandlerWrapper<EventType>>(callback);
-
- if (channel) {
- std::unordered_map<int,
- std::vector<std::unique_ptr<IEventHandlerWrapper>>> &
- handlers_map
- = this->subscribers_by_event_id[event_type];
- std::unordered_map<
- int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator
- 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 {
- std::vector<std::unique_ptr<IEventHandlerWrapper>> & handlers
- = this->subscribers[event_type];
- handlers.emplace_back(std::move(handler));
- }
-}
-
-template <typename EventType>
-void EventManager::queue_event(EventType && event, int channel) {
- std::type_index event_type = std::type_index(typeid(EventType));
-
- std::unique_ptr<EventType> 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));
+ 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::trigger_event(const EventType & event, int channel) {
- std::type_index event_type = std::type_index(typeid(EventType));
-
- if (channel > 0) {
- std::unordered_map<int,
- std::vector<std::unique_ptr<IEventHandlerWrapper>>> &
- handlers_map
- = this->subscribers_by_event_id[event_type];
- std::unordered_map<
- int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator
- handlers_it
- = handlers_map.find(channel);
-
- if (handlers_it != handlers_map.end()) {
- std::vector<std::unique_ptr<IEventHandlerWrapper>> & handlers
- = handlers_it->second;
- for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::iterator it
- = handlers.begin();
- it != handlers.end();++it) {
- // stops when callback returns true
- if((*it)->exec(event)){
- break;
- }
- }
- }
- } else {
- std::vector<std::unique_ptr<IEventHandlerWrapper>> & handlers
- = this->subscribers[event_type];
- for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::iterator it
- = handlers.begin();
- it != handlers.end();++it) {
- // stops when callback returns true
- if((*it)->exec(event)){
- break;
- }
- }
- }
+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::unsubscribe(const EventHandler<EventType> & callback,
- int channel) {
- std::type_index event_type(typeid(EventType));
- std::string handler_name = callback.target_type().name();
-
- if (channel) {
- std::unordered_map<
- std::type_index,
- std::unordered_map<
- int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>>::
- iterator subscriber_list
- = this->subscribers_by_event_id.find(event_type);
- if (subscriber_list != this->subscribers_by_event_id.end()) {
- std::unordered_map<
- int, std::vector<std::unique_ptr<IEventHandlerWrapper>>> &
- handlers_map
- = subscriber_list->second;
- std::unordered_map<
- int,
- std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator
- handlers
- = handlers_map.find(channel);
- if (handlers != handlers_map.end()) {
- std::vector<std::unique_ptr<IEventHandlerWrapper>> & callbacks
- = handlers->second;
- for (std::vector<
- std::unique_ptr<IEventHandlerWrapper>>::iterator it
- = callbacks.begin();
- it != callbacks.end(); ++it) {
- if ((*it)->get_type() == handler_name) {
- it = callbacks.erase(it);
- return;
- }
- }
- }
- }
- } else {
- std::unordered_map<std::type_index,
- std::vector<std::unique_ptr<IEventHandlerWrapper>>>::
- iterator handlers_it
- = this->subscribers.find(event_type);
- if (handlers_it != this->subscribers.end()) {
- std::vector<std::unique_ptr<IEventHandlerWrapper>> & handlers
- = handlers_it->second;
- for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::iterator 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..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
index 4fd9855..8642655 100644
--- a/src/crepe/api/IKeyListener.cpp
+++ b/src/crepe/api/IKeyListener.cpp
@@ -2,50 +2,18 @@
using namespace crepe;
-IKeyListener::IKeyListener() {
- this->channel = channel;
- this->subscribe_events();
-}
-IKeyListener::IKeyListener(int channel) {
- this->channel = channel;
- this->subscribe_events();
-}
-IKeyListener::~IKeyListener() { this->unsubscribe_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);
- };
- EventManager::get_instance().subscribe<KeyPressEvent>(
- std::move(this->key_pressed_handler), this->channel);
- EventManager::get_instance().subscribe<KeyReleaseEvent>(
- std::move(this->key_released_handler), this->channel);
+// 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);
}
-void IKeyListener::unsubscribe_events() {
- EventManager::get_instance().unsubscribe<KeyPressEvent>(
- this->key_pressed_handler, channel);
- EventManager::get_instance().unsubscribe<KeyReleaseEvent>(
- this->key_released_handler, channel);
- std::cout << std::endl;
-}
-void IKeyListener::activate_keys() {
- if (this->active) {
- return;
- }
- this->subscribe_events();
-}
-void IKeyListener::deactivate_keys() {
- if (!this->active) {
- return;
- }
- this->unsubscribe_events();
-}
-void IKeyListener::set_channel(int channel) {
- this->unsubscribe_events();
- this->channel = channel;
- this->subscribe_events();
+// 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
index 839acbf..328a4c2 100644
--- a/src/crepe/api/IKeyListener.h
+++ b/src/crepe/api/IKeyListener.h
@@ -1,8 +1,11 @@
#pragma once
+
#include "Event.h"
#include "EventHandler.h"
#include "EventManager.h"
+namespace crepe {
+
/**
* \class IKeyListener
* \brief Interface for keyboard event handling in the application.
@@ -13,17 +16,12 @@ public:
* \brief Constructs an IKeyListener with a specified channel.
* \param channel The channel ID for event handling.
*/
- IKeyListener(int channel);
-
- /**
- * \brief Default constructor for IKeyListener.
- */
- IKeyListener();
-
- /**
- * \brief Destructor.
- */
+ 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.
@@ -39,40 +37,13 @@ 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;
};
+
+} // namespace crepe
diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp
index 489e55b..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) { this->channel = channel; }
-
-IMouseListener::IMouseListener() { 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);
- };
- EventManager::get_instance().subscribe<MouseClickEvent>(
- std::move(this->mouse_click_handler), this->channel);
- EventManager::get_instance().subscribe<MousePressEvent>(
- std::move(this->mouse_press_handler), this->channel);
- EventManager::get_instance().subscribe<MouseReleaseEvent>(
- std::move(this->mouse_release_handler), this->channel);
- EventManager::get_instance().subscribe<MouseMoveEvent>(
- std::move(this->mouse_move_handler), this->channel);
-}
-// TODO: reference voor singleton
-void IMouseListener::unsubscribe_events() {
- EventManager::get_instance().unsubscribe<MouseClickEvent>(
- this->mouse_click_handler, this->channel);
- EventManager::get_instance().unsubscribe<MousePressEvent>(
- this->mouse_press_handler, this->channel);
- EventManager::get_instance().unsubscribe<MouseReleaseEvent>(
- this->mouse_release_handler, this->channel);
- EventManager::get_instance().unsubscribe<MouseMoveEvent>(
- this->mouse_move_handler, this->channel);
-}
-
-void IMouseListener::activate_mouse() {
- if (this->active) {
- return;
- }
- this->subscribe_events();
-}
-
-void IMouseListener::deactivate_mouse() {
- if (!this->active) {
- return;
- }
- this->unsubscribe_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);
}
-void IMouseListener::set_channel(int channel) {
- this->unsubscribe_events();
- this->channel = channel;
- this->subscribe_events();
+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
index 7e92956..15e1619 100644
--- a/src/crepe/api/IMouseListener.h
+++ b/src/crepe/api/IMouseListener.h
@@ -4,6 +4,8 @@
#include "EventHandler.h"
#include "EventManager.h"
+namespace crepe {
+
/**
* \class IMouseListener
* \brief Interface for mouse event handling in the application.
@@ -11,34 +13,14 @@
class IMouseListener {
public:
/**
- * \brief Default constructor.
- */
- IMouseListener();
-
- /**
* \brief Constructs an IMouseListener with a specified channel.
* \param channel The channel ID for event handling.
*/
- IMouseListener(int channel);
-
- /**
- * \brief Destructor.
- */
+ IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL);
virtual ~IMouseListener();
-
- /**
- * \brief Copy constructor (deleted).
- */
- IMouseListener(const IMouseListener &) = delete;
-
- /**
- * \brief Copy assignment operator (deleted).
- */
IMouseListener & operator=(const IMouseListener &) = delete;
-
- /**
- * \brief Move constructor (deleted).
- */
+ IMouseListener(const IMouseListener &) = delete;
+ IMouseListener & operator=(const IMouseListener &&) = delete;
IMouseListener(IMouseListener &&) = delete;
/**
@@ -74,44 +56,17 @@ 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;
};
+
+} //namespace crepe
diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h
index e5a91fc..9e173e0 100644
--- a/src/crepe/api/KeyCodes.h
+++ b/src/crepe/api/KeyCodes.h
@@ -1,94 +1,93 @@
#pragma once
+//! Enumeration for mouse button inputs, including standard and extended buttons.
enum class MouseButton {
- NONE = 0,
- LEFT_MOUSE = 1,
- RIGHT_MOUSE = 2,
- MIDDLE_MOUSE = 3,
- X1_MOUSE = 4,
- X2_MOUSE = 5,
- SCROLL_UP = 6,
- 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 class Keycode : int {
- NONE = 0,
- SPACE = 32,
- APOSTROPHE = 39, /* ' */
- COMMA = 44, /* , */
- MINUS = 45, /* - */
- PERIOD = 46, /* . */
- SLASH = 47, /* / */
-
- D0 = 48, /* 0 */
- D1 = 49, /* 1 */
- D2 = 50, /* 2 */
- D3 = 51, /* 3 */
- D4 = 52, /* 4 */
- D5 = 53, /* 5 */
- D6 = 54, /* 6 */
- D7 = 55, /* 7 */
- D8 = 56, /* 8 */
- D9 = 57, /* 9 */
-
- SEMICOLON = 59, /* ; */
- EQUAL = 61, /* = */
-
- A = 65,
- B = 66,
- C = 67,
- D = 68,
- E = 69,
- F = 70,
- G = 71,
- H = 72,
- I = 73,
- J = 74,
- K = 75,
- L = 76,
- M = 77,
- N = 78,
- O = 79,
- P = 80,
- Q = 81,
- R = 82,
- S = 83,
- T = 84,
- U = 85,
- V = 86,
- W = 87,
- X = 88,
- Y = 89,
- Z = 90,
-
- LEFT_BRACKET = 91, /* [ */
- BACKSLASH = 92, /* \ */
- RIGHT_BRACKET = 93, /* ] */
- GRAVE_ACCENT = 96, /* ` */
-
- WORLD1 = 161, /* non-US #1 */
- WORLD2 = 162, /* non-US #2 */
-
- /* Function keys */
- ESCAPE = 256,
- ENTER = 257,
- TAB = 258,
- BACKSPACE = 259,
- INSERT = 260,
- DELETE = 261,
- RIGHT = 262,
- LEFT = 263,
- DOWN = 264,
- UP = 265,
- PAGE_UP = 266,
- PAGE_DOWN = 267,
- HOME = 268,
- END = 269,
- CAPS_LOCK = 280,
- SCROLL_LOCK = 281,
- NUM_LOCK = 282,
- PRINT_SCREEN = 283,
- PAUSE = 284,
+//! 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,
@@ -114,8 +113,11 @@ enum class Keycode : int {
F23 = 312,
F24 = 313,
F25 = 314,
-
- /* Keypad */
+ /// \}
+ /**
+ * \name Keypad digits and operators.
+ * \{
+ */
KP0 = 320,
KP1 = 321,
KP2 = 322,
@@ -133,7 +135,11 @@ enum class Keycode : int {
KP_ADD = 334,
KP_ENTER = 335,
KP_EQUAL = 336,
-
+ /// \}
+ /**
+ * \name Modifier keys.
+ * \{
+ */
LEFT_SHIFT = 340,
LEFT_CONTROL = 341,
LEFT_ALT = 342,
@@ -142,5 +148,6 @@ enum class Keycode : int {
RIGHT_CONTROL = 345,
RIGHT_ALT = 346,
RIGHT_SUPER = 347,
- MENU = 348
+ /// \}
+ MENU = 348, //!< Menu key.
};
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
index 37f13ac..13e6dac 100644
--- a/src/crepe/api/LoopManager.h
+++ b/src/crepe/api/LoopManager.h
@@ -8,11 +8,24 @@
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.
@@ -54,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.
*
@@ -72,9 +87,9 @@ private:
private:
//! Component manager instance
ComponentManager component_manager{};
-public:
//! Scene manager instance
SceneManager scene_manager{component_manager};
+
private:
/**
* \brief Collection of System instances
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 939d142..0702e36 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -62,15 +62,15 @@ protected:
* \returns List of component references
*/
template <typename T>
- std::vector<std::reference_wrapper<T>> get_components() const;
-
+ RefVector<T> get_components() const;
+
/**
* \brief Gets game object id this script is attached to
*
* \returns game object id
*/
game_object_id_t get_game_object_id() const {return this->game_object_id;};
-
+
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.
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/Texture.cpp b/src/crepe/api/Texture.cpp
index de0d0ea..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 {
@@ -35,5 +35,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/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 803516e..00523a6 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 "SDLContext.h"
@@ -30,29 +33,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
@@ -60,9 +56,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!");
}
}
@@ -78,7 +72,6 @@ SDLContext::~SDLContext() {
IMG_Quit();
SDL_Quit();
}
-
void SDLContext::handle_events(bool & running) {
//TODO: wouter i need events
/*
@@ -103,40 +96,63 @@ 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));
-
- double adjusted_x = (transform.position.x - cam.x -(sprite.sprite_rect.w/2)) * cam.zoom;
- double adjusted_y = (transform.position.y - cam.y -(sprite.sprite_rect.h/2)) * 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 = {
+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 {
- SDL_Rect dstrect = {
+ 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(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);
@@ -148,9 +164,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); }};
@@ -159,7 +174,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 652a83e..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 {
@@ -121,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();
@@ -128,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(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 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 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..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 96c5f27..8f9467e 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -1,12 +1,16 @@
#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"
@@ -20,12 +24,15 @@ 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");
if (cameras.size() == 0) throw std::runtime_error("No cameras in current scene");
for (Camera & cam : cameras) {
- this->context.camera(cam);
+ if (!cam.active) continue;
+ this->context.set_camera(cam);
this->curr_cam_ref = &cam;
}
}
@@ -37,29 +44,61 @@ bool sorting_comparison(const Sprite & a, const Sprite & b) {
return false;
}
-std::vector<std::reference_wrapper<Sprite>>
-RenderSystem::sort(std::vector<std::reference_wrapper<Sprite>> & objs) {
-
- std::vector<std::reference_wrapper<Sprite>> sorted_objs(objs);
+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::render_sprites() {
+void RenderSystem::update() {
+ this->clear_screen();
+ this->update_camera();
+ this->render();
+ this->present_screen();
+}
+
+bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) {
+
ComponentManager & mgr = this->component_manager;
- vector<reference_wrapper<Sprite>> sprites = mgr.get_components_by_type<Sprite>();
- vector<reference_wrapper<Sprite>> sorted_sprites = this->sort(sprites);
- for (const Sprite & sprite : sorted_sprites) {
- auto transforms = mgr.get_components_by_id<Transform>(sprite.game_object_id);
- this->context.draw(sprite, transforms[0], *this->curr_cam_ref);
+ 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::update() {
- this->clear_screen();
- this->update_camera();
- this->render_sprites();
- this->present_screen();
+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 57b9c73..30b41cf 100644
--- a/src/crepe/system/RenderSystem.h
+++ b/src/crepe/system/RenderSystem.h
@@ -6,6 +6,7 @@
#include "facade/SDLContext.h"
#include "System.h"
+#include <cmath>
namespace crepe {
@@ -38,8 +39,25 @@ private:
//! Updates the active camera used for rendering.
void update_camera();
- //! Renders all active sprites to the screen.
- void render_sprites();
+ //! 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
@@ -47,12 +65,10 @@ private:
* \param objs the vector that will do a sorting algorithm on
* \return returns a sorted reference vector
*/
- std::vector<std::reference_wrapper<Sprite>>
- sort(std::vector<std::reference_wrapper<Sprite>> & objs);
+ 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.
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();
}
diff --git a/src/doc/feature/gameobject.dox b/src/doc/feature/gameobject.dox
new file mode 100644
index 0000000..c561874
--- /dev/null
+++ b/src/doc/feature/gameobject.dox
@@ -0,0 +1,18 @@
+// vim:ft=doxygen
+namespace crepe {
+/**
+
+\defgroup feature_gameobject GameObjects
+\ingroup feature
+\brief GameObject to create a Scene
+
+GameObjects are the fundamental building blocks of a Scene. They represent entities
+in the game world and can have various components attached to them to define their
+behavior and properties. GameObjects can be created and modified within the
+Scene, allowing for a flexible and dynamic game environment.
+
+\see Component
+\see Scene
+
+*/
+}
diff --git a/src/doc/feature/scene.dox b/src/doc/feature/scene.dox
new file mode 100644
index 0000000..5f34446
--- /dev/null
+++ b/src/doc/feature/scene.dox
@@ -0,0 +1,67 @@
+// vim:ft=doxygen
+namespace crepe {
+/**
+
+\defgroup feature_scene Scenes
+\ingroup feature
+\brief User-defined scenes
+
+Scenes can be used to implement game environments, and allow arbitrary game objects to be organized
+as part of the game structure. Scenes are implemented as derivative classes of Scene, which are
+added to the game using the SceneManager. Scenes describe the start of a Scene and cannot modify
+GameObjects during runtime of a Scene (use \ref feature_script "Scripting" for this purpose).
+
+\see SceneManager
+\see GameObject
+\see Script
+\see Scene
+
+\par Example
+
+This example demonstrates how to define and add scenes to the loop/scene manager in the `crepe` framework.
+Each concrete scene should be derived from Scene. In the example below, the concrete scene is named MyScene.
+A concrete scene should, at least, implement (override) two methods, namely load_scene() and get_name(). The
+scene is build (using GameObjects) in the load_scene() method. GameObjects should be made using the
+component_manager::new_object(). In the example below, two GameObjects (named object1 and object2) are added
+to MyScene. object1 and object2 do not have any non-default Components attached to them, however, if needed,
+this should also be done in load_scene(). Each concrete scene must have a unique name. This unique name is
+used to load a new concrete scene (via a Script). The unique name is set using the get_name() method. In the
+example below, MyScene's unique name is my_scene.
+After setting up one or more concrete scene(s), the concrete scene(s) should be added to the loop/scene manager.
+This is done in your main(). Firstly, the LoopManager should be instantiated. Than, all the concrete scene(s)
+should be added to the loop/scene manger via loop_mgr::add_scene<>(). The templated argument should define the
+concrete scene to be added.
+
+```cpp
+#include <crepe/api/LoopManager.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Scene.h>
+#include <crepe/api/Vector2.h>
+
+using namespace crepe;
+
+class MyScene : public Scene {
+public:
+ using Scene::Scene;
+
+ void load_scene() {
+ auto & mgr = this->component_manager;
+ GameObject object1 = mgr.new_object("object1", "tag_my_scene", Vector2{0, 0}, 0, 1);
+ GameObject object2 = mgr.new_object("object2", "tag_my_scene", Vector2{1, 0}, 0, 1);
+ }
+
+ string get_name() const { return "my_scene"; }
+};
+
+int main() {
+ LoopManager loop_mgr;
+
+ // Add the scenes to the loop manager
+ loop_mgr.add_scene<MyScene>();
+
+ loop_mgr.start();
+}
+```
+
+*/
+}
diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt
index 045f4d4..560e2bc 100644
--- a/src/example/CMakeLists.txt
+++ b/src/example/CMakeLists.txt
@@ -16,15 +16,8 @@ function(add_example target_name)
add_dependencies(examples ${target_name})
endfunction()
-add_example(audio_internal)
-add_example(script)
-add_example(log)
-add_example(rendering)
add_example(asset_manager)
add_example(savemgr)
-add_example(proxy)
-add_example(db)
-add_example(game)
-add_example(events)
+add_example(rendering_particle)
add_example(gameloop)
diff --git a/src/example/audio_internal.cpp b/src/example/audio_internal.cpp
deleted file mode 100644
index 661161a..0000000
--- a/src/example/audio_internal.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/** \file
- *
- * Standalone example for usage of the internal \c Sound class.
- */
-
-#include <crepe/api/Config.h>
-#include <crepe/facade/Sound.h>
-#include <crepe/util/Log.h>
-
-#include <thread>
-
-using namespace crepe;
-using namespace std;
-using namespace std::chrono_literals;
-using std::make_unique;
-
-// Unrelated stuff that is not part of this POC
-int _ = []() {
- // Show dbg_trace() output
- auto & cfg = Config::get_instance();
- cfg.log.level = Log::Level::TRACE;
-
- return 0; // satisfy compiler
-}();
-
-int main() {
- // Load a background track (Ogg Vorbis)
- auto bgm = Sound("../mwe/audio/bgm.ogg");
- // Load three short samples (WAV)
- auto sfx1 = Sound("../mwe/audio/sfx1.wav");
- auto sfx2 = Sound("../mwe/audio/sfx2.wav");
- auto sfx3 = Sound("../mwe/audio/sfx3.wav");
-
- // Start the background track
- bgm.play();
-
- // Play each sample sequentially while pausing and resuming the background track
- this_thread::sleep_for(500ms);
- sfx1.play();
- this_thread::sleep_for(500ms);
- sfx2.play();
- bgm.pause();
- this_thread::sleep_for(500ms);
- sfx3.play();
- bgm.play();
- this_thread::sleep_for(500ms);
-
- // Play all samples simultaniously
- sfx1.play();
- sfx2.play();
- sfx3.play();
- this_thread::sleep_for(1000ms);
-
- // Stop all audio and exit
- return EXIT_SUCCESS;
-}
diff --git a/src/example/db.cpp b/src/example/db.cpp
deleted file mode 100644
index ee4e8fc..0000000
--- a/src/example/db.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <crepe/api/Config.h>
-#include <crepe/facade/DB.h>
-#include <crepe/util/Log.h>
-
-using namespace crepe;
-using namespace std;
-
-// run before main
-static auto _ = []() {
- auto & cfg = Config::get_instance();
- cfg.log.level = Log::Level::TRACE;
- return 0;
-}();
-
-int main() {
- dbg_trace();
-
- DB db("file.db");
-
- const char * test_key = "test-key";
- string test_data = "Hello world!";
-
- dbg_logf("DB has key = {}", db.has(test_key));
-
- db.set(test_key, test_data);
-
- dbg_logf("key = \"{}\"", db.get(test_key));
-
- return 0;
-}
diff --git a/src/example/log.cpp b/src/example/log.cpp
deleted file mode 100644
index 5baa021..0000000
--- a/src/example/log.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/** \file
- *
- * Standalone example for usage of the logging functions
- */
-
-#include <crepe/api/Config.h>
-#include <crepe/util/Log.h>
-
-using namespace crepe;
-
-// unrelated setup code
-int _ = []() {
- // make sure all log messages get printed
- auto & cfg = Config::get_instance();
- cfg.log.level = Log::Level::TRACE;
-
- return 0; // satisfy compiler
-}();
-
-int main() {
- dbg_trace();
- dbg_log("debug message");
- Log::logf("info message with variable: {}", 3);
- Log::logf(Log::Level::WARNING, "warning");
- Log::logf(Log::Level::ERROR, "error");
-
- return 0;
-}
diff --git a/src/example/proxy.cpp b/src/example/proxy.cpp
deleted file mode 100644
index 69451f8..0000000
--- a/src/example/proxy.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/** \file
- *
- * Standalone example for usage of the proxy type
- */
-
-#include <crepe/ValueBroker.h>
-#include <crepe/api/Config.h>
-#include <crepe/util/Log.h>
-#include <crepe/util/Proxy.h>
-
-using namespace std;
-using namespace crepe;
-
-void test_ro_ref(const int & val) {}
-void test_rw_ref(int & val) {}
-void test_ro_val(int val) {}
-
-int main() {
- auto & cfg = Config::get_instance();
- cfg.log.level = Log::Level::DEBUG;
-
- int real_value = 0;
-
- ValueBroker<int> broker{
- [&real_value](const int & target) {
- dbg_logf("set {} to {}", real_value, target);
- real_value = target;
- },
- [&real_value]() -> const int & {
- dbg_logf("get {}", real_value);
- return real_value;
- },
- };
-
- Proxy<int> proxy{broker};
-
- broker.set(54);
- proxy = 84;
-
- test_ro_ref(proxy); // this is allowed
- // test_rw_ref(proxy); // this should throw a compile error
- test_ro_val(proxy);
-
- return 0;
-}
diff --git a/src/example/rendering_particle.cpp b/src/example/rendering_particle.cpp
new file mode 100644
index 0000000..4571afb
--- /dev/null
+++ b/src/example/rendering_particle.cpp
@@ -0,0 +1,71 @@
+#include "api/Camera.h"
+#include "system/ParticleSystem.h"
+#include <SDL2/SDL_timer.h>
+#include <crepe/ComponentManager.h>
+
+#include <crepe/Component.h>
+#include <crepe/api/Color.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/ParticleEmitter.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/api/Texture.h>
+#include <crepe/api/Transform.h>
+#include <crepe/api/Vector2.h>
+#include <crepe/system/RenderSystem.h>
+
+#include <chrono>
+#include <iostream>
+#include <memory>
+
+using namespace crepe;
+using namespace std;
+
+int main(int argc, char * argv[]) {
+ ComponentManager mgr;
+ GameObject game_object = mgr.new_object("", "", Vector2{100, 100}, 0, 0.1);
+ RenderSystem sys{mgr};
+ ParticleSystem psys{mgr};
+
+ Color color(255, 255, 255, 255);
+
+ Sprite & test_sprite = game_object.add_component<Sprite>(
+ make_shared<Texture>("../asset/texture/img.png"), color, FlipSettings{false, false});
+ test_sprite.order_in_layer = 5;
+
+ auto & test = game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{
+ .position = {0, 0},
+ .max_particles = 10,
+ .emission_rate = 0.1,
+ .min_speed = 6,
+ .max_speed = 20,
+ .min_angle = -20,
+ .max_angle = 20,
+ .begin_lifespan = 0,
+ .end_lifespan = 60,
+ .force_over_time = Vector2{0, 0},
+ .boundary{
+ .width = 1000,
+ .height = 1000,
+ .offset = Vector2{0, 0},
+ .reset_on_exit = false,
+ },
+ .sprite = test_sprite,
+ });
+ game_object.add_component<Camera>(Color::WHITE);
+
+ game_object
+ .add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"), color,
+ FlipSettings{false, false})
+ .order_in_layer
+ = 6;
+
+ auto start = std::chrono::steady_clock::now();
+ while (std::chrono::steady_clock::now() - start < std::chrono::seconds(5)) {
+ psys.update();
+ sys.update();
+ SDL_Delay(10);
+ }
+
+ return 0;
+}
diff --git a/src/example/script.cpp b/src/example/script.cpp
deleted file mode 100644
index a23295b..0000000
--- a/src/example/script.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/** \file
- *
- * Standalone example for usage of the script component and system
- */
-
-#include <crepe/ComponentManager.h>
-#include <crepe/system/ScriptSystem.h>
-#include <crepe/util/Log.h>
-
-#include <crepe/api/BehaviorScript.h>
-#include <crepe/api/Config.h>
-#include <crepe/api/GameObject.h>
-#include <crepe/api/Script.h>
-#include <crepe/api/Transform.h>
-
-using namespace crepe;
-using namespace std;
-
-// Unrelated stuff that is not part of this POC
-int _ = []() {
- // Show dbg_trace() output
- auto & cfg = Config::get_instance();
- cfg.log.level = Log::Level::TRACE;
-
- return 0; // satisfy compiler
-}();
-
-// User-defined script:
-class MyScript : public Script {
- void update() {
- // Retrieve component from the same GameObject this script is on
- Transform & test = get_component<Transform>();
- dbg_logf("Transform({:.2f}, {:.2f})", test.position.x, test.position.y);
- }
-};
-
-int main() {
- ComponentManager component_manager{};
- ScriptSystem system{component_manager};
-
- // Create game object with Transform and BehaviorScript components
- GameObject obj = component_manager.new_object("name");
- obj.add_component<BehaviorScript>().set_script<MyScript>();
-
- // Update all scripts. This should result in MyScript::update being called
- system.update();
-
- return EXIT_SUCCESS;
-}
diff --git a/src/test/AssetTest.cpp b/src/test/AssetTest.cpp
new file mode 100644
index 0000000..8aa7629
--- /dev/null
+++ b/src/test/AssetTest.cpp
@@ -0,0 +1,26 @@
+#include <gtest/gtest.h>
+
+#include <crepe/api/Asset.h>
+#include <crepe/api/Config.h>
+
+using namespace std;
+using namespace crepe;
+using namespace testing;
+
+class AssetTest : public Test {
+public:
+ Config & cfg = Config::get_instance();
+ void SetUp() override { this->cfg.asset.root_pattern = ".crepe-root"; }
+};
+
+TEST_F(AssetTest, Existant) { ASSERT_NO_THROW(Asset{"asset/texture/img.png"}); }
+
+TEST_F(AssetTest, Nonexistant) { ASSERT_ANY_THROW(Asset{"asset/nonexistant"}); }
+
+TEST_F(AssetTest, Rootless) {
+ cfg.asset.root_pattern.clear();
+
+ string arbitrary = "\\/this is / /../passed through as-is";
+ Asset asset{arbitrary};
+ ASSERT_EQ(arbitrary, asset.get_path());
+}
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 14c5123..8be8252 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -4,7 +4,13 @@ target_sources(test_main PUBLIC
PhysicsTest.cpp
ScriptTest.cpp
ParticleTest.cpp
+ AssetTest.cpp
+ OptionalRefTest.cpp
+ RenderSystemTest.cpp
+ EventTest.cpp
ECSTest.cpp
SceneManagerTest.cpp
+ ValueBrokerTest.cpp
+ DBTest.cpp
)
diff --git a/src/test/DBTest.cpp b/src/test/DBTest.cpp
new file mode 100644
index 0000000..e80814c
--- /dev/null
+++ b/src/test/DBTest.cpp
@@ -0,0 +1,28 @@
+#include <crepe/facade/DB.h>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace crepe;
+using namespace testing;
+
+class DBTest : public Test {
+public:
+ DB db;
+};
+
+TEST_F(DBTest, ReadWrite) {
+ db.set("foo", "bar");
+ EXPECT_EQ(db.get("foo"), "bar");
+}
+
+TEST_F(DBTest, Nonexistant) {
+ EXPECT_THROW(db.get("foo"), std::out_of_range);
+ db.set("foo", "bar");
+ EXPECT_NO_THROW(db.get("foo"));
+}
+
+TEST_F(DBTest, Has) {
+ EXPECT_EQ(db.has("foo"), false);
+ db.set("foo", "bar");
+ EXPECT_EQ(db.has("foo"), true);
+}
diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp
new file mode 100644
index 0000000..b0e6c9c
--- /dev/null
+++ b/src/test/EventTest.cpp
@@ -0,0 +1,254 @@
+
+#include "api/Event.h"
+#include "api/EventManager.h"
+#include "api/IKeyListener.h"
+#include "api/IMouseListener.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+using namespace std;
+using namespace std::chrono_literals;
+using namespace crepe;
+
+class EventManagerTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ // Clear any existing subscriptions or events before each test
+ EventManager::get_instance().clear();
+ }
+
+ void TearDown() override {
+ // Ensure cleanup after each test
+ EventManager::get_instance().clear();
+ }
+};
+class MockKeyListener : public IKeyListener {
+public:
+ MOCK_METHOD(bool, on_key_pressed, (const KeyPressEvent & event), (override));
+ MOCK_METHOD(bool, on_key_released, (const KeyReleaseEvent & event), (override));
+};
+
+class MockMouseListener : public IMouseListener {
+public:
+ MOCK_METHOD(bool, on_mouse_clicked, (const MouseClickEvent & event), (override));
+ MOCK_METHOD(bool, on_mouse_pressed, (const MousePressEvent & event), (override));
+ MOCK_METHOD(bool, on_mouse_released, (const MouseReleaseEvent & event), (override));
+ MOCK_METHOD(bool, on_mouse_moved, (const MouseMoveEvent & event), (override));
+};
+
+TEST_F(EventManagerTest, EventSubscription) {
+ EventHandler<KeyPressEvent> key_handler = [](const KeyPressEvent & e) {
+ std::cout << "Key Event Triggered" << std::endl;
+ return true;
+ };
+
+ // Subscribe to KeyPressEvent
+ EventManager::get_instance().subscribe<KeyPressEvent>(key_handler, 1);
+
+ // Verify subscription (not directly verifiable; test by triggering event)
+
+ EventManager::get_instance().trigger_event<KeyPressEvent>(
+ KeyPressEvent{
+ .repeat = true,
+ .key = Keycode::A,
+ },
+ 1);
+ EventManager::get_instance().trigger_event<KeyPressEvent>(
+ KeyPressEvent{
+ .repeat = true,
+ .key = Keycode::A,
+
+ },
+ EventManager::CHANNEL_ALL);
+}
+TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) {
+ bool triggered = false;
+
+ EventHandler<MouseClickEvent> mouse_handler = [&](const MouseClickEvent & e) {
+ triggered = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false;
+ };
+ EventManager::get_instance().subscribe<MouseClickEvent>(mouse_handler,
+ EventManager::CHANNEL_ALL);
+
+ MouseClickEvent click_event{
+ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE};
+ EventManager::get_instance().trigger_event<MouseClickEvent>(click_event,
+ EventManager::CHANNEL_ALL);
+
+ EXPECT_TRUE(triggered);
+}
+TEST_F(EventManagerTest, EventManagerTest_trigger_one_channel) {
+ bool triggered = false;
+ int test_channel = 1;
+ EventHandler<MouseClickEvent> mouse_handler = [&](const MouseClickEvent & e) {
+ triggered = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false;
+ };
+ EventManager::get_instance().subscribe<MouseClickEvent>(mouse_handler, test_channel);
+
+ MouseClickEvent click_event{
+ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE};
+ EventManager::get_instance().trigger_event<MouseClickEvent>(click_event,
+ EventManager::CHANNEL_ALL);
+
+ EXPECT_FALSE(triggered);
+ EventManager::get_instance().trigger_event<MouseClickEvent>(click_event, test_channel);
+}
+
+TEST_F(EventManagerTest, EventManagerTest_callback_propagation) {
+ EventManager & event_manager = EventManager::get_instance();
+
+ // Flags to track handler calls
+ bool triggered_true = false;
+ bool triggered_false = false;
+
+ // Handlers
+ EventHandler<MouseClickEvent> mouse_handler_true = [&](const MouseClickEvent & e) {
+ triggered_true = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return true; // Stops propagation
+ };
+
+ EventHandler<MouseClickEvent> mouse_handler_false = [&](const MouseClickEvent & e) {
+ triggered_false = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false; // Allows propagation
+ };
+
+ // Test event
+ MouseClickEvent click_event{
+ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE};
+ event_manager.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL);
+ event_manager.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL);
+
+ // Trigger event
+ event_manager.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL);
+
+ // Check that only the true handler was triggered
+ EXPECT_TRUE(triggered_true);
+ EXPECT_FALSE(triggered_false);
+
+ // Reset and clear
+ triggered_true = false;
+ triggered_false = false;
+ event_manager.clear();
+ event_manager.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL);
+ event_manager.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL);
+
+ // Trigger event again
+ event_manager.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL);
+
+ // Check that both handlers were triggered
+ EXPECT_TRUE(triggered_true);
+ EXPECT_TRUE(triggered_false);
+}
+
+TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) {
+ EventManager & event_manager = EventManager::get_instance();
+ bool triggered1 = false;
+ bool triggered2 = false;
+ int test_channel = 1;
+ EventHandler<MouseClickEvent> mouse_handler1 = [&](const MouseClickEvent & e) {
+ triggered1 = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false; // Allows propagation
+ };
+ EventHandler<MouseClickEvent> mouse_handler2 = [&](const MouseClickEvent & e) {
+ triggered2 = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false; // Allows propagation
+ };
+ event_manager.subscribe<MouseClickEvent>(mouse_handler1);
+ event_manager.subscribe<MouseClickEvent>(mouse_handler2, test_channel);
+
+ event_manager.queue_event<MouseClickEvent>(
+ MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE});
+ event_manager.queue_event<MouseClickEvent>(
+ MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE},
+ test_channel);
+ event_manager.dispatch_events();
+ EXPECT_TRUE(triggered1);
+ EXPECT_TRUE(triggered2);
+}
+
+TEST_F(EventManagerTest, EventManagerTest_unsubscribe) {
+ EventManager & event_manager = EventManager::get_instance();
+
+ // Flags to track if handlers are triggered
+ bool triggered1 = false;
+ bool triggered2 = false;
+
+ // Define EventHandlers
+ EventHandler<MouseClickEvent> mouse_handler1 = [&](const MouseClickEvent & e) {
+ triggered1 = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false; // Allows propagation
+ };
+
+ EventHandler<MouseClickEvent> mouse_handler2 = [&](const MouseClickEvent & e) {
+ triggered2 = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false; // Allows propagation
+ };
+ // Subscribe handlers
+ subscription_t handler1_id = event_manager.subscribe<MouseClickEvent>(mouse_handler1);
+ subscription_t handler2_id = event_manager.subscribe<MouseClickEvent>(mouse_handler2);
+
+ // Queue events
+ event_manager.queue_event<MouseClickEvent>(
+ MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE});
+
+ // Dispatch events - both handlers should be triggered
+ event_manager.dispatch_events();
+ EXPECT_TRUE(triggered1); // Handler 1 should be triggered
+ EXPECT_TRUE(triggered2); // Handler 2 should be triggered
+
+ // Reset flags
+ triggered1 = false;
+ triggered2 = false;
+
+ // Unsubscribe handler1
+ event_manager.unsubscribe(handler1_id);
+
+ // Queue the same event again
+ event_manager.queue_event<MouseClickEvent>(
+ MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE});
+
+ // Dispatch events - only handler 2 should be triggered, handler 1 should NOT
+ event_manager.dispatch_events();
+ EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered
+ EXPECT_TRUE(triggered2); // Handler 2 should be triggered
+
+ // Reset flags
+ triggered2 = false;
+
+ // Unsubscribe handler2
+ event_manager.unsubscribe(handler2_id);
+
+ // Queue the event again
+ event_manager.queue_event<MouseClickEvent>(
+ MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE});
+
+ // Dispatch events - no handler should be triggered
+ event_manager.dispatch_events();
+ EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered
+ EXPECT_FALSE(triggered2); // Handler 2 should NOT be triggered
+}
diff --git a/src/test/OptionalRefTest.cpp b/src/test/OptionalRefTest.cpp
new file mode 100644
index 0000000..83f7b23
--- /dev/null
+++ b/src/test/OptionalRefTest.cpp
@@ -0,0 +1,42 @@
+#include <gtest/gtest.h>
+
+#include <crepe/util/OptionalRef.h>
+
+using namespace std;
+using namespace crepe;
+using namespace testing;
+
+TEST(OptionalRefTest, Normal) {
+ string value = "foo";
+ OptionalRef<string> ref = value;
+
+ EXPECT_TRUE(ref);
+ ASSERT_NO_THROW({
+ string & value_ref = ref;
+ EXPECT_EQ(value_ref, value);
+ });
+
+ ref.clear();
+ EXPECT_FALSE(ref);
+ ASSERT_THROW({ string & value_ref = ref; }, runtime_error);
+}
+
+TEST(OptionalRefTest, Empty) {
+ string value = "foo";
+ OptionalRef<string> ref;
+
+ EXPECT_FALSE(ref);
+ ASSERT_THROW({ string & value_ref = ref; }, runtime_error);
+}
+
+TEST(OptionalRefTest, Chain) {
+ string value = "foo";
+ OptionalRef<string> ref1 = value;
+ OptionalRef<string> ref2 = ref1;
+
+ EXPECT_TRUE(ref2);
+ string & value_ref = ref2;
+ EXPECT_EQ(value_ref, value);
+ value_ref = "bar";
+ EXPECT_EQ(value_ref, value);
+}
diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp
index 4e655a9..eee022f 100644
--- a/src/test/ParticleTest.cpp
+++ b/src/test/ParticleTest.cpp
@@ -28,8 +28,8 @@ public:
GameObject game_object = mgr.new_object("", "", Vector2{0, 0}, 0, 0);
Color color(0, 0, 0, 0);
- Sprite test_sprite = game_object.add_component<Sprite>(
- make_shared<Texture>("../asset/texture/img.png"), color,
+ Sprite & test_sprite = game_object.add_component<Sprite>(
+ make_shared<Texture>("asset/texture/img.png"), color,
FlipSettings{true, true});
game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{
diff --git a/src/test/SceneManagerTest.cpp b/src/test/SceneManagerTest.cpp
index 69e1171..1efcfb2 100644
--- a/src/test/SceneManagerTest.cpp
+++ b/src/test/SceneManagerTest.cpp
@@ -20,6 +20,8 @@ public:
GameObject object2 = mgr.new_object("scene_1", "tag_scene_1", Vector2{1, 0}, 0, 1);
GameObject object3 = mgr.new_object("scene_1", "tag_scene_1", Vector2{2, 0}, 0, 1);
}
+
+ string get_name() const { return "scene1"; }
};
class ConcreteScene2 : public Scene {
@@ -33,6 +35,8 @@ public:
GameObject object3 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 2}, 0, 1);
GameObject object4 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 3}, 0, 1);
}
+
+ string get_name() const { return "scene2"; }
};
class SceneManagerTest : public ::testing::Test {
@@ -42,8 +46,8 @@ public:
};
TEST_F(SceneManagerTest, loadScene) {
- scene_mgr.add_scene<ConcreteScene1>("scene1");
- scene_mgr.add_scene<ConcreteScene2>("scene2");
+ scene_mgr.add_scene<ConcreteScene1>();
+ scene_mgr.add_scene<ConcreteScene2>();
scene_mgr.load_next_scene();
diff --git a/src/test/ValueBrokerTest.cpp b/src/test/ValueBrokerTest.cpp
new file mode 100644
index 0000000..e6bb058
--- /dev/null
+++ b/src/test/ValueBrokerTest.cpp
@@ -0,0 +1,63 @@
+#include <gtest/gtest.h>
+
+#include <crepe/ValueBroker.h>
+#include <crepe/util/Proxy.h>
+
+using namespace std;
+using namespace crepe;
+using namespace testing;
+
+class ValueBrokerTest : public Test {
+public:
+ int read_count = 0;
+ int write_count = 0;
+ int value = 0;
+
+ ValueBroker<int> broker{
+ [this](const int & target) -> void {
+ this->write_count++;
+ this->value = target;
+ },
+ [this]() -> const int & {
+ this->read_count++;
+ return this->value;
+ },
+ };
+ Proxy<int> proxy{broker};
+
+ void SetUp() override {
+ ASSERT_EQ(read_count, 0);
+ ASSERT_EQ(write_count, 0);
+ }
+};
+
+TEST_F(ValueBrokerTest, BrokerWrite) {
+ broker.set(0);
+ EXPECT_EQ(read_count, 0);
+ EXPECT_EQ(write_count, 1);
+}
+
+TEST_F(ValueBrokerTest, BrokerRead) {
+ broker.get();
+ EXPECT_EQ(read_count, 1);
+ EXPECT_EQ(write_count, 0);
+}
+
+TEST_F(ValueBrokerTest, ProxyWrite) {
+ proxy = 0;
+ EXPECT_EQ(read_count, 0);
+ EXPECT_EQ(write_count, 1);
+}
+
+void dummy(int) {}
+TEST_F(ValueBrokerTest, ProxyRead) {
+ dummy(proxy);
+ EXPECT_EQ(read_count, 1);
+ EXPECT_EQ(write_count, 0);
+}
+
+TEST_F(ValueBrokerTest, ProxyReadWrite) {
+ proxy = proxy;
+ ASSERT_EQ(read_count, 1);
+ ASSERT_EQ(write_count, 1);
+}