diff options
author | JAROWMR <jarorutjes07@gmail.com> | 2024-11-19 20:49:09 +0100 |
---|---|---|
committer | JAROWMR <jarorutjes07@gmail.com> | 2024-11-19 20:49:09 +0100 |
commit | 0f9f379a4dec7ad9d1dce30af5e2259931692c5f (patch) | |
tree | d84a76559d99fd6a3541d0087538564d79e39470 /src | |
parent | 38a2476bcfa41b6d83a9a72d35f5acb684dc87fd (diff) | |
parent | 0476a8e9dbe7afb422862f7b1c15aaed7f3c416e (diff) |
merge with master
Diffstat (limited to 'src')
117 files changed, 1626 insertions, 1192 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4922df..445a8b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,6 +38,5 @@ install( target_link_libraries(test_main PRIVATE gtest - PRIVATE gtest_main PUBLIC crepe ) diff --git a/src/crepe/Asset.cpp b/src/crepe/Asset.cpp index 8a2a11c..9c41ecb 100644 --- a/src/crepe/Asset.cpp +++ b/src/crepe/Asset.cpp @@ -3,14 +3,14 @@ #include "Asset.h" using namespace crepe; +using namespace std; -Asset::Asset(const std::string & src) { - // FIXME: restore this - // this->src = std::filesystem::canonical(src); - this->src = src; +// 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); } -const std::istream & Asset::read() { return this->file; } +istream & Asset::get_stream() { return this->file; } -const char * Asset::canonical() { return this->src.c_str(); } +const string & Asset::get_canonical() const { return this->src; } diff --git a/src/crepe/Asset.h b/src/crepe/Asset.h index 0cb5834..9051c5e 100644 --- a/src/crepe/Asset.h +++ b/src/crepe/Asset.h @@ -9,8 +9,8 @@ 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. + * 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: @@ -20,13 +20,21 @@ public: Asset(const std::string & src); public: - //! Get an input stream to the contents of this resource - const std::istream & read(); - //! Get the canonical path to this resource - const char * canonical(); + /** + * \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: - std::string src; + //! Canonical path to asset + const std::string src; + //! File handle (stream) std::ifstream file; }; diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index fc95bd3..3b05742 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -4,7 +4,6 @@ target_sources(crepe PUBLIC ComponentManager.cpp Component.cpp Collider.cpp - Exception.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -15,7 +14,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Collider.h ValueBroker.h ValueBroker.hpp - Exception.h ) add_subdirectory(api) diff --git a/src/crepe/Component.cpp b/src/crepe/Component.cpp index 73c2d07..acfd35c 100644 --- a/src/crepe/Component.cpp +++ b/src/crepe/Component.cpp @@ -1,5 +1,4 @@ #include "Component.h" -#include "types.h" using namespace crepe; diff --git a/src/crepe/Component.h b/src/crepe/Component.h index 0fe60b2..5279fb3 100644 --- a/src/crepe/Component.h +++ b/src/crepe/Component.h @@ -2,8 +2,6 @@ #include "types.h" -#include <cstdint> - namespace crepe { class ComponentManager; @@ -11,36 +9,37 @@ class ComponentManager; /** * \brief Base class for all components * - * This class is the base class for all components. It provides a common - * interface for all components. + * This class is the base class for all components. It provides a common interface for all + * components. */ class Component { +public: + //! Whether the component is active + bool active = true; + //! The id of the GameObject this component belongs to + const game_object_id_t game_object_id; + protected: - //! Only the ComponentManager can create components - friend class crepe::ComponentManager; /** * \param id The id of the GameObject this component belongs to */ Component(game_object_id_t id); + //! Only the ComponentManager can create components + friend class ComponentManager; public: virtual ~Component() = default; + +public: /** * \brief Get the maximum number of instances for this component * - * This method returns -1 by default, which means that there is no limit - * for the number of instances. Concrete components can override this method - * to set a limit. + * This method returns -1 by default, which means that there is no limit for the number of + * instances. Concrete components can override this method to set a limit. * * \return The maximum number of instances for this component */ virtual int get_instances_max() const { return -1; } - -public: - //! The id of the GameObject this component belongs to - const game_object_id_t game_object_id; - //! Whether the component is active - bool active = true; }; } // namespace crepe diff --git a/src/crepe/ComponentManager.cpp b/src/crepe/ComponentManager.cpp index 85149c8..e310577 100644 --- a/src/crepe/ComponentManager.cpp +++ b/src/crepe/ComponentManager.cpp @@ -1,13 +1,13 @@ -#include "util/log.h" +#include "api/GameObject.h" +#include "util/Log.h" #include "ComponentManager.h" using namespace crepe; +using namespace std; -ComponentManager & ComponentManager::get_instance() { - static ComponentManager instance; - return instance; -} +ComponentManager::ComponentManager() { dbg_trace(); } +ComponentManager::~ComponentManager() { dbg_trace(); } void ComponentManager::delete_all_components_of_id(game_object_id_t id) { // Loop through all the types (in the unordered_map<>) @@ -21,10 +21,14 @@ void ComponentManager::delete_all_components_of_id(game_object_id_t id) { } void ComponentManager::delete_all_components() { - // Clear the whole unordered_map<> this->components.clear(); + this->next_id = 0; } -ComponentManager::ComponentManager() { dbg_trace(); } - -ComponentManager::~ComponentManager() { dbg_trace(); } +GameObject ComponentManager::new_object(const string & name, const string & tag, + const Vector2 & position, double rotation, + double scale) { + GameObject object{*this, this->next_id, name, tag, position, rotation, scale}; + this->next_id++; + return object; +} diff --git a/src/crepe/ComponentManager.h b/src/crepe/ComponentManager.h index c8c196c..2107453 100644 --- a/src/crepe/ComponentManager.h +++ b/src/crepe/ComponentManager.h @@ -1,40 +1,63 @@ #pragma once -#include <cstdint> #include <memory> #include <typeindex> #include <unordered_map> #include <vector> +#include "api/Vector2.h" + #include "Component.h" namespace crepe { +class GameObject; + /** * \brief Manages all components * - * This class manages all components. It provides methods to add, delete and get - * components. + * This class manages all components. It provides methods to add, delete and get components. */ class ComponentManager { + // TODO: This relation should be removed! I (loek) believe that the scene manager should + // create/destroy components because the GameObject's are stored in concrete Scene classes, + // which will in turn call GameObject's destructor, which will in turn call + // ComponentManager::delete_components_by_id or something. This is a pretty major change, so + // here is a comment and temporary fix instead :tada: + friend class SceneManager; + public: + ComponentManager(); // dbg_trace + ~ComponentManager(); // dbg_trace + /** - * \brief Get the instance of the ComponentManager - * - * \return The instance of the ComponentManager + * \brief Create a new game object using the component manager + * + * \param name Metadata::name (required) + * \param tag Metadata::tag (optional, empty by default) + * \param position Transform::position (optional, origin by default) + * \param rotation Transform::rotation (optional, 0 by default) + * \param scale Transform::scale (optional, 1 by default) + * + * \returns GameObject interface + * + * \note This method automatically assigns a new entity ID */ - static ComponentManager & get_instance(); - ComponentManager(const ComponentManager &) = delete; - ComponentManager(ComponentManager &&) = delete; - ComponentManager & operator=(const ComponentManager &) = delete; - ComponentManager & operator=(ComponentManager &&) = delete; - ~ComponentManager(); + GameObject new_object(const std::string & name, const std::string & tag = "", + const Vector2 & position = {0, 0}, double rotation = 0, + double scale = 1); +protected: + /** + * GameObject is used as an interface to add/remove components, and the game programmer is + * supposed to use it instead of interfacing with the component manager directly. + */ + friend class GameObject; /** * \brief Add a component to the ComponentManager * - * This method adds a component to the ComponentManager. The component is - * created with the given arguments and added to the ComponentManager. + * This method adds a component to the ComponentManager. The component is created with the + * given arguments and added to the ComponentManager. * * \tparam T The type of the component * \tparam Args The types of the arguments @@ -77,6 +100,8 @@ public: * This method deletes all components. */ void delete_all_components(); + +public: /** * \brief Get all components of a specific type and id * @@ -87,8 +112,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; + std::vector<std::reference_wrapper<T>> get_components_by_id(game_object_id_t id) const; /** * \brief Get all components of a specific type * @@ -101,23 +125,21 @@ public: std::vector<std::reference_wrapper<T>> get_components_by_type() const; private: - ComponentManager(); - -private: /** * \brief The components * - * This unordered_map stores all components. The key is the type of the - * component and the value is a vector of vectors of unique pointers to the - * components. - * Every component type has its own vector of vectors of unique pointers to - * the components. The first vector is for the ids of the GameObjects and the - * second vector is for the components (because a GameObject might have multiple - * components). + * This unordered_map stores all components. The key is the type of the component and the + * value is a vector of vectors of unique pointers to the components. + * + * Every component type has its own vector of vectors of unique pointers to the components. + * The first vector is for the ids of the GameObjects and the second vector is for the + * components (because a GameObject might have multiple components). */ - std::unordered_map<std::type_index, - std::vector<std::vector<std::unique_ptr<Component>>>> + std::unordered_map<std::type_index, std::vector<std::vector<std::unique_ptr<Component>>>> components; + + //! ID of next GameObject allocated by \c ComponentManager::new_object + game_object_id_t next_id = 0; }; } // namespace crepe diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/ComponentManager.hpp index 98efb49..be99cac 100644 --- a/src/crepe/ComponentManager.hpp +++ b/src/crepe/ComponentManager.hpp @@ -40,7 +40,6 @@ T & ComponentManager::add_component(game_object_id_t id, Args &&... args) { // Check if the vector size is not greater than get_instances_max int max_instances = instance->get_instances_max(); if (max_instances != -1 && components[type][id].size() >= max_instances) { - // TODO: Exception throw std::runtime_error( "Exceeded maximum number of instances for this component type"); } @@ -61,8 +60,7 @@ void ComponentManager::delete_components_by_id(game_object_id_t id) { // Find the type (in the unordered_map<>) if (this->components.find(type) != this->components.end()) { // Get the correct vector<> - vector<vector<unique_ptr<Component>>> & component_array - = this->components[type]; + vector<vector<unique_ptr<Component>>> & component_array = this->components[type]; // Make sure that the id (that we are looking for) is within the boundaries of the vector<> if (id < component_array.size()) { @@ -93,12 +91,10 @@ ComponentManager::get_components_by_id(game_object_id_t id) const { // Create an empty vector<> vector<reference_wrapper<T>> component_vector; - if (this->components.find(type) == this->components.end()) - return component_vector; + if (this->components.find(type) == this->components.end()) return component_vector; // Get the correct vector<> - const vector<vector<unique_ptr<Component>>> & component_array - = this->components.at(type); + const vector<vector<unique_ptr<Component>>> & component_array = this->components.at(type); // Make sure that the id (that we are looking for) is within the boundaries of the vector<> if (id >= component_array.size()) return component_vector; @@ -118,8 +114,7 @@ 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 { +std::vector<std::reference_wrapper<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<>) @@ -129,12 +124,10 @@ ComponentManager::get_components_by_type() const { vector<reference_wrapper<T>> component_vector; // Find the type (in the unordered_map<>) - if (this->components.find(type) == this->components.end()) - return component_vector; + if (this->components.find(type) == this->components.end()) return component_vector; // Get the correct vector<> - const vector<vector<unique_ptr<Component>>> & component_array - = this->components.at(type); + const vector<vector<unique_ptr<Component>>> & component_array = this->components.at(type); // Loop through the whole vector<> for (const vector<unique_ptr<Component>> & component : component_array) { diff --git a/src/crepe/Exception.cpp b/src/crepe/Exception.cpp deleted file mode 100644 index dab8f2e..0000000 --- a/src/crepe/Exception.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include <cstdarg> - -#include "Exception.h" -#include "util/fmt.h" - -using namespace std; -using namespace crepe; - -const char * Exception::what() { return error.c_str(); } - -Exception::Exception(const char * fmt, ...) { - va_list args; - va_start(args, fmt); - this->error = va_stringf(args, fmt); - va_end(args); -} diff --git a/src/crepe/Exception.h b/src/crepe/Exception.h deleted file mode 100644 index 6473043..0000000 --- a/src/crepe/Exception.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include <exception> -#include <string> - -namespace crepe { - -//! Exception class with printf-style constructor -class Exception : public std::exception { -public: - //! printf - Exception(const char * fmt, ...); - //! Get formatted error message - const char * what(); - -protected: - Exception() = default; - //! Formatted error message - std::string error; -}; - -} // namespace crepe diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp index 582edf4..1068cbf 100644 --- a/src/crepe/Particle.cpp +++ b/src/crepe/Particle.cpp @@ -2,8 +2,8 @@ using namespace crepe; -void Particle::reset(uint32_t lifespan, const Vector2 & position, - const Vector2 & velocity, double angle) { +void Particle::reset(uint32_t lifespan, const Vector2 & position, const Vector2 & velocity, + double angle) { // Initialize the particle state this->time_in_life = 0; this->lifespan = lifespan; diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h index 3eaebc3..19859fe 100644 --- a/src/crepe/Particle.h +++ b/src/crepe/Particle.h @@ -9,9 +9,9 @@ namespace crepe { /** * \brief Represents a particle in the particle emitter. * - * This class stores information about a single particle, including its position, - * velocity, lifespan, and other properties. Particles can be updated over time - * to simulate movement and can also be reset or stopped. + * This class stores information about a single particle, including its position, velocity, + * lifespan, and other properties. Particles can be updated over time to simulate movement and + * can also be reset or stopped. */ class Particle { // TODO: add friend particleSsytem and rendersystem. Unit test will fail. @@ -35,20 +35,20 @@ public: /** * \brief Resets the particle with new properties. * - * This method initializes the particle with a specific lifespan, position, - * velocity, and angle, marking it as active and resetting its life counter. + * This method initializes the particle with a specific lifespan, position, velocity, and + * angle, marking it as active and resetting its life counter. * * \param lifespan The lifespan of the particle in amount of updates. * \param position The starting position of the particle. * \param velocity The initial velocity of the particle. * \param angle The angle of the particle's trajectory or orientation. */ - void reset(uint32_t lifespan, const Vector2 & position, - const Vector2 & velocity, double angle); + void reset(uint32_t lifespan, const Vector2 & position, const Vector2 & velocity, + double angle); /** * \brief Updates the particle's state. * - * Advances the particle's position based on its velocity and applies accumulated forces. + * Advances the particle's position based on its velocity and applies accumulated forces. * Deactivates the particle if its lifespan has expired. */ void update(); diff --git a/src/crepe/ValueBroker.h b/src/crepe/ValueBroker.h index d844d6a..673b660 100644 --- a/src/crepe/ValueBroker.h +++ b/src/crepe/ValueBroker.h @@ -7,10 +7,9 @@ namespace crepe { /** * \brief Give reference to value through custom set/get functions * - * This class can be used to abstract direct access to any arbitrary value - * through a custom get and set function passed to its constructor. Consumers - * of this type may want to wrap it in a \c Proxy so it behaves like a regular - * variable. + * This class can be used to abstract direct access to any arbitrary value through a custom get + * and set function passed to its constructor. Consumers of this type may want to wrap it in a + * \c Proxy so it behaves like a regular variable. * * \tparam T Type of the underlying variable */ diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp index 58fee2a..464b0fd 100644 --- a/src/crepe/api/Animator.cpp +++ b/src/crepe/api/Animator.cpp @@ -1,7 +1,5 @@ -#include <cstdint> - -#include "util/log.h" +#include "util/Log.h" #include "Animator.h" #include "Component.h" @@ -9,7 +7,7 @@ using namespace crepe; -Animator::Animator(uint32_t id, Sprite & ss, int row, int col, int col_animator) +Animator::Animator(game_object_id_t id, Sprite & ss, int row, int col, int col_animator) : Component(id), spritesheet(ss), row(row), diff --git a/src/crepe/api/Animator.h b/src/crepe/api/Animator.h index def0240..53f4b91 100644 --- a/src/crepe/api/Animator.h +++ b/src/crepe/api/Animator.h @@ -1,20 +1,19 @@ #pragma once -#include <cstdint> - #include "Component.h" #include "Sprite.h" namespace crepe { + class AnimatorSystem; class SDLContext; /** - * \brief The Animator component is used to animate sprites by managing the movement - * and frame changes within a sprite sheet. + * \brief The Animator component is used to animate sprites by managing the movement and frame + * changes within a sprite sheet. * - * This component allows for controlling sprite animation through rows and columns of a sprite sheet. - * It can be used to play animations, loop them, or stop them. + * This component allows for controlling sprite animation through rows and columns of a sprite + * sheet. It can be used to play animations, loop them, or stop them. */ class Animator : public Component { @@ -28,15 +27,17 @@ public: * \brief Constructs an Animator object that will control animations for a sprite sheet. * * \param id The unique identifier for the component, typically assigned automatically. - * \param spritesheet A reference to the Sprite object which holds the sprite sheet for animation. + * \param spritesheet A reference to the Sprite object which holds the sprite sheet for + * animation. * \param row The maximum number of rows in the sprite sheet. * \param col The maximum number of columns in the sprite sheet. - * \param col__animate The specific col index of the sprite sheet to animate. This allows selecting which col to animate from multiple col in the sheet. + * \param col_animate The specific col index of the sprite sheet to animate. This allows + * selecting which col to animate from multiple col in the sheet. * - * This constructor sets up the Animator with the given parameters, and initializes the animation system. + * This constructor sets up the Animator with the given parameters, and initializes the + * animation system. */ - Animator(uint32_t id, Sprite & spritesheet, int row, int col, - int col_animate); + Animator(uint32_t id, Sprite & spritesheet, int row, int col, int col_animate); ~Animator(); // dbg_trace Animator(const Animator &) = delete; diff --git a/src/crepe/api/AssetManager.cpp b/src/crepe/api/AssetManager.cpp index b891760..3925758 100644 --- a/src/crepe/api/AssetManager.cpp +++ b/src/crepe/api/AssetManager.cpp @@ -1,4 +1,4 @@ -#include "util/log.h" +#include "util/Log.h" #include "AssetManager.h" diff --git a/src/crepe/api/AssetManager.h b/src/crepe/api/AssetManager.h index 86a9902..fee6780 100644 --- a/src/crepe/api/AssetManager.h +++ b/src/crepe/api/AssetManager.h @@ -8,13 +8,12 @@ namespace crepe { /** - * \brief The AssetManager is responsible for storing and managing assets over - * multiple scenes. + * \brief The AssetManager is responsible for storing and managing assets over multiple scenes. * - * The AssetManager ensures that assets are loaded once and can be accessed - * across different scenes. It caches assets to avoid reloading them every time - * a scene is loaded. Assets are retained in memory until the AssetManager is - * destroyed, at which point the cached assets are cleared. + * The AssetManager ensures that assets are loaded once and can be accessed across different + * scenes. It caches assets to avoid reloading them every time a scene is loaded. Assets are + * retained in memory until the AssetManager is destroyed, at which point the cached assets are + * cleared. */ class AssetManager { @@ -44,20 +43,18 @@ public: * \brief Caches an asset by loading it from the given file path. * * \param file_path The path to the asset file to load. - * \param reload If true, the asset will be reloaded from the file, even if - * it is already cached. + * \param reload If true, the asset will be reloaded from the file, even if it is already + * cached. * \tparam T The type of asset to cache (e.g., texture, sound, etc.). * * \return A shared pointer to the cached asset. * - * This template function caches the asset at the given file path. If the - * asset is already cached and `reload` is false, the existing cached version - * will be returned. Otherwise, the asset will be reloaded and added to the - * cache. + * This template function caches the asset at the given file path. If the asset is already + * cached and `reload` is false, the existing cached version will be returned. Otherwise, the + * asset will be reloaded and added to the cache. */ template <typename T> - std::shared_ptr<T> cache(const std::string & file_path, - bool reload = false); + std::shared_ptr<T> cache(const std::string & file_path, bool reload = false); }; } // namespace crepe diff --git a/src/crepe/api/AssetManager.hpp b/src/crepe/api/AssetManager.hpp index 977b4e1..1c0e978 100644 --- a/src/crepe/api/AssetManager.hpp +++ b/src/crepe/api/AssetManager.hpp @@ -5,16 +5,14 @@ namespace crepe { template <typename asset> -std::shared_ptr<asset> AssetManager::cache(const std::string & file_path, - bool reload) { +std::shared_ptr<asset> AssetManager::cache(const std::string & file_path, bool reload) { auto it = asset_cache.find(file_path); if (!reload && it != asset_cache.end()) { return std::any_cast<std::shared_ptr<asset>>(it->second); } - std::shared_ptr<asset> new_asset - = std::make_shared<asset>(file_path.c_str()); + std::shared_ptr<asset> new_asset = std::make_shared<asset>(file_path.c_str()); asset_cache[file_path] = new_asset; diff --git a/src/crepe/api/AudioSource.cpp b/src/crepe/api/AudioSource.cpp deleted file mode 100644 index 63fd0d7..0000000 --- a/src/crepe/api/AudioSource.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include <memory> - -#include "../facade/Sound.h" - -#include "AudioSource.h" - -using namespace crepe; - -AudioSource::AudioSource(std::unique_ptr<Asset> audio_clip) { - this->sound = std::make_unique<crepe::Sound>(std::move(audio_clip)); -} - -void AudioSource::play() { return this->play(false); } - -void AudioSource::play(bool looping) { - this->sound->set_looping(looping); - this->sound->play(); -} - -void AudioSource::stop() { - this->sound->pause(); - this->sound->rewind(); -} diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h deleted file mode 100644 index 1e24ae8..0000000 --- a/src/crepe/api/AudioSource.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include <memory> - -#include "../Asset.h" -#include "../Component.h" - -namespace crepe { - -class Sound; - -//! Audio source component -class AudioSource : public Component { -public: - AudioSource(std::unique_ptr<Asset> audio_clip); - virtual ~AudioSource() = default; - -public: - //! Start or resume this audio source - void play(); - void play(bool looping); - //! Stop this audio source - void stop(); - -public: - //! Sample file location - std::unique_ptr<Asset> audio_clip; - //! TODO: ????? - bool play_on_awake; - //! Repeat the current audio clip during playback - bool loop; - //! Normalized volume (0.0 - 1.0) - float volume; - -private: - std::unique_ptr<Sound> sound; -}; - -} // namespace crepe diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp index e69de29..7bbace0 100644 --- a/src/crepe/api/BehaviorScript.cpp +++ b/src/crepe/api/BehaviorScript.cpp @@ -0,0 +1,15 @@ +#include "BehaviorScript.h" +#include "Component.h" +#include "GameObject.h" + +using namespace crepe; + +BehaviorScript::BehaviorScript(game_object_id_t id, ComponentManager & mgr) + : Component(id), + component_manager(mgr) {} + +template <> +BehaviorScript & GameObject::add_component<BehaviorScript>() { + ComponentManager & mgr = this->component_manager; + return mgr.add_component<BehaviorScript>(this->id, mgr); +} diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h index 6b1fec7..9d85d4c 100644 --- a/src/crepe/api/BehaviorScript.h +++ b/src/crepe/api/BehaviorScript.h @@ -4,29 +4,69 @@ #include "../Component.h" +#include "GameObject.h" + namespace crepe { class ScriptSystem; class ComponentManager; class Script; +/** + * \brief Script component + * + * This class acts as a (component) wrapper around an instance of (a class derivatived from) \c + * Script. \c BehaviorScript is the only ECS component that stores member function + * implementations as data. + */ class BehaviorScript : public Component { protected: - friend class crepe::ComponentManager; - using Component::Component; - -public: - virtual ~BehaviorScript() = default; + /** + * \param id Parent \c GameObject id + * \param component_manager Reference to component manager (passed through to \c Script + * instance) + * + * \note Calls to this constructor (should) always pass through \c GameObject::add_component, + * which has an exception for this specific component type. This was done so the user does + * not have to pass references used within \c Script to each \c BehaviorScript instance. + */ + BehaviorScript(game_object_id_t id, ComponentManager & component_manager); + //! Only ComponentManager is allowed to instantiate BehaviorScript + friend class ComponentManager; public: + /** + * \brief Set the concrete script of this component + * + * \tparam T Concrete script type (derived from \c crepe::Script) + * + * \returns Reference to BehaviorScript component (`*this`) + */ template <class T> BehaviorScript & set_script(); protected: - friend class crepe::ScriptSystem; + //! Script instance std::unique_ptr<Script> script = nullptr; + //! ScriptSystem needs direct access to the script instance + friend class ScriptSystem; + +protected: + //! Reference to component manager (passed to Script) + ComponentManager & component_manager; }; +/** + * \brief Add a BehaviorScript component to this game object + * + * The \c BehaviorScript class is the only exception to the ECS harmony, and requires a + * reference to the component manager passed to its constructor in order to function normally. + * This is because the \c BehaviorScript (and \c Script) classes are the only component-related + * classes that store implemented member functions as data. + */ +template <> +BehaviorScript & GameObject::add_component<BehaviorScript>(); + } // namespace crepe #include "BehaviorScript.hpp" diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp index 4751607..d80321d 100644 --- a/src/crepe/api/BehaviorScript.hpp +++ b/src/crepe/api/BehaviorScript.hpp @@ -2,7 +2,7 @@ #include <type_traits> -#include "../util/log.h" +#include "../util/Log.h" #include "BehaviorScript.h" #include "Script.h" @@ -11,10 +11,11 @@ namespace crepe { template <class T> BehaviorScript & BehaviorScript::set_script() { - static_assert(std::is_base_of<Script, T>::value); dbg_trace(); + static_assert(std::is_base_of<Script, T>::value); Script * s = new T(); - s->parent = this; + s->game_object_id = this->game_object_id; + s->component_manager_ref = &this->component_manager; this->script = std::unique_ptr<Script>(s); return *this; } diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 07341e2..07b3a82 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -1,7 +1,6 @@ target_sources(crepe PUBLIC # AudioSource.cpp BehaviorScript.cpp - Script.cpp GameObject.cpp Rigidbody.cpp ParticleEmitter.cpp diff --git a/src/crepe/api/Camera.cpp b/src/crepe/api/Camera.cpp index 6355a03..5835bdd 100644 --- a/src/crepe/api/Camera.cpp +++ b/src/crepe/api/Camera.cpp @@ -1,7 +1,4 @@ - -#include <cstdint> - -#include "util/log.h" +#include "util/Log.h" #include "Camera.h" #include "Color.h" @@ -9,7 +6,7 @@ using namespace crepe; -Camera::Camera(uint32_t id, const Color & bg_color) +Camera::Camera(game_object_id_t id, const Color & bg_color) : Component(id), bg_color(bg_color) { dbg_trace(); diff --git a/src/crepe/api/Camera.h b/src/crepe/api/Camera.h index ba3a9ef..e0cda34 100644 --- a/src/crepe/api/Camera.h +++ b/src/crepe/api/Camera.h @@ -1,7 +1,5 @@ #pragma once -#include <cstdint> - #include "Color.h" #include "Component.h" @@ -11,9 +9,8 @@ namespace crepe { * \class Camera * \brief Represents a camera component for rendering in the game. * - * The Camera class defines the view parameters, including background color, - * aspect ratio, position, and zoom level. It controls what part of the game - * world is visible on the screen. + * The Camera class defines the view parameters, including background color, aspect ratio, + * position, and zoom level. It controls what part of the game world is visible on the screen. */ class Camera : public Component { @@ -23,7 +20,7 @@ public: * \param id Unique identifier for the camera component. * \param bg_color Background color for the camera view. */ - Camera(uint32_t id, const Color & bg_color); + Camera(game_object_id_t id, const Color & bg_color); ~Camera(); // dbg_trace only public: diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 8c9e643..3ab877a 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -1,19 +1,24 @@ #pragma once -#include "../util/log.h" +#include "../util/Log.h" namespace crepe { +/** + * \brief Global configuration interface + * + * This class stores engine default settings. Properties on this class are only supposed to be + * modified *before* execution is handed over from the game programmer to the engine (i.e. the + * main loop is started). + */ class Config { -private: - Config() = default; - -public: - ~Config() = default; - public: //! Retrieve handle to global Config instance static Config & get_instance(); + +private: + Config() = default; + // singleton Config(const Config &) = delete; Config(Config &&) = delete; @@ -26,10 +31,9 @@ public: /** * \brief Log level * - * Only messages with equal or higher priority than this value will be - * logged. + * Only messages with equal or higher priority than this value will be logged. */ - LogLevel level = LogLevel::INFO; + Log::Level level = Log::Level::INFO; /** * \brief Colored log output * @@ -43,8 +47,8 @@ public: /** * \brief Save file location * - * This location is used by the constructor of SaveManager, and should be - * set before save manager functionality is attempted to be used. + * This location is used by the constructor of SaveManager, and should be set before save + * manager functionality is attempted to be used. */ std::string location = "save.crepe.db"; } savemgr; diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp index d252e77..287e81d 100644 --- a/src/crepe/api/GameObject.cpp +++ b/src/crepe/api/GameObject.cpp @@ -1,23 +1,26 @@ #include "api/Transform.h" +#include "BehaviorScript.h" #include "GameObject.h" #include "Metadata.h" using namespace crepe; using namespace std; -GameObject::GameObject(game_object_id_t id, const std::string & name, - const std::string & tag, const Vector2 & position, - double rotation, double scale) - : id(id) { +GameObject::GameObject(ComponentManager & component_manager, game_object_id_t id, + const std::string & name, const std::string & tag, + const Vector2 & position, double rotation, double scale) + : id(id), + component_manager(component_manager) { + // Add Transform and Metadata components - ComponentManager & mgr = ComponentManager::get_instance(); + ComponentManager & mgr = this->component_manager; mgr.add_component<Transform>(this->id, position, rotation, scale); mgr.add_component<Metadata>(this->id, name, tag); } void GameObject::set_parent(const GameObject & parent) { - ComponentManager & mgr = ComponentManager::get_instance(); + ComponentManager & mgr = this->component_manager; // Set parent on own Metadata component vector<reference_wrapper<Metadata>> this_metadata diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h index d703730..34ef8bb 100644 --- a/src/crepe/api/GameObject.h +++ b/src/crepe/api/GameObject.h @@ -2,25 +2,26 @@ #include <string> +#include "Vector2.h" #include "types.h" namespace crepe { -class Vector2; +class ComponentManager; /** * \brief Represents a GameObject * - * This class represents a GameObject. The GameObject class is only used - * as an interface for the game programmer. The actual implementation is - * done in the ComponentManager. + * This class represents a GameObject. The GameObject class is only used as an interface for + * the game programmer. The actual implementation is done in the ComponentManager. */ class GameObject { -public: +private: /** - * This constructor creates a new GameObject. It creates a new - * Transform and Metadata component and adds them to the ComponentManager. + * This constructor creates a new GameObject. It creates a new Transform and Metadata + * component and adds them to the ComponentManager. * + * \param component_manager Reference to component_manager * \param id The id of the GameObject * \param name The name of the GameObject * \param tag The tag of the GameObject @@ -28,15 +29,19 @@ public: * \param rotation The rotation of the GameObject * \param scale The scale of the GameObject */ - GameObject(game_object_id_t id, const std::string & name, - const std::string & tag, const Vector2 & position, + GameObject(ComponentManager & component_manager, game_object_id_t id, + const std::string & name, const std::string & tag, const Vector2 & position, double rotation, double scale); + //! ComponentManager instances GameObject + friend class ComponentManager; + +public: /** * \brief Set the parent of this GameObject * - * This method sets the parent of this GameObject. It sets the parent - * in the Metadata component of this GameObject and adds this GameObject - * to the children list of the parent GameObject. + * This method sets the parent of this GameObject. It sets the parent in the Metadata + * component of this GameObject and adds this GameObject to the children list of the parent + * GameObject. * * \param parent The parent GameObject */ @@ -44,8 +49,8 @@ public: /** * \brief Add a component to the GameObject * - * This method adds a component to the GameObject. It forwards the - * arguments to the ComponentManager. + * This method adds a component to the GameObject. It forwards the arguments to the + * ComponentManager. * * \tparam T The type of the component * \tparam Args The types of the arguments @@ -58,6 +63,9 @@ public: public: //! The id of the GameObject const game_object_id_t id; + +protected: + ComponentManager & component_manager; }; } // namespace crepe diff --git a/src/crepe/api/GameObject.hpp b/src/crepe/api/GameObject.hpp index bfba7fe..17b17d7 100644 --- a/src/crepe/api/GameObject.hpp +++ b/src/crepe/api/GameObject.hpp @@ -8,7 +8,7 @@ namespace crepe { template <typename T, typename... Args> T & GameObject::add_component(Args &&... args) { - ComponentManager & mgr = ComponentManager::get_instance(); + ComponentManager & mgr = this->component_manager; return mgr.add_component<T>(this->id, std::forward<Args>(args)...); } diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index f9c5362..a4bc101 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,5 +1,9 @@ - #include "../facade/SDLContext.h" + +#include "../system/AnimatorSystem.h" +#include "../system/CollisionSystem.h" +#include "../system/ParticleSystem.h" +#include "../system/PhysicsSystem.h" #include "../system/RenderSystem.h" #include "../system/ScriptSystem.h" #include "..//system/PhysicsSystem.h" @@ -9,11 +13,21 @@ #include "LoopTimer.h" using namespace crepe; +using namespace std; + +LoopManager::LoopManager() { + this->load_system<AnimatorSystem>(); + this->load_system<CollisionSystem>(); + this->load_system<ParticleSystem>(); + this->load_system<PhysicsSystem>(); + this->load_system<RenderSystem>(); + this->load_system<ScriptSystem>(); +} -LoopManager::LoopManager() {} void LoopManager::process_input() { SDLContext::get_instance().handle_events(this->game_running); } + void LoopManager::start() { this->setup(); this->loop(); @@ -55,7 +69,7 @@ void LoopManager::setup() { void LoopManager::render() { if (this->game_running) { - RenderSystem::get_instance().update(); + this->get_system<RenderSystem>().update(); } } diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 2f03193..f6904be 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -2,15 +2,9 @@ #include <memory> -class RenderSystem; -class SDLContext; -class LoopTimer; -class ScriptSystem; -class SoundSystem; -class ParticleSystem; -class PhysicsSystem; -class AnimatorSystem; -class CollisionSystem; +#include "../ComponentManager.h" +#include "../system/System.h" + namespace crepe { class LoopManager { @@ -73,7 +67,35 @@ private: void render(); bool game_running = false; - //#TODO add system instances + +private: + //! Component manager instance + ComponentManager component_manager{}; + +private: + /** + * \brief Collection of System instances + * + * This map holds System instances indexed by the system's class typeid. It is filled in the + * constructor of \c LoopManager using LoopManager::load_system. + */ + std::unordered_map<std::type_index, std::unique_ptr<System>> systems; + /** + * \brief Initialize a system + * \tparam T System type (must be derivative of \c System) + */ + template <class T> + void load_system(); + /** + * \brief Retrieve a reference to ECS system + * \tparam T System type + * \returns Reference to system instance + * \throws std::runtime_error if the System is not initialized + */ + template <class T> + T & get_system(); }; } // namespace crepe + +#include "LoopManager.hpp" diff --git a/src/crepe/api/LoopManager.hpp b/src/crepe/api/LoopManager.hpp new file mode 100644 index 0000000..0b14fdb --- /dev/null +++ b/src/crepe/api/LoopManager.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <cassert> +#include <format> +#include <memory> + +#include "../system/System.h" + +#include "LoopManager.h" + +namespace crepe { + +template <class T> +T & LoopManager::get_system() { + using namespace std; + static_assert(is_base_of<System, T>::value, + "get_system must recieve a derivative class of System"); + + const type_info & type = typeid(T); + if (!this->systems.contains(type)) + throw runtime_error(format("LoopManager: {} is not initialized", type.name())); + + System * system = this->systems.at(type).get(); + T * concrete_system = dynamic_cast<T *>(system); + assert(concrete_system != nullptr); + + return *concrete_system; +} + +template <class T> +void LoopManager::load_system() { + using namespace std; + static_assert(is_base_of<System, T>::value, + "load_system must recieve a derivative class of System"); + + System * system = new T(this->component_manager); + this->systems[typeid(T)] = unique_ptr<System>(system); +} + +} // namespace crepe diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp index 8f09e41..a9800b7 100644 --- a/src/crepe/api/LoopTimer.cpp +++ b/src/crepe/api/LoopTimer.cpp @@ -1,7 +1,7 @@ #include <chrono> #include "../facade/SDLContext.h" -#include "../util/log.h" +#include "../util/Log.h" #include "LoopTimer.h" @@ -24,9 +24,8 @@ void LoopTimer::start() { void LoopTimer::update() { auto current_frame_time = std::chrono::steady_clock::now(); // Convert to duration in seconds for delta time - this->delta_time - = std::chrono::duration_cast<std::chrono::duration<double>>( - current_frame_time - last_frame_time); + this->delta_time = std::chrono::duration_cast<std::chrono::duration<double>>( + current_frame_time - last_frame_time); if (this->delta_time > this->maximum_delta_time) { this->delta_time = this->maximum_delta_time; @@ -39,17 +38,11 @@ void LoopTimer::update() { double LoopTimer::get_delta_time() const { return this->delta_time.count(); } -double LoopTimer::get_current_time() const { - return this->elapsed_time.count(); -} +double LoopTimer::get_current_time() const { return this->elapsed_time.count(); } -void LoopTimer::advance_fixed_update() { - this->elapsed_fixed_time += this->fixed_delta_time; -} +void LoopTimer::advance_fixed_update() { this->elapsed_fixed_time += this->fixed_delta_time; } -double LoopTimer::get_fixed_delta_time() const { - return this->fixed_delta_time.count(); -} +double LoopTimer::get_fixed_delta_time() const { return this->fixed_delta_time.count(); } void LoopTimer::set_fps(int fps) { this->fps = fps; @@ -66,13 +59,13 @@ void LoopTimer::enforce_frame_rate() { std::chrono::steady_clock::time_point current_frame_time = std::chrono::steady_clock::now(); std::chrono::milliseconds frame_duration - = std::chrono::duration_cast<std::chrono::milliseconds>( - current_frame_time - this->last_frame_time); + = std::chrono::duration_cast<std::chrono::milliseconds>(current_frame_time + - this->last_frame_time); if (frame_duration < this->frame_target_time) { std::chrono::milliseconds delay_time - = std::chrono::duration_cast<std::chrono::milliseconds>( - this->frame_target_time - frame_duration); + = std::chrono::duration_cast<std::chrono::milliseconds>(this->frame_target_time + - frame_duration); if (delay_time.count() > 0) { SDLContext::get_instance().delay(delay_time.count()); } diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h index 85687be..f277d7b 100644 --- a/src/crepe/api/LoopTimer.h +++ b/src/crepe/api/LoopTimer.h @@ -1,124 +1,123 @@ #pragma once #include <chrono> -#include <cstdint> namespace crepe { class LoopTimer { public: /** - * \brief Get the singleton instance of LoopTimer. - * - * \return A reference to the LoopTimer instance. - */ + * \brief Get the singleton instance of LoopTimer. + * + * \return A reference to the LoopTimer instance. + */ static LoopTimer & get_instance(); /** - * \brief Get the current delta time for the current frame. - * - * \return Delta time in seconds since the last frame. - */ + * \brief Get the current delta time for the current frame. + * + * \return Delta time in seconds since the last frame. + */ double get_delta_time() const; /** - * \brief Get the current game time. - * - * \note The current game time may vary from real-world elapsed time. - * It is the cumulative sum of each frame's delta time. - * - * \return Elapsed game time in seconds. - */ + * \brief Get the current game time. + * + * \note The current game time may vary from real-world elapsed time. It is the cumulative + * sum of each frame's delta time. + * + * \return Elapsed game time in seconds. + */ double get_current_time() const; /** - * \brief Set the target frames per second (FPS). - * - * \param fps The desired frames rendered per second. - */ + * \brief Set the target frames per second (FPS). + * + * \param fps The desired frames rendered per second. + */ void set_fps(int fps); /** - * \brief Get the current frames per second (FPS). - * - * \return Current FPS. - */ + * \brief Get the current frames per second (FPS). + * + * \return Current FPS. + */ int get_fps() const; /** - * \brief Get the current game scale. - * - * \return The current game scale, where 0 = paused, 1 = normal speed, and values > 1 speed up the game. - */ + * \brief Get the current game scale. + * + * \return The current game scale, where 0 = paused, 1 = normal speed, and values > 1 speed + * up the game. + */ double get_game_scale() const; /** - * \brief Set the game scale. - * - * \param game_scale The desired game scale (0 = pause, 1 = normal speed, > 1 = speed up). - */ + * \brief Set the game scale. + * + * \param game_scale The desired game scale (0 = pause, 1 = normal speed, > 1 = speed up). + */ void set_game_scale(double game_scale); private: friend class LoopManager; /** - * \brief Start the loop timer. - * - * Initializes the timer to begin tracking frame times. - */ + * \brief Start the loop timer. + * + * Initializes the timer to begin tracking frame times. + */ void start(); /** - * \brief Enforce the frame rate limit. - * - * Ensures that the game loop does not exceed the target FPS by delaying - * frame updates as necessary. - */ + * \brief Enforce the frame rate limit. + * + * Ensures that the game loop does not exceed the target FPS by delaying frame updates as + * necessary. + */ void enforce_frame_rate(); /** - * \brief Get the fixed delta time for consistent updates. - * - * Fixed delta time is used for operations that require uniform time steps, - * such as physics calculations. - * - * \return Fixed delta time in seconds. - */ + * \brief Get the fixed delta time for consistent updates. + * + * Fixed delta time is used for operations that require uniform time steps, such as physics + * calculations. + * + * \return Fixed delta time in seconds. + */ double get_fixed_delta_time() const; /** - * \brief Get the accumulated lag in the game loop. - * - * Lag represents the difference between the target frame time and the - * actual frame time, useful for managing fixed update intervals. - * - * \return Accumulated lag in seconds. - */ + * \brief Get the accumulated lag in the game loop. + * + * Lag represents the difference between the target frame time and the actual frame time, + * useful for managing fixed update intervals. + * + * \return Accumulated lag in seconds. + */ double get_lag() const; /** - * \brief Construct a new LoopTimer object. - * - * Private constructor for singleton pattern to restrict instantiation - * outside the class. - */ + * \brief Construct a new LoopTimer object. + * + * Private constructor for singleton pattern to restrict instantiation outside the class. + */ LoopTimer(); /** - * \brief Update the timer to the current frame. - * - * Calculates and updates the delta time for the current frame and adds it to - * the cumulative game time. - */ + * \brief Update the timer to the current frame. + * + * Calculates and updates the delta time for the current frame and adds it to the cumulative + * game time. + */ void update(); /** - * \brief Advance the game loop by a fixed update interval. - * - * This method progresses the game state by a consistent, fixed time step, - * allowing for stable updates independent of frame rate fluctuations. - */ + * \brief Advance the game loop by a fixed update interval. + * + * This method progresses the game state by a consistent, fixed time step, allowing for + * stable updates independent of frame rate fluctuations. + */ void advance_fixed_update(); private: @@ -131,11 +130,9 @@ private: //! Delta time for the current frame in seconds std::chrono::duration<double> delta_time{0.0}; //! Target time per frame in seconds - std::chrono::duration<double> frame_target_time - = std::chrono::seconds(1) / fps; + std::chrono::duration<double> frame_target_time = std::chrono::seconds(1) / fps; //! Fixed delta time for fixed updates in seconds - std::chrono::duration<double> fixed_delta_time - = std::chrono::seconds(1) / 50; + std::chrono::duration<double> fixed_delta_time = std::chrono::seconds(1) / 50; //! Total elapsed game time in seconds std::chrono::duration<double> elapsed_time{0.0}; //! Total elapsed time for fixed updates in seconds diff --git a/src/crepe/api/Metadata.h b/src/crepe/api/Metadata.h index c61e006..235d42f 100644 --- a/src/crepe/api/Metadata.h +++ b/src/crepe/api/Metadata.h @@ -10,8 +10,8 @@ namespace crepe { /** * \brief Metadata component * - * This class represents the Metadata component. It stores the name, tag, parent - * and children of a GameObject. + * This class represents the Metadata component. It stores the name, tag, parent and children + * of a GameObject. */ class Metadata : public Component { public: @@ -20,8 +20,7 @@ public: * \param name The name of the GameObject * \param tag The tag of the GameObject */ - Metadata(game_object_id_t id, const std::string & name, - const std::string & tag); + Metadata(game_object_id_t id, const std::string & name, const std::string & tag); /** * \brief Get the maximum number of instances for this component * diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp index 35f960d..90b77a0 100644 --- a/src/crepe/api/ParticleEmitter.cpp +++ b/src/crepe/api/ParticleEmitter.cpp @@ -2,8 +2,7 @@ using namespace crepe; -ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, - const Data & data) +ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Data & data) : Component(game_object_id), data(data) { for (size_t i = 0; i < this->data.max_particles; i++) { diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h index a9e872f..33112e1 100644 --- a/src/crepe/api/ParticleEmitter.h +++ b/src/crepe/api/ParticleEmitter.h @@ -13,16 +13,16 @@ class Sprite; /** * \brief Data holder for particle emission parameters. * - * The ParticleEmitter class stores configuration data for particle properties, - * defining the characteristics and boundaries of particle emissions. + * The ParticleEmitter class stores configuration data for particle properties, defining the + * characteristics and boundaries of particle emissions. */ class ParticleEmitter : public Component { public: /** * \brief Defines the boundary within which particles are constrained. * - * This structure specifies the boundary's size and offset, as well as the - * behavior of particles upon reaching the boundary limits. + * This structure specifies the boundary's size and offset, as well as the behavior of + * particles upon reaching the boundary limits. */ struct Boundary { //! boundary width (midpoint is emitter location) @@ -38,8 +38,8 @@ public: /** * \brief Holds parameters that control particle emission. * - * Contains settings for the emitter’s position, particle speed, angle, lifespan, - * boundary, and the sprite used for rendering particles. + * Contains settings for the emitter’s position, particle speed, angle, lifespan, boundary, + * and the sprite used for rendering particles. */ struct Data { //! position of the emitter diff --git a/src/crepe/api/Rigidbody.cpp b/src/crepe/api/Rigidbody.cpp index 3bf1c5b..6b87695 100644 --- a/src/crepe/api/Rigidbody.cpp +++ b/src/crepe/api/Rigidbody.cpp @@ -2,8 +2,8 @@ using namespace crepe; -crepe::Rigidbody::Rigidbody(uint32_t game_object_id, const Data & data) - : Component(game_object_id), +crepe::Rigidbody::Rigidbody(game_object_id_t id, const Data & data) + : Component(id), data(data) {} void crepe::Rigidbody::add_force_linear(const Vector2 & force) { diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h index a007107..fddbf5c 100644 --- a/src/crepe/api/Rigidbody.h +++ b/src/crepe/api/Rigidbody.h @@ -1,7 +1,6 @@ #pragma once #include <cmath> -#include <cstdint> #include "../Component.h" @@ -12,8 +11,8 @@ namespace crepe { /** * \brief Rigidbody class * - * This class is used by the physics sytem and collision system. - * It configures how to system interact with the gameobject for movement and collisions. + * This class is used by the physics sytem and collision system. It configures how to system + * interact with the gameobject for movement and collisions. */ class Rigidbody : public Component { public: @@ -33,8 +32,8 @@ public: /** * \brief PhysicsConstraints to constrain movement * - * This struct configures the movement constraint for this object. - * If a constraint is enabled the systems will not move the object. + * This struct configures the movement constraint for this object. If a constraint is enabled + * the systems will not move the object. */ struct PhysicsConstraints { //! X constraint @@ -87,7 +86,7 @@ public: * \param game_object_id id of the gameobject the rigibody is added to. * \param data struct to configure the rigidbody. */ - Rigidbody(uint32_t game_object_id, const Data & data); + Rigidbody(game_object_id_t id, const Data & data); //! struct to hold data of rigidbody Data data; diff --git a/src/crepe/api/SaveManager.cpp b/src/crepe/api/SaveManager.cpp index 43276c5..c5f43ea 100644 --- a/src/crepe/api/SaveManager.cpp +++ b/src/crepe/api/SaveManager.cpp @@ -1,5 +1,5 @@ #include "../facade/DB.h" -#include "../util/log.h" +#include "../util/Log.h" #include "Config.h" #include "SaveManager.h" @@ -139,14 +139,11 @@ ValueBroker<T> SaveManager::get(const string & key, const T & default_value) { } template ValueBroker<uint8_t> SaveManager::get(const string &, const uint8_t &); template ValueBroker<int8_t> SaveManager::get(const string &, const int8_t &); -template ValueBroker<uint16_t> SaveManager::get(const string &, - const uint16_t &); +template ValueBroker<uint16_t> SaveManager::get(const string &, const uint16_t &); template ValueBroker<int16_t> SaveManager::get(const string &, const int16_t &); -template ValueBroker<uint32_t> SaveManager::get(const string &, - const uint32_t &); +template ValueBroker<uint32_t> SaveManager::get(const string &, const uint32_t &); template ValueBroker<int32_t> SaveManager::get(const string &, const int32_t &); -template ValueBroker<uint64_t> SaveManager::get(const string &, - const uint64_t &); +template ValueBroker<uint64_t> SaveManager::get(const string &, const uint64_t &); template ValueBroker<int64_t> SaveManager::get(const string &, const int64_t &); template ValueBroker<float> SaveManager::get(const string &, const float &); template ValueBroker<double> SaveManager::get(const string &, const double &); diff --git a/src/crepe/api/SaveManager.h b/src/crepe/api/SaveManager.h index 4be85fb..3d8c852 100644 --- a/src/crepe/api/SaveManager.h +++ b/src/crepe/api/SaveManager.h @@ -24,7 +24,8 @@ public: * \brief Get a read/write reference to a value and initialize it if it does not yet exist * * \param key The value key - * \param default_value Value to initialize \c key with if it does not already exist in the database + * \param default_value Value to initialize \c key with if it does not already exist in the + * database * * \return Read/write reference to the value */ @@ -38,8 +39,8 @@ public: * * \return Read/write reference to the value * - * \note Attempting to read this value before it is initialized (i.e. set) - * will result in an exception + * \note Attempting to read this value before it is initialized (i.e. set) will result in an + * exception */ template <typename T> ValueBroker<T> get(const std::string & key); @@ -102,8 +103,8 @@ private: * * \returns DB instance * - * This function exists because DB is a facade class, which can't directly be - * used in the API without workarounds + * This function exists because DB is a facade class, which can't directly be used in the API + * without workarounds * * TODO: better solution */ diff --git a/src/crepe/api/Scene.cpp b/src/crepe/api/Scene.cpp index 933edf4..88aa82d 100644 --- a/src/crepe/api/Scene.cpp +++ b/src/crepe/api/Scene.cpp @@ -2,4 +2,6 @@ using namespace crepe; -Scene::Scene(const std::string & name) : name(name) {} +Scene::Scene(ComponentManager & mgr, const std::string & name) + : component_manager(mgr), + name(name) {} diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h index f8bcc3d..0e516b6 100644 --- a/src/crepe/api/Scene.h +++ b/src/crepe/api/Scene.h @@ -4,14 +4,23 @@ namespace crepe { +class SceneManager; +class ComponentManager; + class Scene { +protected: + Scene(ComponentManager & mgr, const std::string & name); + friend class SceneManager; + public: - Scene(const std::string & name); virtual ~Scene() = default; - virtual void load_scene() = 0; public: - std::string name; + virtual void load_scene() = 0; + const std::string name; + +protected: + ComponentManager & component_manager; }; } // namespace crepe diff --git a/src/crepe/api/SceneManager.cpp b/src/crepe/api/SceneManager.cpp index dfed6ee..7fb5cb0 100644 --- a/src/crepe/api/SceneManager.cpp +++ b/src/crepe/api/SceneManager.cpp @@ -8,10 +8,7 @@ using namespace crepe; using namespace std; -SceneManager & SceneManager::get_instance() { - static SceneManager instance; - return instance; -} +SceneManager::SceneManager(ComponentManager & mgr) : component_manager(mgr) {} void SceneManager::set_next_scene(const string & name) { next_scene = name; } @@ -19,18 +16,17 @@ void SceneManager::load_next_scene() { // next scene not set if (this->next_scene.empty()) return; - auto it - = find_if(this->scenes.begin(), this->scenes.end(), - [&next_scene = this->next_scene](unique_ptr<Scene> & scene) { - return scene->name == 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; + }); // next scene not found if (it == this->scenes.end()) return; unique_ptr<Scene> & scene = *it; // Delete all components of the current scene - ComponentManager & mgr = ComponentManager::get_instance(); + ComponentManager & mgr = this->component_manager; mgr.delete_all_components(); // Load the new scene diff --git a/src/crepe/api/SceneManager.h b/src/crepe/api/SceneManager.h index 1e0e670..e854794 100644 --- a/src/crepe/api/SceneManager.h +++ b/src/crepe/api/SceneManager.h @@ -8,14 +8,11 @@ namespace crepe { +class ComponentManager; + class SceneManager { public: - // Singleton - static SceneManager & get_instance(); - SceneManager(const SceneManager &) = delete; - SceneManager(SceneManager &&) = delete; - SceneManager & operator=(const SceneManager &) = delete; - SceneManager & operator=(SceneManager &&) = delete; + SceneManager(ComponentManager & mgr); public: /** @@ -38,11 +35,9 @@ public: void load_next_scene(); private: - SceneManager() = default; - -private: std::vector<std::unique_ptr<Scene>> scenes; std::string next_scene; + ComponentManager & component_manager; }; } // namespace crepe diff --git a/src/crepe/api/SceneManager.hpp b/src/crepe/api/SceneManager.hpp index 8bad7b2..714f690 100644 --- a/src/crepe/api/SceneManager.hpp +++ b/src/crepe/api/SceneManager.hpp @@ -1,13 +1,16 @@ +#pragma once + #include "SceneManager.h" namespace crepe { template <typename T> void SceneManager::add_scene(const std::string & name) { - static_assert(std::is_base_of<Scene, T>::value, - "T must be derived from Scene"); + using namespace std; + static_assert(is_base_of<Scene, T>::value, "T must be derived from Scene"); - scenes.emplace_back(make_unique<T>(name)); + Scene * scene = new T(this->component_manager, name); + 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()) { diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp deleted file mode 100644 index 390cec7..0000000 --- a/src/crepe/api/Script.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "Script.h" - -using namespace crepe; diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 0a10848..2b70379 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -2,35 +2,85 @@ #include <vector> -namespace crepe { -class ScriptSystem; -} +#include "../types.h" namespace crepe { +class ScriptSystem; class BehaviorScript; +class ComponentManager; +/** + * \brief Script interface + * + * This class is used as a base class for user-defined scripts that can be added to game + * objects using the \c BehaviorScript component. + * + * \note Additional *events* (like Unity's OnDisable and OnEnable) should be implemented as + * member or lambda methods in derivative user script classes and registered in \c init(). + */ class Script { - friend class crepe::ScriptSystem; - protected: + /** + * \brief Script initialization function + * + * This function is called during the ScriptSystem::update() routine *before* + * Script::update() if it (a) has not yet been called and (b) the \c BehaviorScript component + * holding this script instance is active. + */ virtual void init() {} + /** + * \brief Script update function + * + * This function is called during the ScriptSystem::update() routine if the \c BehaviorScript + * component holding this script instance is active. + */ virtual void update() {} - // NOTE: additional *events* (like unity's OnDisable and OnEnable) should be - // implemented as member methods in derivative user script classes and - // registered in init(), otherwise this class will balloon in size with each - // added event. + //! ScriptSystem calls \c init() and \c update() + friend class crepe::ScriptSystem; protected: + /** + * \brief Get single component of type \c T on this game object (utility) + * + * \tparam T Type of component + * + * \returns Reference to component + * + * \throws nullptr if this game object does not have a component matching type \c T + */ template <typename T> - T & get_component(); + T & get_component() const; + // TODO: make get_component calls for component types that can have more than 1 instance + // cause compile-time errors + /** + * \brief Get all components of type \c T on this game object (utility) + * + * \tparam T Type of component + * + * \returns List of component references + */ template <typename T> - std::vector<std::reference_wrapper<T>> get_components(); + std::vector<std::reference_wrapper<T>> get_components() const; + +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. + Script() = default; + //! Only \c BehaviorScript instantiates Script + friend class BehaviorScript; + +private: + // These references are set by BehaviorScript immediately after calling the constructor of + // Script. + game_object_id_t game_object_id = -1; + ComponentManager * component_manager_ref = nullptr; + // TODO: use OptionalRef instead of pointer -public: - friend class crepe::BehaviorScript; - BehaviorScript * parent = nullptr; +private: + //! Flag to indicate if \c init() has been called already + bool initialized = false; }; } // namespace crepe diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index d96c0e8..a064a90 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -8,18 +8,21 @@ namespace crepe { template <typename T> -T & Script::get_component() { - std::vector<std::reference_wrapper<T>> all_components - = this->get_components<T>(); - if (all_components.size() < 1) throw nullptr; // TODO +T & Script::get_component() const { + using namespace std; + vector<reference_wrapper<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())); return all_components.back().get(); } template <typename T> -std::vector<std::reference_wrapper<T>> Script::get_components() { - ComponentManager & mgr = ComponentManager::get_instance(); - return mgr.get_components_by_id<T>(this->parent->game_object_id); +std::vector<std::reference_wrapper<T>> Script::get_components() const { + auto & mgr = *this->component_manager_ref; + + return mgr.get_components_by_id<T>(this->game_object_id); } } // namespace crepe diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp index 6f0433f..bd2d5cf 100644 --- a/src/crepe/api/Sprite.cpp +++ b/src/crepe/api/Sprite.cpp @@ -1,6 +1,6 @@ #include <memory> -#include "../util/log.h" +#include "../util/Log.h" #include "facade/SDLContext.h" #include "Component.h" @@ -10,8 +10,8 @@ using namespace std; using namespace crepe; -Sprite::Sprite(game_object_id_t id, const shared_ptr<Texture> image, - const Color & color, const FlipSettings & flip) +Sprite::Sprite(game_object_id_t id, const shared_ptr<Texture> image, const Color & color, + const FlipSettings & flip) : Component(id), color(color), flip(flip), diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h index deb3f93..0192793 100644 --- a/src/crepe/api/Sprite.h +++ b/src/crepe/api/Sprite.h @@ -1,6 +1,5 @@ #pragma once -#include <cstdint> #include <memory> #include "Color.h" @@ -28,8 +27,8 @@ class AnimatorSystem; /** * \brief Represents a renderable sprite component. * - * A renderable sprite that can be displayed in the game. It includes a texture, - * color, and flip settings, and is managed in layers with defined sorting orders. + * A renderable sprite that can be displayed in the game. It includes a texture, color, and + * flip settings, and is managed in layers with defined sorting orders. */ class Sprite : public Component { @@ -43,8 +42,8 @@ public: * \param color Color tint applied to the sprite. * \param flip Flip settings for horizontal and vertical orientation. */ - Sprite(game_object_id_t id, const std::shared_ptr<Texture> image, - const Color & color, const FlipSettings & flip); + Sprite(game_object_id_t id, const std::shared_ptr<Texture> image, const Color & color, + const FlipSettings & flip); /** * \brief Destroys the Sprite instance. @@ -81,7 +80,8 @@ private: //! Reads the all the variables plus the sprite_rect friend class AnimatorSystem; - //! Render area of the sprite this will also be adjusted by the AnimatorSystem if an Animator object is present in GameObject + //! Render area of the sprite this will also be adjusted by the AnimatorSystem if an Animator + // object is present in GameObject Rect sprite_rect; }; diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp index 5ebd23d..de0d0ea 100644 --- a/src/crepe/api/Texture.cpp +++ b/src/crepe/api/Texture.cpp @@ -1,7 +1,7 @@ #include <SDL2/SDL_render.h> #include "../facade/SDLContext.h" -#include "../util/log.h" +#include "../util/Log.h" #include "Asset.h" #include "Texture.h" @@ -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->canonical())); + this->texture = std::move(ctx.texture_from_path(res->get_canonical())); } int Texture::get_width() const { diff --git a/src/crepe/api/Texture.h b/src/crepe/api/Texture.h index b89bc17..6965223 100644 --- a/src/crepe/api/Texture.h +++ b/src/crepe/api/Texture.h @@ -1,8 +1,7 @@ #pragma once -// FIXME: this header can't be included because this is an API header, and SDL2 -// development headers won't be bundled with crepe. Why is this facade in the -// API namespace? +// FIXME: this header can't be included because this is an API header, and SDL2 development +// headers won't be bundled with crepe. Why is this facade in the API namespace? #include <SDL2/SDL_render.h> #include <functional> @@ -19,8 +18,8 @@ class Animator; * \class Texture * \brief Manages texture loading and properties. * - * The Texture class is responsible for loading an image from a source - * and providing access to its dimensions. Textures can be used for rendering. + * The Texture class is responsible for loading an image from a source and providing access to + * its dimensions. Textures can be used for rendering. */ class Texture { @@ -41,8 +40,7 @@ public: * \brief Destroys the Texture instance, freeing associated resources. */ ~Texture(); - // FIXME: this constructor shouldn't be necessary because this class doesn't - // manage memory + // FIXME: this constructor shouldn't be necessary because this class doesn't manage memory /** * \brief Gets the width of the texture. diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp index e401120..cd944bd 100644 --- a/src/crepe/api/Transform.cpp +++ b/src/crepe/api/Transform.cpp @@ -1,11 +1,10 @@ -#include "util/log.h" +#include "../util/Log.h" #include "Transform.h" using namespace crepe; -Transform::Transform(game_object_id_t id, const Vector2 & point, - double rotation, double scale) +Transform::Transform(game_object_id_t id, const Vector2 & point, double rotation, double scale) : Component(id), position(point), rotation(rotation), diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h index 756e45b..18aa293 100644 --- a/src/crepe/api/Transform.h +++ b/src/crepe/api/Transform.h @@ -9,33 +9,33 @@ namespace crepe { /** * \brief Transform component * - * This class represents the Transform component. It stores the position, - * rotation and scale of a GameObject. + * This class represents the Transform component. It stores the position, rotation and scale of + * a GameObject. */ class Transform : public Component { public: + //! Translation (shift) + Vector2 position = {0, 0}; + //! Rotation, in degrees + double rotation = 0; + //! Multiplication factor + double scale = 0; + +protected: /** * \param id The id of the GameObject this component belongs to * \param point The position of the GameObject * \param rotation The rotation of the GameObject * \param scale The scale of the GameObject */ - Transform(game_object_id_t id, const Vector2 & point, double rotation, - double scale); + Transform(game_object_id_t id, const Vector2 & point, double rotation, double scale); /** - * \brief Get the maximum number of instances for this component - * - * \return The maximum number of instances for this component + * There is always exactly one transform component per entity + * \return 1 */ virtual int get_instances_max() const { return 1; } - -public: - //! Translation (shift) - Vector2 position; - //! Rotation, in degrees - double rotation; - //! Multiplication factor - double scale; + //! ComponentManager instantiates all components + friend class ComponentManager; }; } // namespace crepe diff --git a/src/crepe/api/Vector2.cpp b/src/crepe/api/Vector2.cpp index 947c49e..30b968e 100644 --- a/src/crepe/api/Vector2.cpp +++ b/src/crepe/api/Vector2.cpp @@ -1,61 +1,33 @@ #include "Vector2.h" -namespace crepe { +using namespace crepe; -// Constructor with initial values -Vector2::Vector2(double x, double y) : x(x), y(y) {} +Vector2 Vector2::operator-(const Vector2 & other) const { return {x - other.x, y - other.y}; } -// Subtracts another vector from this vector and returns the result. -Vector2 Vector2::operator-(const Vector2 & other) const { - return {x - other.x, y - other.y}; -} +Vector2 Vector2::operator+(const Vector2 & other) const { return {x + other.x, y + other.y}; } -// Adds another vector to this vector and returns the result. -Vector2 Vector2::operator+(const Vector2 & other) const { - return {x + other.x, y + other.y}; -} +Vector2 Vector2::operator*(double scalar) const { return {x * scalar, y * scalar}; } -// Multiplies this vector by a scalar and returns the result. -Vector2 Vector2::operator*(double scalar) const { - return {x * scalar, y * scalar}; -} - -// Multiplies this vector by another vector element-wise and updates this vector. Vector2 & Vector2::operator*=(const Vector2 & other) { x *= other.x; y *= other.y; return *this; } -// Adds another vector to this vector and updates this vector. Vector2 & Vector2::operator+=(const Vector2 & other) { x += other.x; y += other.y; return *this; } -// Adds a scalar value to both components of this vector and updates this vector. Vector2 & Vector2::operator+=(double other) { x += other; y += other; return *this; } -// Returns the negation of this vector. Vector2 Vector2::operator-() const { return {-x, -y}; } -// Checks if this vector is equal to another vector. -bool Vector2::operator==(const Vector2 & other) const { - return x == other.x && y == other.y; -} - -// Checks if this vector is not equal to another vector. -bool Vector2::operator!=(const Vector2 & other) const { - return !(*this == other); -} - -double Vector2::dot(const Vector2& other) const { - return this->x * other.x + this->y * other.y; -} +bool Vector2::operator==(const Vector2 & other) const { return x == other.x && y == other.y; } -} // namespace crepe +bool Vector2::operator!=(const Vector2 & other) const { return !(*this == other); } diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h index 90d9d57..438fde6 100644 --- a/src/crepe/api/Vector2.h +++ b/src/crepe/api/Vector2.h @@ -2,19 +2,12 @@ namespace crepe { -//! Vector2 class -class Vector2 { -public: +//! 2D vector +struct Vector2 { //! X component of the vector - double x; + double x = 0; //! Y component of the vector - double y; - - //! Default constructor - Vector2() = default; - - //! Constructor with initial values - Vector2(double x, double y); + double y = 0; //! Subtracts another vector from this vector and returns the result. Vector2 operator-(const Vector2 & other) const; diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp index 0a2f455..d5d19dc 100644 --- a/src/crepe/facade/DB.cpp +++ b/src/crepe/facade/DB.cpp @@ -1,7 +1,6 @@ #include <cstring> -#include "Exception.h" -#include "util/log.h" +#include "util/Log.h" #include "DB.h" @@ -15,19 +14,18 @@ DB::DB(const string & path) { // init database struct libdb::DB * db; if ((ret = libdb::db_create(&db, NULL, 0)) != 0) - throw Exception("db_create: %s", libdb::db_strerror(ret)); + throw runtime_error(format("db_create: {}", libdb::db_strerror(ret))); this->db = {db, [](libdb::DB * db) { db->close(db, 0); }}; // load or create database file - if ((ret = this->db->open(this->db.get(), NULL, path.c_str(), NULL, - libdb::DB_BTREE, DB_CREATE, 0)) - != 0) - throw Exception("db->open: %s", libdb::db_strerror(ret)); + ret = this->db->open(this->db.get(), NULL, path.c_str(), NULL, libdb::DB_BTREE, DB_CREATE, + 0); + if (ret != 0) throw runtime_error(format("db->open: {}", libdb::db_strerror(ret))); // create cursor libdb::DBC * cursor; - if ((ret = this->db->cursor(this->db.get(), NULL, &cursor, 0)) != 0) - throw Exception("db->cursor: %s", libdb::db_strerror(ret)); + ret = this->db->cursor(this->db.get(), NULL, &cursor, 0); + if (ret != 0) throw runtime_error(format("db->cursor: {}", libdb::db_strerror(ret))); this->cursor = {cursor, [](libdb::DBC * cursor) { cursor->close(cursor); }}; } @@ -45,21 +43,24 @@ string DB::get(const string & key) { memset(&db_val, 0, sizeof(libdb::DBT)); int ret = this->cursor->get(this->cursor.get(), &db_key, &db_val, DB_FIRST); - if (ret != 0) throw Exception("cursor->get: %s", libdb::db_strerror(ret)); - return {static_cast<char *>(db_val.data), db_val.size}; + if (ret == 0) return {static_cast<char *>(db_val.data), db_val.size}; + + string err = format("cursor->get: {}", libdb::db_strerror(ret)); + if (ret == DB_NOTFOUND) throw out_of_range(err); + else throw runtime_error(err); } void DB::set(const string & key, const string & value) { libdb::DBT db_key = this->to_thing(key); libdb::DBT db_val = this->to_thing(value); int ret = this->db->put(this->db.get(), NULL, &db_key, &db_val, 0); - if (ret != 0) throw Exception("cursor->get: %s", libdb::db_strerror(ret)); + if (ret != 0) throw runtime_error(format("cursor->get: {}", libdb::db_strerror(ret))); } -bool DB::has(const std::string & key) noexcept { +bool DB::has(const std::string & key) { try { this->get(key); - } catch (...) { + } catch (std::out_of_range &) { return false; } return true; diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h index 7c757a2..629b0eb 100644 --- a/src/crepe/facade/DB.h +++ b/src/crepe/facade/DB.h @@ -15,8 +15,8 @@ namespace crepe { /** * \brief Berkeley DB facade * - * Berkeley DB is a simple key-value database that stores arbitrary data as - * both key and value. This facade uses STL strings as keys/values. + * Berkeley DB is a simple key-value database that stores arbitrary data as both key and value. + * This facade uses STL strings as keys/values. */ class DB { public: @@ -34,7 +34,8 @@ public: * * \return The value * - * \throws Exception if value is not found in DB or other error occurs + * \throws std::out_of_range if value is not found in DB + * \throws std::runtime_error if other error occurs */ std::string get(const std::string & key); /** @@ -43,7 +44,7 @@ public: * \param key The value key * \param value The value to store * - * \throws Exception if an error occurs + * \throws std::runtime_error if an error occurs */ void set(const std::string & key, const std::string & value); /** @@ -53,7 +54,7 @@ public: * * \returns True if the key exists, or false if it does not */ - bool has(const std::string & key) noexcept; + bool has(const std::string & key); private: //! RAII wrapper around \c DB struct diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 5d22cdf..803516e 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -10,17 +10,16 @@ #include <iostream> #include <memory> #include <string> -#include <utility> #include "../api/Sprite.h" #include "../api/Texture.h" #include "../api/Transform.h" -#include "../util/log.h" -#include "Exception.h" +#include "../util/Log.h" #include "SDLContext.h" using namespace crepe; +using namespace std; SDLContext & SDLContext::get_instance() { static SDLContext instance; @@ -33,41 +32,37 @@ SDLContext::SDLContext() { if (SDL_Init(SDL_INIT_VIDEO) < 0) { // FIXME: throw exception - std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() - << std::endl; + std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; return; } - SDL_Window * tmp_window = SDL_CreateWindow( - "Crepe Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - this->viewport.w, this->viewport.h, 0); + 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; + std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl; return; } - this->game_window - = {tmp_window, [](SDL_Window * window) { SDL_DestroyWindow(window); }}; + 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); + 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; + std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError() + << std::endl; SDL_DestroyWindow(this->game_window.get()); return; } - this->game_renderer = {tmp_renderer, [](SDL_Renderer * renderer) { - SDL_DestroyRenderer(renderer); - }}; + this->game_renderer + = {tmp_renderer, [](SDL_Renderer * renderer) { SDL_DestroyRenderer(renderer); }}; 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; + std::cout << "SDL_image could not initialize! SDL_image Error: " << IMG_GetError() + << std::endl; } } @@ -106,12 +101,9 @@ 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::present_screen() { SDL_RenderPresent(this->game_renderer.get()); } -void SDLContext::draw(const Sprite & sprite, const Transform & transform, - const Camera & cam) { +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) @@ -136,9 +128,7 @@ void SDLContext::draw(const Sprite & sprite, const Transform & transform, .h = static_cast<int>(adjusted_h), }; - SDL_RenderCopyEx(this->game_renderer.get(), - sprite.sprite_image->texture.get(), &srcrect, - + SDL_RenderCopyEx(this->game_renderer.get(), sprite.sprite_image->texture.get(), &srcrect, &dstrect, transform.rotation, NULL, render_flip); } @@ -148,8 +138,8 @@ void SDLContext::camera(const Camera & cam) { this->viewport.x = static_cast<int>(cam.x) - (SCREEN_WIDTH / 2); this->viewport.y = static_cast<int>(cam.y) - (SCREEN_HEIGHT / 2); - SDL_SetRenderDrawColor(this->game_renderer.get(), cam.bg_color.r, - cam.bg_color.g, cam.bg_color.b, cam.bg_color.a); + SDL_SetRenderDrawColor(this->game_renderer.get(), cam.bg_color.r, cam.bg_color.g, + cam.bg_color.b, cam.bg_color.a); } uint64_t SDLContext::get_ticks() const { return SDL_GetTicks64(); } @@ -162,22 +152,18 @@ SDLContext::texture_from_path(const std::string & path) { tmp = IMG_Load("../asset/texture/ERROR.png"); } - std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> - img_surface; - img_surface - = {tmp, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }}; + std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> img_surface; + img_surface = {tmp, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }}; - SDL_Texture * tmp_texture = SDL_CreateTextureFromSurface( - this->game_renderer.get(), img_surface.get()); + SDL_Texture * tmp_texture + = SDL_CreateTextureFromSurface(this->game_renderer.get(), img_surface.get()); if (tmp_texture == nullptr) { - throw Exception("Texture cannot be load from %s", path.c_str()); + throw runtime_error(format("Texture cannot be load from {}", path)); } - std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> - img_texture; - img_texture = {tmp_texture, - [](SDL_Texture * texture) { SDL_DestroyTexture(texture); }}; + std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> img_texture; + img_texture = {tmp_texture, [](SDL_Texture * texture) { SDL_DestroyTexture(texture); }}; return img_texture; } diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index 536dec5..007092b 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -17,9 +17,8 @@ const int SCREEN_HEIGHT = 480; namespace crepe { -// TODO: SDL_Keycode is defined in a header not distributed with crepe, which -// means this typedef is unusable when crepe is packaged. Wouter will fix this -// later. +// TODO: SDL_Keycode is defined in a header not distributed with crepe, which means this +// typedef is unusable when crepe is packaged. Wouter will fix this later. typedef SDL_Keycode CREPE_KEYCODES; class Texture; @@ -29,8 +28,8 @@ class LoopManager; * \class SDLContext * \brief Facade for the SDL library * - * SDLContext is a singleton that handles the SDL window and renderer, provides methods - * for event handling, and rendering to the screen. It is never used directly by the user + * SDLContext is a singleton that handles the SDL window and renderer, provides methods for + * event handling, and rendering to the screen. It is never used directly by the user */ class SDLContext { @@ -68,9 +67,8 @@ private: /** * \brief Pauses the execution for a specified duration. * - * This function uses SDL's delay function to halt the program execution - * for a given number of milliseconds, allowing for frame rate control - * or other timing-related functionality. + * This function uses SDL's delay function to halt the program execution for a given number + * of milliseconds, allowing for frame rate control or other timing-related functionality. * * \param ms Duration of the delay in milliseconds. */ @@ -127,8 +125,7 @@ private: * \param transform Reference to the Transform for positioning. * \param camera Reference to the Camera for view adjustments. */ - void draw(const Sprite & sprite, const Transform & transform, - const Camera & camera); + void draw(const Sprite & sprite, const Transform & transform, const Camera & camera); //! Clears the screen, preparing for a new frame. void clear_screen(); @@ -147,8 +144,7 @@ private: std::unique_ptr<SDL_Window, std::function<void(SDL_Window *)>> game_window; //! renderer for the crepe engine - std::unique_ptr<SDL_Renderer, std::function<void(SDL_Renderer *)>> - game_renderer; + std::unique_ptr<SDL_Renderer, std::function<void(SDL_Renderer *)>> game_renderer; //! viewport for the camera window SDL_Rect viewport = {0, 0, 640, 480}; diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 648ec81..7aa89a9 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -1,23 +1,22 @@ -#include "../util/log.h" +#include "../util/Log.h" #include "Sound.h" #include "SoundContext.h" using namespace crepe; +using namespace std; -Sound::Sound(std::unique_ptr<Asset> res) { +Sound::Sound(unique_ptr<Asset> res) { dbg_trace(); this->load(std::move(res)); } Sound::Sound(const char * src) { dbg_trace(); - this->load(std::make_unique<Asset>(src)); + this->load(make_unique<Asset>(src)); } -void Sound::load(std::unique_ptr<Asset> res) { - this->sample.load(res->canonical()); -} +void Sound::load(unique_ptr<Asset> res) { this->sample.load(res->get_canonical().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 183bd7c..32b6478 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -8,34 +8,37 @@ namespace crepe { +/** + * \brief Sound resource facade + * + * This class is a wrapper around a \c SoLoud::Wav instance, which holds a + * single sample. It is part of the sound facade. + */ class Sound { public: /** * \brief Pause this sample * - * Pauses this sound if it is playing, or does nothing if it is already - * paused. The playhead position is saved, such that calling \c play() after - * this function makes the sound resume. + * Pauses this sound if it is playing, or does nothing if it is already paused. The playhead + * position is saved, such that calling \c play() after this function makes the sound resume. */ void pause(); /** * \brief Play this sample * - * Resume playback if this sound is paused, or start from the beginning of - * the sample. + * Resume playback if this sound is paused, or start from the beginning of the sample. * - * \note This class only saves a reference to the most recent 'voice' of this - * sound. Calling \c play() while the sound is already playing causes - * multiple instances of the sample to play simultaniously. The sample - * started last is the one that is controlled afterwards. + * \note This class only saves a reference to the most recent 'voice' of this sound. Calling + * \c play() while the sound is already playing causes multiple instances of the sample to + * play simultaniously. The sample started last is the one that is controlled afterwards. */ void play(); /** * \brief Reset playhead position * - * Resets the playhead position so that calling \c play() after this function - * makes it play from the start of the sample. If the sound is not paused - * before calling this function, this function will stop playback. + * Resets the playhead position so that calling \c play() after this function makes it play + * from the start of the sample. If the sound is not paused before calling this function, + * this function will stop playback. */ void rewind(); /** diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp index 5e5a3a9..deb2b62 100644 --- a/src/crepe/facade/SoundContext.cpp +++ b/src/crepe/facade/SoundContext.cpp @@ -1,4 +1,4 @@ -#include "../util/log.h" +#include "../util/Log.h" #include "SoundContext.h" diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h index d3123d2..d703c16 100644 --- a/src/crepe/facade/SoundContext.h +++ b/src/crepe/facade/SoundContext.h @@ -6,19 +6,24 @@ namespace crepe { +/** + * \brief Sound engine facade + * + * This class is a wrapper around a \c SoLoud::Soloud instance, which provides + * the methods for playing \c Sound instances. It is part of the sound facade. + */ class SoundContext { private: + // singleton SoundContext(); virtual ~SoundContext(); - - // singleton - static SoundContext & get_instance(); SoundContext(const SoundContext &) = delete; SoundContext(SoundContext &&) = delete; SoundContext & operator=(const SoundContext &) = delete; SoundContext & operator=(SoundContext &&) = delete; private: + static SoundContext & get_instance(); SoLoud::Soloud engine; friend class Sound; }; diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp index bf45362..9d18873 100644 --- a/src/crepe/system/AnimatorSystem.cpp +++ b/src/crepe/system/AnimatorSystem.cpp @@ -1,27 +1,17 @@ - #include <cstdint> #include <functional> #include <vector> #include "api/Animator.h" #include "facade/SDLContext.h" -#include "util/log.h" #include "AnimatorSystem.h" #include "ComponentManager.h" using namespace crepe; -AnimatorSystem::AnimatorSystem() { dbg_trace(); } -AnimatorSystem::~AnimatorSystem() { dbg_trace(); } - -AnimatorSystem & AnimatorSystem::get_instance() { - static AnimatorSystem instance; - return instance; -} - void AnimatorSystem::update() { - ComponentManager & mgr = ComponentManager::get_instance(); + ComponentManager & mgr = this->component_manager; std::vector<std::reference_wrapper<Animator>> animations = mgr.get_components_by_type<Animator>(); diff --git a/src/crepe/system/AnimatorSystem.h b/src/crepe/system/AnimatorSystem.h index 969e9d1..56cc7b3 100644 --- a/src/crepe/system/AnimatorSystem.h +++ b/src/crepe/system/AnimatorSystem.h @@ -17,28 +17,16 @@ namespace crepe { class AnimatorSystem : public System { public: - /** - * \brief Retrieves the singleton instance of the AnimatorSystem. - * - * \return A reference to the single instance of the AnimatorSystem. - * - * This method ensures that there is only one instance of the AnimatorSystem, following the - * singleton design pattern. It can be used to access the system globally. - */ - static AnimatorSystem & get_instance(); - + using System::System; /** * \brief Updates the Animator components. * * This method is called periodically (likely every frame) to update the state of all - * Animator components, moving the animations forward and managing their behavior (e.g., looping). + * Animator components, moving the animations forward and managing their behavior (e.g., + * looping). */ void update() override; - -private: - // private because singleton - AnimatorSystem(); // dbg_trace - ~AnimatorSystem(); // dbg_trace + // FIXME: never say "likely" in the documentation lmao }; } // namespace crepe diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt index 4c18b87..d658b25 100644 --- a/src/crepe/system/CMakeLists.txt +++ b/src/crepe/system/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(crepe PUBLIC + System.cpp ParticleSystem.cpp ScriptSystem.cpp PhysicsSystem.cpp diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp index e7a3bec..7316309 100644 --- a/src/crepe/system/ParticleSystem.cpp +++ b/src/crepe/system/ParticleSystem.cpp @@ -13,20 +13,17 @@ using namespace crepe; void ParticleSystem::update() { // Get all emitters - ComponentManager & mgr = ComponentManager::get_instance(); + ComponentManager & mgr = this->component_manager; std::vector<std::reference_wrapper<ParticleEmitter>> emitters = mgr.get_components_by_type<ParticleEmitter>(); for (ParticleEmitter & emitter : emitters) { // Get transform linked to emitter const Transform & transform - = mgr.get_components_by_id<Transform>(emitter.game_object_id) - .front() - .get(); + = mgr.get_components_by_id<Transform>(emitter.game_object_id).front().get(); // Emit particles based on emission_rate - int updates - = calculate_update(this->update_count, emitter.data.emission_rate); + int updates = calculate_update(this->update_count, emitter.data.emission_rate); for (size_t i = 0; i < updates; i++) { emit_particle(emitter, transform); } @@ -45,8 +42,7 @@ void ParticleSystem::update() { this->update_count = (this->update_count + 1) % this->MAX_UPDATE_COUNT; } -void ParticleSystem::emit_particle(ParticleEmitter & emitter, - const Transform & transform) { +void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & transform) { constexpr double DEG_TO_RAD = M_PI / 180.0; Vector2 initial_position = emitter.data.position + transform.position; @@ -57,13 +53,13 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter, = generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); double angle_radians = random_angle * DEG_TO_RAD; - Vector2 velocity = {random_speed * std::cos(angle_radians), - random_speed * std::sin(angle_radians)}; + Vector2 velocity + = {random_speed * std::cos(angle_radians), random_speed * std::sin(angle_radians)}; for (Particle & particle : emitter.data.particles) { if (!particle.active) { - particle.reset(emitter.data.end_lifespan, initial_position, - velocity, random_angle); + particle.reset(emitter.data.end_lifespan, initial_position, velocity, + random_angle); break; } } @@ -81,10 +77,8 @@ int ParticleSystem::calculate_update(int count, double emission) const { return static_cast<int>(emission); } -void ParticleSystem::check_bounds(ParticleEmitter & emitter, - const Transform & transform) { - Vector2 offset = emitter.data.boundary.offset + transform.position - + emitter.data.position; +void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform & transform) { + Vector2 offset = emitter.data.boundary.offset + transform.position + emitter.data.position; double half_width = emitter.data.boundary.width / 2.0; double half_height = emitter.data.boundary.height / 2.0; @@ -95,8 +89,8 @@ void ParticleSystem::check_bounds(ParticleEmitter & emitter, for (Particle & particle : emitter.data.particles) { const Vector2 & position = particle.position; - bool within_bounds = (position.x >= LEFT && position.x <= RIGHT - && position.y >= TOP && position.y <= BOTTOM); + bool within_bounds = (position.x >= LEFT && position.x <= RIGHT && position.y >= TOP + && position.y <= BOTTOM); if (!within_bounds) { if (emitter.data.boundary.reset_on_exit) { @@ -112,30 +106,25 @@ void ParticleSystem::check_bounds(ParticleEmitter & emitter, } } -double ParticleSystem::generate_random_angle(double min_angle, - double max_angle) const { +double ParticleSystem::generate_random_angle(double min_angle, double max_angle) const { if (min_angle == max_angle) { return min_angle; } else if (min_angle < max_angle) { return min_angle - + static_cast<double>(std::rand() - % static_cast<int>(max_angle - min_angle)); + + static_cast<double>(std::rand() % static_cast<int>(max_angle - min_angle)); } else { double angle_offset = (360 - min_angle) + max_angle; - double random_angle = min_angle - + static_cast<double>( - std::rand() % static_cast<int>(angle_offset)); + double random_angle + = min_angle + static_cast<double>(std::rand() % static_cast<int>(angle_offset)); return (random_angle >= 360) ? random_angle - 360 : random_angle; } } -double ParticleSystem::generate_random_speed(double min_speed, - double max_speed) const { +double ParticleSystem::generate_random_speed(double min_speed, double max_speed) const { if (min_speed == max_speed) { return min_speed; } else { return min_speed - + static_cast<double>(std::rand() - % static_cast<int>(max_speed - min_speed)); + + static_cast<double>(std::rand() % static_cast<int>(max_speed - min_speed)); } } diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h index d7ca148..c647284 100644 --- a/src/crepe/system/ParticleSystem.h +++ b/src/crepe/system/ParticleSystem.h @@ -5,66 +5,75 @@ #include "System.h" namespace crepe { + class ParticleEmitter; class Transform; + /** - * \brief ParticleSystem class responsible for managing particle emission, updates, and bounds checking. - */ + * \brief ParticleSystem class responsible for managing particle emission, updates, and bounds + * checking. + */ class ParticleSystem : public System { public: + using System::System; /** - * \brief Updates all particle emitters by emitting particles, updating particle states, and checking bounds. - */ + * \brief Updates all particle emitters by emitting particles, updating particle states, and + * checking bounds. + */ void update() override; private: /** - * \brief Emits a particle from the specified emitter based on its emission properties. - * - * \param emitter Reference to the ParticleEmitter. - * \param transform Const reference to the Transform component associated with the emitter. - */ + * \brief Emits a particle from the specified emitter based on its emission properties. + * + * \param emitter Reference to the ParticleEmitter. + * \param transform Const reference to the Transform component associated with the emitter. + */ void emit_particle(ParticleEmitter & emitter, const Transform & transform); /** - * \brief Calculates the number of times particles should be emitted based on emission rate and update count. - * - * \param count Current update count. - * \param emission Emission rate. - * \return The number of particles to emit. - */ + * \brief Calculates the number of times particles should be emitted based on emission rate + * and update count. + * + * \param count Current update count. + * \param emission Emission rate. + * \return The number of particles to emit. + */ int calculate_update(int count, double emission) const; /** - * \brief Checks whether particles are within the emitter’s boundary, resets or stops particles if they exit. - * - * \param emitter Reference to the ParticleEmitter. - * \param transform Const reference to the Transform component associated with the emitter. - */ + * \brief Checks whether particles are within the emitter’s boundary, resets or stops + * particles if they exit. + * + * \param emitter Reference to the ParticleEmitter. + * \param transform Const reference to the Transform component associated with the emitter. + */ void check_bounds(ParticleEmitter & emitter, const Transform & transform); /** - * \brief Generates a random angle for particle emission within the specified range. - * - * \param min_angle Minimum emission angle in degrees. - * \param max_angle Maximum emission angle in degrees. - * \return Random angle in degrees. - */ + * \brief Generates a random angle for particle emission within the specified range. + * + * \param min_angle Minimum emission angle in degrees. + * \param max_angle Maximum emission angle in degrees. + * \return Random angle in degrees. + */ double generate_random_angle(double min_angle, double max_angle) const; /** - * \brief Generates a random speed for particle emission within the specified range. - * - * \param min_speed Minimum emission speed. - * \param max_speed Maximum emission speed. - * \return Random speed. - */ + * \brief Generates a random speed for particle emission within the specified range. + * + * \param min_speed Minimum emission speed. + * \param max_speed Maximum emission speed. + * \return Random speed. + */ double generate_random_speed(double min_speed, double max_speed) const; private: - //! Counter to count updates to determine how many times emit_particle is called. + //! Counter to count updates to determine how many times emit_particle is + // called. unsigned int update_count = 0; - //! Determines the lowest amount of emission rate (1000 = 0.001 = 1 particle per 1000 updates). + //! Determines the lowest amount of emission rate (1000 = 0.001 = 1 particle per 1000 + // updates). static constexpr unsigned int MAX_UPDATE_COUNT = 100; }; diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp index eb54ad3..4a7dbfb 100644 --- a/src/crepe/system/PhysicsSystem.cpp +++ b/src/crepe/system/PhysicsSystem.cpp @@ -11,7 +11,7 @@ using namespace crepe; void PhysicsSystem::update() { - ComponentManager & mgr = ComponentManager::get_instance(); + 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 @@ -29,17 +29,15 @@ void PhysicsSystem::update() { // Add gravity if (rigidbody.data.use_gravity) { rigidbody.data.linear_velocity.y - += (rigidbody.data.mass - * rigidbody.data.gravity_scale * gravity); + += (rigidbody.data.mass * rigidbody.data.gravity_scale + * gravity); } // Add damping if (rigidbody.data.angular_damping != 0) { - rigidbody.data.angular_velocity - *= rigidbody.data.angular_damping; + rigidbody.data.angular_velocity *= rigidbody.data.angular_damping; } if (rigidbody.data.linear_damping != Vector2{0, 0}) { - rigidbody.data.linear_velocity - *= rigidbody.data.linear_damping; + rigidbody.data.linear_velocity *= rigidbody.data.linear_damping; } // Max velocity check @@ -75,21 +73,17 @@ void PhysicsSystem::update() { // Move object if (!rigidbody.data.constraints.rotation) { - transform.rotation - += rigidbody.data.angular_velocity; - transform.rotation - = std::fmod(transform.rotation, 360.0); + transform.rotation += rigidbody.data.angular_velocity; + transform.rotation = std::fmod(transform.rotation, 360.0); if (transform.rotation < 0) { transform.rotation += 360.0; } } if (!rigidbody.data.constraints.x) { - transform.position.x - += rigidbody.data.linear_velocity.x; + transform.position.x += rigidbody.data.linear_velocity.x; } if (!rigidbody.data.constraints.y) { - transform.position.y - += rigidbody.data.linear_velocity.y; + transform.position.y += rigidbody.data.linear_velocity.y; } } } diff --git a/src/crepe/system/PhysicsSystem.h b/src/crepe/system/PhysicsSystem.h index 038c120..227ab69 100644 --- a/src/crepe/system/PhysicsSystem.h +++ b/src/crepe/system/PhysicsSystem.h @@ -3,14 +3,16 @@ #include "System.h" namespace crepe { + /** * \brief System that controls all physics * - * This class is a physics system that uses a rigidbody and transform - * to add physics to a game object. + * This class is a physics system that uses a rigidbody and transform to add physics to a game + * object. */ class PhysicsSystem : public System { public: + using System::System; /** * \brief updates the physics system. * diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index 10211a3..fa3d0de 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -5,33 +5,19 @@ #include "../api/Sprite.h" #include "../api/Transform.h" #include "../facade/SDLContext.h" -#include "../util/log.h" +#include "../util/Log.h" #include "RenderSystem.h" using namespace crepe; -RenderSystem::RenderSystem() { dbg_trace(); } +void RenderSystem::clear_screen() const { SDLContext::get_instance().clear_screen(); } -RenderSystem::~RenderSystem() { dbg_trace(); } - -RenderSystem & RenderSystem::get_instance() { - static RenderSystem instance; - return instance; -} - -void RenderSystem::clear_screen() const { - SDLContext::get_instance().clear_screen(); -} - -void RenderSystem::present_screen() const { - SDLContext::get_instance().present_screen(); -} +void RenderSystem::present_screen() const { SDLContext::get_instance().present_screen(); } void RenderSystem::update_camera() { - ComponentManager & mgr = ComponentManager::get_instance(); + ComponentManager & mgr = this->component_manager; - std::vector<std::reference_wrapper<Camera>> cameras - = mgr.get_components_by_type<Camera>(); + std::vector<std::reference_wrapper<Camera>> cameras = mgr.get_components_by_type<Camera>(); for (Camera & cam : cameras) { SDLContext::get_instance().camera(cam); @@ -39,16 +25,13 @@ void RenderSystem::update_camera() { } } void RenderSystem::render_sprites() const { + ComponentManager & mgr = this->component_manager; - ComponentManager & mgr = ComponentManager::get_instance(); - - std::vector<std::reference_wrapper<Sprite>> sprites - = mgr.get_components_by_type<Sprite>(); + std::vector<std::reference_wrapper<Sprite>> sprites = mgr.get_components_by_type<Sprite>(); SDLContext & render = SDLContext::get_instance(); for (const Sprite & sprite : sprites) { - auto transforms - = mgr.get_components_by_id<Transform>(sprite.game_object_id); + auto transforms = mgr.get_components_by_id<Transform>(sprite.game_object_id); render.draw(sprite, transforms[0], *curr_cam); } } diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h index 70db21a..87ec494 100644 --- a/src/crepe/system/RenderSystem.h +++ b/src/crepe/system/RenderSystem.h @@ -10,19 +10,13 @@ namespace crepe { * \class RenderSystem * \brief Manages rendering operations for all game objects. * - * RenderSystem is responsible for rendering sprites, clearing and presenting the screen, - * and managing the active camera. It functions as a singleton, providing centralized - * rendering services for the application. + * RenderSystem is responsible for rendering sprites, clearing and presenting the screen, and + * managing the active camera. It functions as a singleton, providing centralized rendering + * services for the application. */ class RenderSystem : public System { - public: - /** - * \brief Gets the singleton instance of RenderSystem. - * \return Reference to the RenderSystem instance. - */ - static RenderSystem & get_instance(); - + using System::System; /** * \brief Updates the RenderSystem for the current frame. * This method is called to perform all rendering operations for the current game frame. @@ -30,10 +24,6 @@ public: void update() override; private: - // Private constructor to enforce singleton pattern. - RenderSystem(); - ~RenderSystem(); - //! Clears the screen in preparation for rendering. void clear_screen() const; @@ -61,4 +51,5 @@ private: Camera * curr_cam = nullptr; // TODO: needs a better solution }; + } // namespace crepe diff --git a/src/crepe/system/ScriptSystem.cpp b/src/crepe/system/ScriptSystem.cpp index e41961e..c4d724c 100644 --- a/src/crepe/system/ScriptSystem.cpp +++ b/src/crepe/system/ScriptSystem.cpp @@ -5,7 +5,6 @@ #include "../ComponentManager.h" #include "../api/BehaviorScript.h" #include "../api/Script.h" -#include "../util/log.h" #include "ScriptSystem.h" @@ -13,20 +12,23 @@ using namespace std; using namespace crepe; void ScriptSystem::update() { - using namespace std; dbg_trace(); - forward_list<Script *> scripts = this->get_scripts(); - for (Script * script : scripts) - { - script->init(); - script->update(); - }; + forward_list<reference_wrapper<Script>> scripts = this->get_scripts(); + + for (auto & script_ref : scripts) { + Script & script = script_ref.get(); + if (!script.initialized) { + script.init(); + script.initialized = true; + } + script.update(); + } } -forward_list<Script *> ScriptSystem::get_scripts() { - forward_list<Script *> scripts = {}; - ComponentManager & mgr = ComponentManager::get_instance(); +forward_list<reference_wrapper<Script>> ScriptSystem::get_scripts() const { + forward_list<reference_wrapper<Script>> scripts = {}; + ComponentManager & mgr = this->component_manager; vector<reference_wrapper<BehaviorScript>> behavior_scripts = mgr.get_components_by_type<BehaviorScript>(); @@ -35,7 +37,7 @@ forward_list<Script *> ScriptSystem::get_scripts() { if (!behavior_script.active) continue; Script * script = behavior_script.script.get(); if (script == nullptr) continue; - scripts.push_front(script); + scripts.push_front(*script); } return scripts; diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h index 4fa6141..deb89cb 100644 --- a/src/crepe/system/ScriptSystem.h +++ b/src/crepe/system/ScriptSystem.h @@ -8,13 +8,32 @@ namespace crepe { class Script; +/** + * \brief Script system + * + * The script system is responsible for all \c BehaviorScript components, and + * calls the methods on classes derived from \c Script. + */ class ScriptSystem : public System { public: - void update(); + using System::System; + /** + * \brief Call Script::update() on all active \c BehaviorScript instances + * + * This routine updates all scripts sequentially using the Script::update() + * method. It also calls Script::init() if this has not been done before on + * the \c BehaviorScript instance. + */ + void update() override; private: - // TODO: to forward_list<reference_wrapper> - std::forward_list<Script *> get_scripts(); + /** + * \brief Aggregate all active \c BehaviorScript components and return a list + * of references to their \c Script instances (utility) + * + * \returns List of active \c Script instances + */ + std::forward_list<std::reference_wrapper<Script>> get_scripts() const; }; } // namespace crepe diff --git a/src/crepe/system/System.cpp b/src/crepe/system/System.cpp new file mode 100644 index 0000000..937a423 --- /dev/null +++ b/src/crepe/system/System.cpp @@ -0,0 +1,7 @@ +#include "../util/Log.h" + +#include "System.h" + +using namespace crepe; + +System::System(ComponentManager & mgr) : component_manager(mgr) { dbg_trace(); } diff --git a/src/crepe/system/System.h b/src/crepe/system/System.h index 3b81bef..28ea20e 100644 --- a/src/crepe/system/System.h +++ b/src/crepe/system/System.h @@ -2,13 +2,28 @@ namespace crepe { +class ComponentManager; + +/** + * \brief Base ECS system class + * + * This class is used as the base for all system classes. Classes derived from + * System must implement the System::update() method and copy Script::Script + * with the `using`-syntax. + */ class System { public: + /** + * \brief Process all components this system is responsible for. + */ virtual void update() = 0; public: - System() = default; + System(ComponentManager &); virtual ~System() = default; + +protected: + ComponentManager & component_manager; }; } // namespace crepe diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt index 0fa4343..4be738a 100644 --- a/src/crepe/util/CMakeLists.txt +++ b/src/crepe/util/CMakeLists.txt @@ -1,13 +1,12 @@ target_sources(crepe PUBLIC LogColor.cpp - log.cpp - fmt.cpp + Log.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES LogColor.h - log.h - fmt.h + Log.h + Log.hpp Proxy.h Proxy.hpp ) diff --git a/src/crepe/util/Log.cpp b/src/crepe/util/Log.cpp new file mode 100644 index 0000000..84d80a8 --- /dev/null +++ b/src/crepe/util/Log.cpp @@ -0,0 +1,37 @@ +#include <iostream> +#include <string> + +#include "../api/Config.h" + +#include "Log.h" + +using namespace crepe; +using namespace std; + +string Log::prefix(const Level & level) { + switch (level) { + case Level::TRACE: + return LogColor().fg_white().str("[TRACE]") + " "; + case Level::DEBUG: + return LogColor().fg_magenta().str("[DEBUG]") + " "; + case Level::INFO: + return LogColor().fg_blue().str("[INFO]") + " "; + case Level::WARNING: + return LogColor().fg_yellow().str("[WARN]") + " "; + case Level::ERROR: + return LogColor().fg_red().str("[ERROR]") + " "; + } + return ""; +} + +void Log::log(const Level & level, const string & msg) { + auto & cfg = Config::get_instance(); + if (level < cfg.log.level) return; + + string out = Log::prefix(level) + msg; + if (!out.ends_with("\n")) out += "\n"; + + // TODO: also log to file or smth + cout.write(out.data(), out.size()); + cout.flush(); +} diff --git a/src/crepe/util/Log.h b/src/crepe/util/Log.h new file mode 100644 index 0000000..d55b11e --- /dev/null +++ b/src/crepe/util/Log.h @@ -0,0 +1,84 @@ +#pragma once + +#include <format> + +// allow user to disable debug macros +#ifndef CREPE_DISABLE_MACROS + +#include "LogColor.h" + +// utility macros +#define _crepe_logf_here(level, fmt, ...) \ + crepe::Log::logf(level, "{}" fmt, \ + crepe::LogColor().fg_white(false).str(std::format( \ + "{} ({}:{})", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__)), \ + __VA_ARGS__) + +// very illegal global function-style macros +// NOLINTBEGIN +#define dbg_logf(fmt, ...) _crepe_logf_here(crepe::Log::Level::DEBUG, ": " fmt, __VA_ARGS__) +#define dbg_log(str) _crepe_logf_here(crepe::Log::Level::DEBUG, ": {}", str) +#define dbg_trace() _crepe_logf_here(crepe::Log::Level::TRACE, "", "") +// NOLINTEND + +#endif + +namespace crepe { + +/** + * \brief Logging utility + * + * This class is used to output log messages to the console and/or log files. + */ +class Log { +public: + //! Log message severity + enum Level { + TRACE, //< Include (internal) function calls + DEBUG, //< Include dbg_logf output + INFO, //< General-purpose messages + WARNING, //< Non-fatal errors + ERROR, //< Fatal errors + }; + + /** + * \brief Log a formatted message + * + * \param level Message severity + * \param msg Formatted message + */ + static void log(const Level & level, const std::string & msg); + + /** + * \brief Format a message and log it + * + * \param level Message severity + * \param fmt Message format + * \param args Format arguments + */ + template <class... Args> + static void logf(const Level & level, std::format_string<Args...> fmt, Args &&... args); + + /** + * \brief Format a message and log it (with default severity \c INFO) + * + * \param fmt Message format + * \param args Format arguments + */ + template <class... Args> + static void logf(std::format_string<Args...> fmt, Args &&... args); + +private: + /** + * \brief Output a message prefix depending on the log level + * + * \param level Message severity + * + * \return Colored message severity prefix string + */ + static std::string prefix(const Level & level); +}; + +} // namespace crepe + +#include "Log.hpp" diff --git a/src/crepe/util/Log.hpp b/src/crepe/util/Log.hpp new file mode 100644 index 0000000..c2156cd --- /dev/null +++ b/src/crepe/util/Log.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "Log.h" + +namespace crepe { + +template <class... Args> +void Log::logf(std::format_string<Args...> fmt, Args &&... args) { + Log::logf(Level::INFO, fmt, std::forward<Args>(args)...); +} + +template <class... Args> +void Log::logf(const Level & level, std::format_string<Args...> fmt, Args &&... args) { + Log::log(level, std::format(fmt, std::forward<Args>(args)...)); +} + +} // namespace crepe diff --git a/src/crepe/util/LogColor.cpp b/src/crepe/util/LogColor.cpp index b5fe3ea..5411898 100644 --- a/src/crepe/util/LogColor.cpp +++ b/src/crepe/util/LogColor.cpp @@ -1,16 +1,15 @@ #include <cstdarg> #include "../api/Config.h" -#include "LogColor.h" -#include "fmt.h" +#include "LogColor.h" using namespace crepe; using namespace std; static constexpr const char * RESET_CODE = "\e[0m"; -const string LogColor::str(const string & content) { +const string LogColor::str(const string & content) const { auto & cfg = Config::get_instance(); string out = content; if (cfg.log.color) out = this->code + out; @@ -19,21 +18,8 @@ const string LogColor::str(const string & content) { return out; } -const char * LogColor::c_str(const char * content) { - this->final = this->str(content == NULL ? "" : content); - return this->final.c_str(); -} - -const char * LogColor::fmt(const char * fmt, ...) { - va_list args; - va_start(args, fmt); - string content = va_stringf(args, fmt); - va_end(args); - return this->c_str(content.c_str()); -} - LogColor & LogColor::add_code(unsigned int code) { - this->code += stringf("\e[%dm", code); + this->code += format("\e[{}m", code); return *this; } @@ -42,51 +28,19 @@ LogColor & LogColor::reset() { return *this; } -LogColor & LogColor::fg_black(bool bright) { - return this->add_code(bright ? 90 : 30); -} -LogColor & LogColor::fg_red(bool bright) { - return this->add_code(bright ? 91 : 31); -} -LogColor & LogColor::fg_green(bool bright) { - return this->add_code(bright ? 92 : 32); -} -LogColor & LogColor::fg_yellow(bool bright) { - return this->add_code(bright ? 93 : 33); -} -LogColor & LogColor::fg_blue(bool bright) { - return this->add_code(bright ? 94 : 34); -} -LogColor & LogColor::fg_magenta(bool bright) { - return this->add_code(bright ? 95 : 35); -} -LogColor & LogColor::fg_cyan(bool bright) { - return this->add_code(bright ? 96 : 36); -} -LogColor & LogColor::fg_white(bool bright) { - return this->add_code(bright ? 97 : 37); -} -LogColor & LogColor::bg_black(bool bright) { - return this->add_code(bright ? 100 : 40); -} -LogColor & LogColor::bg_red(bool bright) { - return this->add_code(bright ? 101 : 41); -} -LogColor & LogColor::bg_green(bool bright) { - return this->add_code(bright ? 102 : 42); -} -LogColor & LogColor::bg_yellow(bool bright) { - return this->add_code(bright ? 103 : 43); -} -LogColor & LogColor::bg_blue(bool bright) { - return this->add_code(bright ? 104 : 44); -} -LogColor & LogColor::bg_magenta(bool bright) { - return this->add_code(bright ? 105 : 45); -} -LogColor & LogColor::bg_cyan(bool bright) { - return this->add_code(bright ? 106 : 46); -} -LogColor & LogColor::bg_white(bool bright) { - return this->add_code(bright ? 107 : 47); -} +LogColor & LogColor::fg_black(bool bright) { return this->add_code(bright ? 90 : 30); } +LogColor & LogColor::fg_red(bool bright) { return this->add_code(bright ? 91 : 31); } +LogColor & LogColor::fg_green(bool bright) { return this->add_code(bright ? 92 : 32); } +LogColor & LogColor::fg_yellow(bool bright) { return this->add_code(bright ? 93 : 33); } +LogColor & LogColor::fg_blue(bool bright) { return this->add_code(bright ? 94 : 34); } +LogColor & LogColor::fg_magenta(bool bright) { return this->add_code(bright ? 95 : 35); } +LogColor & LogColor::fg_cyan(bool bright) { return this->add_code(bright ? 96 : 36); } +LogColor & LogColor::fg_white(bool bright) { return this->add_code(bright ? 97 : 37); } +LogColor & LogColor::bg_black(bool bright) { return this->add_code(bright ? 100 : 40); } +LogColor & LogColor::bg_red(bool bright) { return this->add_code(bright ? 101 : 41); } +LogColor & LogColor::bg_green(bool bright) { return this->add_code(bright ? 102 : 42); } +LogColor & LogColor::bg_yellow(bool bright) { return this->add_code(bright ? 103 : 43); } +LogColor & LogColor::bg_blue(bool bright) { return this->add_code(bright ? 104 : 44); } +LogColor & LogColor::bg_magenta(bool bright) { return this->add_code(bright ? 105 : 45); } +LogColor & LogColor::bg_cyan(bool bright) { return this->add_code(bright ? 106 : 46); } +LogColor & LogColor::bg_white(bool bright) { return this->add_code(bright ? 107 : 47); } diff --git a/src/crepe/util/LogColor.h b/src/crepe/util/LogColor.h index c1170cb..132fb94 100644 --- a/src/crepe/util/LogColor.h +++ b/src/crepe/util/LogColor.h @@ -4,23 +4,35 @@ namespace crepe { +/** + * \brief Utility class for coloring text using ANSI escape codes + * + * \note Most methods in this class return a reference to \c this, which may be + * used to chain multiple display attributes. + */ class LogColor { public: - LogColor() = default; + /** + * \brief Get color code as STL string + * + * \param content If given, color this string and append a color reset escape sequence. + * + * \returns Color escape sequence + */ + const std::string str(const std::string & content = "") const; public: - //! get color code as c-style string (or color content string) - const char * c_str(const char * content = NULL); - //! color printf-style format string - const char * fmt(const char * fmt, ...); - //! get color code as stl string (or color content string) - const std::string str(const std::string & content = ""); - -public: - //! reset color to default foreground and background color + //! Reset color to default foreground and background color LogColor & reset(); public: + /** + * \name Foreground colors + * + * These functions set the foreground (text) color. The \c bright parameter + * makes the color brighter, or bold on some terminals. + * \{ + */ LogColor & fg_black(bool bright = false); LogColor & fg_red(bool bright = false); LogColor & fg_green(bool bright = false); @@ -29,8 +41,16 @@ public: LogColor & fg_magenta(bool bright = false); LogColor & fg_cyan(bool bright = false); LogColor & fg_white(bool bright = false); + /// \} public: + /** + * \name Background colors + * + * These functions set the background color. The \c bright parameter makes + * the color brighter. + * \{ + */ LogColor & bg_black(bool bright = false); LogColor & bg_red(bool bright = false); LogColor & bg_green(bool bright = false); @@ -39,13 +59,22 @@ public: LogColor & bg_magenta(bool bright = false); LogColor & bg_cyan(bool bright = false); LogColor & bg_white(bool bright = false); + /// \} private: + /** + * \brief Append SGR escape sequence to \c this->code + * + * \param code SGR attribute number + * + * See <https://en.wikipedia.org/wiki/ANSI_escape_code> for magic number + * reference. + */ LogColor & add_code(unsigned int code); private: + //! Color escape sequence std::string code = ""; - std::string final = ""; }; } // namespace crepe diff --git a/src/crepe/util/Proxy.h b/src/crepe/util/Proxy.h index f84e462..b34f7c6 100644 --- a/src/crepe/util/Proxy.h +++ b/src/crepe/util/Proxy.h @@ -7,8 +7,8 @@ namespace crepe { /** * \brief Utility wrapper for \c ValueBroker * - * This class can be used to to wrap a ValueBroker instance so it behaves like - * a regular variable. + * This class can be used to to wrap a ValueBroker instance so it behaves like a regular + * variable. * * \tparam T Type of the underlying variable */ diff --git a/src/crepe/util/fmt.cpp b/src/crepe/util/fmt.cpp deleted file mode 100644 index 4b50da8..0000000 --- a/src/crepe/util/fmt.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include <cstdarg> -#include <cstdio> -#include <string> - -#include "fmt.h" - -using namespace std; - -string crepe::va_stringf(va_list args, const char * fmt) { - string out; - - va_list args_copy; - va_copy(args_copy, args); - size_t length = vsnprintf(NULL, 0, fmt, args_copy); - // resize to include terminating null byte - out.resize(length + 1); - va_end(args_copy); - - // vsnprintf adds terminating null byte - vsnprintf(out.data(), out.size(), fmt, args); - // resize to actual length - out.resize(length); - - va_end(args); - - return out; -} - -string crepe::stringf(const char * fmt, ...) { - va_list args; - va_start(args, fmt); - string out = va_stringf(args, fmt); - va_end(args); - return out; -} diff --git a/src/crepe/util/fmt.h b/src/crepe/util/fmt.h deleted file mode 100644 index e319e6e..0000000 --- a/src/crepe/util/fmt.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include <string> - -namespace crepe { - -std::string va_stringf(va_list args, const char * fmt); -std::string stringf(const char * fmt, ...); - -} // namespace crepe diff --git a/src/crepe/util/log.cpp b/src/crepe/util/log.cpp deleted file mode 100644 index 4a8f8e8..0000000 --- a/src/crepe/util/log.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include <cstdarg> -#include <cstdio> -#include <cstdlib> -#include <string> - -#include "../api/Config.h" -#include "fmt.h" -#include "log.h" - -using namespace crepe; -using namespace std; - -string log_prefix(LogLevel level) { - switch (level) { - case LogLevel::TRACE: - return LogColor().fg_white().str("[TRACE]") + " "; - case LogLevel::DEBUG: - return LogColor().fg_magenta().str("[DEBUG]") + " "; - case LogLevel::INFO: - return LogColor().fg_blue().str("[INFO]") + " "; - case LogLevel::WARNING: - return LogColor().fg_yellow().str("[WARN]") + " "; - case LogLevel::ERROR: - return LogColor().fg_red().str("[ERROR]") + " "; - } - return ""; -} - -static void log(LogLevel level, const string msg) { - auto & cfg = Config::get_instance(); - if (level < cfg.log.level) return; - - string out = log_prefix(level) + msg; - if (!out.ends_with("\n")) out += "\n"; - - // TODO: also log to file or smth - fwrite(out.c_str(), 1, out.size(), stdout); - fflush(stdout); -} - -void crepe::logf(const char * fmt, ...) { - va_list args; - va_start(args, fmt); - log(LogLevel::DEBUG, va_stringf(args, fmt)); - va_end(args); -} - -void crepe::logf(LogLevel level, const char * fmt, ...) { - va_list args; - va_start(args, fmt); - log(level, va_stringf(args, fmt)); - va_end(args); -} diff --git a/src/crepe/util/log.h b/src/crepe/util/log.h deleted file mode 100644 index 5a1cf00..0000000 --- a/src/crepe/util/log.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -// allow user to disable debug macros -#ifndef CREPE_DISABLE_MACROS - -#include "LogColor.h" - -// utility macros -#define _crepe_logf_here(level, format, ...) \ - crepe::logf( \ - level, "%s" format, \ - crepe::LogColor().fg_white(false).fmt( \ - "%s (%s:%d)", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__), \ - __VA_ARGS__) - -// very illegal global function-style macros -// NOLINTBEGIN -#define dbg_logf(fmt, ...) \ - _crepe_logf_here(crepe::LogLevel::DEBUG, ": " fmt, __VA_ARGS__) -#define dbg_log(str) _crepe_logf_here(crepe::LogLevel::DEBUG, "%s: " str, "") -#define dbg_trace() _crepe_logf_here(crepe::LogLevel::TRACE, "%s", "") -// NOLINTEND - -#endif - -namespace crepe { - -enum LogLevel { - TRACE, - DEBUG, - INFO, - WARNING, - ERROR, -}; - -void logf(const char * fmt, ...); -void logf(enum LogLevel level, const char * fmt, ...); - -} // namespace crepe diff --git a/src/doc/feature/script.dox b/src/doc/feature/script.dox new file mode 100644 index 0000000..d25a63b --- /dev/null +++ b/src/doc/feature/script.dox @@ -0,0 +1,62 @@ +// vim:ft=doxygen +namespace crepe { +/** + +\defgroup feature_script Scripting +\ingroup feature +\brief User-defined scripts for game objects + +Scripts can be used to implement game behavior, and allow arbitrary code to run +as part of the game loop. Scripts are implemented as derivative classes of +Script, which are added to game objects using the BehaviorScript \ref Component +"component". + +\todo This section is incomplete: +- Utility functions to get components/events/etc inside script +- How to listen for events +- Extensions of script (keylistener) + +\see Script +\see BehaviorScript +\see GameObject + +\par Example + +First, define a class that inherits from Script. This class acts as an +interface, and has two functions (\ref Script::init "\c init()" and \ref +Script::update "\c update()"), which may be implemented (they are empty by +default). From now on, this derivative class will be referred to as a *concrete +script*. + +```cpp +#include <crepe/api/Script.h> +#include <crepe/api/BehaviorScript.h> + +class MyScript : public crepe::Script { + void init() { + // called once + } + void update() { + // called on fixed update + } +}; +``` + +Concrete scripts can be instantiated and attached to \ref GameObject +"game objects" using the BehaviorScript \ref Component "component". + +```cpp +using namespace crepe; +GameObject obj = component_manager.new_object("name"); + +// create BehaviorScript instance +BehaviorScript & behavior_script = obj.add_component<BehaviorScript>(); +// attach (and instantiate) MyScript to behavior_script +behavior_script.set_script<MyScript>(); + +// the above can also be done in a single call for convenience: +obj.add_component<BehaviorScript>().set_script<MyScript>(); +``` + +*/ +} diff --git a/src/doc/features.dox b/src/doc/features.dox new file mode 100644 index 0000000..4786bed --- /dev/null +++ b/src/doc/features.dox @@ -0,0 +1,10 @@ +// vim:ft=doxygen +/** + +\defgroup feature Features +\brief Engine components + +This page lists engine features and contains usage instructions for each +feature. + +*/ diff --git a/src/doc/index.dox b/src/doc/index.dox new file mode 100644 index 0000000..5ec7889 --- /dev/null +++ b/src/doc/index.dox @@ -0,0 +1,10 @@ +// vim:ft=doxygen +/** + +\mainpage crêpe game engine + +Welcome to the documentation for the crêpe game engine. + +\see feature + +*/ diff --git a/src/doc/installing.dox b/src/doc/installing.dox new file mode 100644 index 0000000..48b27d7 --- /dev/null +++ b/src/doc/installing.dox @@ -0,0 +1,9 @@ +// vim:ft=doxygen +/** + +\defgroup install Installation +\brief Engine installation instructions + +\todo This entire page + +*/ diff --git a/src/doc/layout.xml b/src/doc/layout.xml new file mode 100644 index 0000000..2244fa7 --- /dev/null +++ b/src/doc/layout.xml @@ -0,0 +1,252 @@ +<?xml version="1.0" encoding="UTF-8"?> +<doxygenlayout version="1.0"> + <navindex> + <tab type="mainpage" visible="yes" title=""/> + <tab type="pages" visible="no" title="" intro=""/> + <tab type="topics" visible="yes" title="" intro=""/> + <tab type="modules" visible="yes" title="" intro=""> + <tab type="modulelist" visible="yes" title="" intro=""/> + <tab type="modulemembers" visible="yes" title="" intro=""/> + </tab> + <tab type="namespaces" visible="no" title=""> + <tab type="namespacelist" visible="yes" title="" intro=""/> + <tab type="namespacemembers" visible="yes" title="" intro=""/> + </tab> + <tab type="concepts" visible="yes" title=""> + </tab> + <tab type="interfaces" visible="yes" title=""> + <tab type="interfacelist" visible="yes" title="" intro=""/> + <tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/> + <tab type="interfacehierarchy" visible="yes" title="" intro=""/> + </tab> + <tab type="classes" visible="yes" title=""> + <tab type="classlist" visible="yes" title="" intro=""/> + <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/> + <tab type="hierarchy" visible="yes" title="" intro=""/> + <tab type="classmembers" visible="yes" title="" intro=""/> + </tab> + <tab type="structs" visible="yes" title=""> + <tab type="structlist" visible="yes" title="" intro=""/> + <tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/> + </tab> + <tab type="exceptions" visible="yes" title=""> + <tab type="exceptionlist" visible="yes" title="" intro=""/> + <tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/> + <tab type="exceptionhierarchy" visible="yes" title="" intro=""/> + </tab> + <tab type="files" visible="yes" title=""> + <tab type="filelist" visible="yes" title="" intro=""/> + <tab type="globals" visible="yes" title="" intro=""/> + </tab> + <tab type="examples" visible="yes" title="" intro=""/> + </navindex> + <class> + <briefdescription visible="yes"/> + <includes visible="$SHOW_HEADERFILE"/> + <inheritancegraph visible="yes"/> + <collaborationgraph visible="yes"/> + <memberdecl> + <nestedclasses visible="yes" title=""/> + <publictypes title=""/> + <services title=""/> + <interfaces title=""/> + <publicslots title=""/> + <signals title=""/> + <publicmethods title=""/> + <publicstaticmethods title=""/> + <publicattributes title=""/> + <publicstaticattributes title=""/> + <protectedtypes title=""/> + <protectedslots title=""/> + <protectedmethods title=""/> + <protectedstaticmethods title=""/> + <protectedattributes title=""/> + <protectedstaticattributes title=""/> + <packagetypes title=""/> + <packagemethods title=""/> + <packagestaticmethods title=""/> + <packageattributes title=""/> + <packagestaticattributes title=""/> + <properties title=""/> + <events title=""/> + <privatetypes title=""/> + <privateslots title=""/> + <privatemethods title=""/> + <privatestaticmethods title=""/> + <privateattributes title=""/> + <privatestaticattributes title=""/> + <friends title=""/> + <related title="" subtitle=""/> + <membergroups visible="yes"/> + </memberdecl> + <detaileddescription title=""/> + <memberdef> + <inlineclasses title=""/> + <typedefs title=""/> + <enums title=""/> + <services title=""/> + <interfaces title=""/> + <constructors title=""/> + <functions title=""/> + <related title=""/> + <variables title=""/> + <properties title=""/> + <events title=""/> + </memberdef> + <allmemberslink visible="yes"/> + <usedfiles visible="$SHOW_USED_FILES"/> + <authorsection visible="yes"/> + </class> + <namespace> + <briefdescription visible="yes"/> + <memberdecl> + <nestednamespaces visible="yes" title=""/> + <constantgroups visible="yes" title=""/> + <interfaces visible="yes" title=""/> + <classes visible="yes" title=""/> + <concepts visible="yes" title=""/> + <structs visible="yes" title=""/> + <exceptions visible="yes" title=""/> + <typedefs title=""/> + <sequences title=""/> + <dictionaries title=""/> + <enums title=""/> + <functions title=""/> + <variables title=""/> + <properties title=""/> + <membergroups visible="yes"/> + </memberdecl> + <detaileddescription title=""/> + <memberdef> + <inlineclasses title=""/> + <typedefs title=""/> + <sequences title=""/> + <dictionaries title=""/> + <enums title=""/> + <functions title=""/> + <variables title=""/> + <properties title=""/> + </memberdef> + <authorsection visible="yes"/> + </namespace> + <concept> + <briefdescription visible="yes"/> + <includes visible="$SHOW_HEADERFILE"/> + <definition visible="yes" title=""/> + <detaileddescription title=""/> + <authorsection visible="yes"/> + </concept> + <file> + <briefdescription visible="yes"/> + <includes visible="$SHOW_INCLUDE_FILES"/> + <includegraph visible="yes"/> + <includedbygraph visible="yes"/> + <sourcelink visible="yes"/> + <memberdecl> + <interfaces visible="yes" title=""/> + <classes visible="yes" title=""/> + <structs visible="yes" title=""/> + <exceptions visible="yes" title=""/> + <namespaces visible="yes" title=""/> + <concepts visible="yes" title=""/> + <constantgroups visible="yes" title=""/> + <defines title=""/> + <typedefs title=""/> + <sequences title=""/> + <dictionaries title=""/> + <enums title=""/> + <functions title=""/> + <variables title=""/> + <properties title=""/> + <membergroups visible="yes"/> + </memberdecl> + <detaileddescription title=""/> + <memberdef> + <inlineclasses title=""/> + <defines title=""/> + <typedefs title=""/> + <sequences title=""/> + <dictionaries title=""/> + <enums title=""/> + <functions title=""/> + <variables title=""/> + <properties title=""/> + </memberdef> + <authorsection/> + </file> + <group> + <detaileddescription title=""/> + <groupgraph visible="yes"/> + <memberdecl> + <nestedgroups visible="yes" title=""/> + <modules visible="yes" title=""/> + <dirs visible="yes" title=""/> + <files visible="yes" title=""/> + <namespaces visible="yes" title=""/> + <concepts visible="yes" title=""/> + <classes visible="yes" title=""/> + <defines title=""/> + <typedefs title=""/> + <sequences title=""/> + <dictionaries title=""/> + <enums title=""/> + <enumvalues title=""/> + <functions title=""/> + <variables title=""/> + <signals title=""/> + <publicslots title=""/> + <protectedslots title=""/> + <privateslots title=""/> + <events title=""/> + <properties title=""/> + <friends title=""/> + <membergroups visible="yes"/> + </memberdecl> + <memberdef> + <pagedocs/> + <inlineclasses title=""/> + <defines title=""/> + <typedefs title=""/> + <sequences title=""/> + <dictionaries title=""/> + <enums title=""/> + <enumvalues title=""/> + <functions title=""/> + <variables title=""/> + <signals title=""/> + <publicslots title=""/> + <protectedslots title=""/> + <privateslots title=""/> + <events title=""/> + <properties title=""/> + <friends title=""/> + </memberdef> + <authorsection visible="yes"/> + </group> + <module> + <briefdescription visible="yes"/> + <exportedmodules visible="yes"/> + <memberdecl> + <concepts visible="yes" title=""/> + <classes visible="yes" title=""/> + <enums title=""/> + <typedefs title=""/> + <functions title=""/> + <variables title=""/> + <membergroups title=""/> + </memberdecl> + <detaileddescription title=""/> + <memberdecl> + <files visible="yes"/> + </memberdecl> + </module> + <directory> + <briefdescription visible="yes"/> + <directorygraph visible="yes"/> + <memberdecl> + <dirs visible="yes"/> + <files visible="yes"/> + </memberdecl> + <detaileddescription title=""/> + </directory> +</doxygenlayout> diff --git a/src/doc/style.css b/src/doc/style.css new file mode 100644 index 0000000..08bc9f5 --- /dev/null +++ b/src/doc/style.css @@ -0,0 +1,4 @@ +#titlearea, +address { + display: none; +} diff --git a/src/example/asset_manager.cpp b/src/example/asset_manager.cpp index cf64f89..917b547 100644 --- a/src/example/asset_manager.cpp +++ b/src/example/asset_manager.cpp @@ -6,18 +6,17 @@ using namespace crepe; int main() { - // this needs to be called before the asset manager otherwise the destructor - // of sdl is not in the right order + // this needs to be called before the asset manager otherwise the destructor of sdl is not in + // the right order { Texture test("../asset/texture/img.png"); } - // FIXME: make it so the issue described by the above comment is not possible - // (i.e. the order in which internal classes are instantiated should not - // impact the way the engine works). + // FIXME: make it so the issue described by the above comment is not possible (i.e. the order + // in which internal classes are instantiated should not impact the way the engine works). auto & mgr = AssetManager::get_instance(); { - // TODO: [design] the Sound class can't be directly included by the user as - // it includes SoLoud headers. + // TODO: [design] the Sound class can't be directly included by the user as it includes + // SoLoud headers. auto bgm = mgr.cache<Sound>("../mwe/audio/bgm.ogg"); auto sfx1 = mgr.cache<Sound>("../mwe/audio/sfx1.wav"); auto sfx2 = mgr.cache<Sound>("../mwe/audio/sfx2.wav"); diff --git a/src/example/audio_internal.cpp b/src/example/audio_internal.cpp index 1ea839d..661161a 100644 --- a/src/example/audio_internal.cpp +++ b/src/example/audio_internal.cpp @@ -5,7 +5,7 @@ #include <crepe/api/Config.h> #include <crepe/facade/Sound.h> -#include <crepe/util/log.h> +#include <crepe/util/Log.h> #include <thread> @@ -18,7 +18,7 @@ using std::make_unique; int _ = []() { // Show dbg_trace() output auto & cfg = Config::get_instance(); - cfg.log.level = LogLevel::TRACE; + cfg.log.level = Log::Level::TRACE; return 0; // satisfy compiler }(); @@ -34,8 +34,7 @@ int main() { // Start the background track bgm.play(); - // Play each sample sequentially while pausing and resuming the background - // track + // Play each sample sequentially while pausing and resuming the background track this_thread::sleep_for(500ms); sfx1.play(); this_thread::sleep_for(500ms); diff --git a/src/example/components_internal.cpp b/src/example/components_internal.cpp index ea1eaad..2a232a9 100644 --- a/src/example/components_internal.cpp +++ b/src/example/components_internal.cpp @@ -13,7 +13,7 @@ #include <crepe/api/Rigidbody.h> #include <crepe/api/Sprite.h> -#include <crepe/util/log.h> +#include <crepe/util/Log.h> using namespace crepe; using namespace std; @@ -23,17 +23,14 @@ using namespace std; int main() { dbg_trace(); - auto & mgr = ComponentManager::get_instance(); + ComponentManager mgr{}; auto start_adding = chrono::high_resolution_clock::now(); - GameObject * game_object[OBJ_COUNT]; - for (int i = 0; i < OBJ_COUNT; ++i) { - game_object[i] = new GameObject(i, "Name", "Tag", 0); - - game_object[i]->add_component<Sprite>("test"); - game_object[i]->add_component<Rigidbody>(0, 0, i); + GameObject obj = mgr.new_object("Name", "Tag"); + obj.add_component<Sprite>("test"); + obj.add_component<Rigidbody>(0, 0, i); } auto stop_adding = chrono::high_resolution_clock::now(); @@ -45,14 +42,8 @@ int main() { auto stop_looping = chrono::high_resolution_clock::now(); - for (int i = 0; i < OBJ_COUNT; ++i) { - delete game_object[i]; - } - - auto add_time = chrono::duration_cast<chrono::microseconds>(stop_adding - - start_adding); - auto loop_time = chrono::duration_cast<chrono::microseconds>(stop_looping - - stop_adding); + auto add_time = chrono::duration_cast<chrono::microseconds>(stop_adding - start_adding); + auto loop_time = chrono::duration_cast<chrono::microseconds>(stop_looping - stop_adding); printf("add time: %ldus\n", add_time.count()); printf("loop time: %ldus\n", loop_time.count()); diff --git a/src/example/db.cpp b/src/example/db.cpp index 8c06a84..ee4e8fc 100644 --- a/src/example/db.cpp +++ b/src/example/db.cpp @@ -1,6 +1,6 @@ #include <crepe/api/Config.h> #include <crepe/facade/DB.h> -#include <crepe/util/log.h> +#include <crepe/util/Log.h> using namespace crepe; using namespace std; @@ -8,7 +8,7 @@ using namespace std; // run before main static auto _ = []() { auto & cfg = Config::get_instance(); - cfg.log.level = LogLevel::TRACE; + cfg.log.level = Log::Level::TRACE; return 0; }(); @@ -20,11 +20,11 @@ int main() { const char * test_key = "test-key"; string test_data = "Hello world!"; - dbg_logf("DB has key = %d", db.has(test_key)); + dbg_logf("DB has key = {}", db.has(test_key)); db.set(test_key, test_data); - dbg_logf("key = \"%s\"", db.get(test_key).c_str()); + dbg_logf("key = \"{}\"", db.get(test_key)); return 0; } diff --git a/src/example/ecs.cpp b/src/example/ecs.cpp index e61c398..d5ba51b 100644 --- a/src/example/ecs.cpp +++ b/src/example/ecs.cpp @@ -9,13 +9,15 @@ using namespace crepe; using namespace std; int main() { + ComponentManager mgr{}; + // Create a few GameObjects try { - GameObject body(0, "body", "person", Vector2{0, 0}, 0, 1); - GameObject right_leg(1, "rightLeg", "person", Vector2{1, 1}, 0, 1); - GameObject left_leg(2, "leftLeg", "person", Vector2{1, 1}, 0, 1); - GameObject right_foot(3, "rightFoot", "person", Vector2{2, 2}, 0, 1); - GameObject left_foot(4, "leftFoot", "person", Vector2{2, 2}, 0, 1); + GameObject body = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1); + GameObject right_leg = mgr.new_object("rightLeg", "person", Vector2{1, 1}, 0, 1); + GameObject left_leg = mgr.new_object("leftLeg", "person", Vector2{1, 1}, 0, 1); + GameObject right_foot = mgr.new_object("rightFoot", "person", Vector2{2, 2}, 0, 1); + GameObject left_foot = mgr.new_object("leftFoot", "person", Vector2{2, 2}, 0, 1); // Set the parent of each GameObject right_foot.set_parent(right_leg); @@ -30,25 +32,21 @@ int main() { } // Get the Metadata and Transform components of each GameObject - ComponentManager & mgr = ComponentManager::get_instance(); - vector<reference_wrapper<Metadata>> metadata - = mgr.get_components_by_type<Metadata>(); - vector<reference_wrapper<Transform>> transform - = mgr.get_components_by_type<Transform>(); + vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>(); + vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>(); // Print the Metadata and Transform components for (auto & m : metadata) { cout << "Id: " << m.get().game_object_id << " Name: " << m.get().name - << " Tag: " << m.get().tag << " Parent: " << m.get().parent - << " Children: "; + << " Tag: " << m.get().tag << " Parent: " << m.get().parent << " Children: "; for (auto & c : m.get().children) { cout << c << " "; } cout << endl; } for (auto & t : transform) { - cout << "Id: " << t.get().game_object_id << " Position: [" - << t.get().position.x << ", " << t.get().position.y << "]" << endl; + cout << "Id: " << t.get().game_object_id << " Position: [" << t.get().position.x + << ", " << t.get().position.y << "]" << endl; } return 0; diff --git a/src/example/log.cpp b/src/example/log.cpp index db8aa48..5baa021 100644 --- a/src/example/log.cpp +++ b/src/example/log.cpp @@ -4,7 +4,7 @@ */ #include <crepe/api/Config.h> -#include <crepe/util/log.h> +#include <crepe/util/Log.h> using namespace crepe; @@ -12,17 +12,17 @@ using namespace crepe; int _ = []() { // make sure all log messages get printed auto & cfg = Config::get_instance(); - cfg.log.level = LogLevel::TRACE; + cfg.log.level = Log::Level::TRACE; return 0; // satisfy compiler }(); int main() { dbg_trace(); - dbg_logf("test printf parameters: %d", 3); - logf(LogLevel::INFO, "info message"); - logf(LogLevel::WARNING, "warning"); - logf(LogLevel::ERROR, "error"); + 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/particles.cpp b/src/example/particles.cpp index 6eab046..3d5f676 100644 --- a/src/example/particles.cpp +++ b/src/example/particles.cpp @@ -14,11 +14,11 @@ using namespace crepe; using namespace std; int main(int argc, char * argv[]) { - GameObject game_object(0, "", "", Vector2{0, 0}, 0, 0); + ComponentManager mgr{}; + 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, - FlipSettings{true, true}); + make_shared<Texture>("../asset/texture/img.png"), color, FlipSettings{true, true}); game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{ .position = {0, 0}, .max_particles = 100, diff --git a/src/example/physics.cpp b/src/example/physics.cpp index 848f857..ad663a0 100644 --- a/src/example/physics.cpp +++ b/src/example/physics.cpp @@ -9,9 +9,10 @@ using namespace crepe; using namespace std; int main(int argc, char * argv[]) { - GameObject * game_object; - game_object = new GameObject(0, "Name", "Tag", Vector2{0, 0}, 0, 0); - game_object->add_component<Rigidbody>(Rigidbody::Data{ + ComponentManager mgr{}; + + GameObject game_object = mgr.new_object("Name", "Tag", Vector2{0, 0}, 0, 0); + game_object.add_component<Rigidbody>(Rigidbody::Data{ .mass = 1, .gravity_scale = 1, .body_type = Rigidbody::BodyType::DYNAMIC, @@ -19,6 +20,5 @@ int main(int argc, char * argv[]) { .use_gravity = true, .bounce = false, }); - delete game_object; return 0; } diff --git a/src/example/proxy.cpp b/src/example/proxy.cpp index 0afff41..69451f8 100644 --- a/src/example/proxy.cpp +++ b/src/example/proxy.cpp @@ -5,8 +5,8 @@ #include <crepe/ValueBroker.h> #include <crepe/api/Config.h> +#include <crepe/util/Log.h> #include <crepe/util/Proxy.h> -#include <crepe/util/log.h> using namespace std; using namespace crepe; @@ -17,18 +17,17 @@ void test_ro_val(int val) {} int main() { auto & cfg = Config::get_instance(); - cfg.log.level = LogLevel::DEBUG; + cfg.log.level = Log::Level::DEBUG; int real_value = 0; ValueBroker<int> broker{ [&real_value](const int & target) { - dbg_logf("set %s to %s", to_string(real_value).c_str(), - to_string(target).c_str()); + dbg_logf("set {} to {}", real_value, target); real_value = target; }, [&real_value]() -> const int & { - dbg_logf("get %s", to_string(real_value).c_str()); + dbg_logf("get {}", real_value); return real_value; }, }; diff --git a/src/example/rendering.cpp b/src/example/rendering.cpp index 813fd25..c9e62f1 100644 --- a/src/example/rendering.cpp +++ b/src/example/rendering.cpp @@ -2,7 +2,7 @@ #include <crepe/ComponentManager.h> #include <crepe/api/GameObject.h> #include <crepe/system/RenderSystem.h> -#include <crepe/util/log.h> +#include <crepe/util/Log.h> #include <crepe/api/AssetManager.h> #include <crepe/api/Color.h> @@ -20,23 +20,24 @@ using namespace crepe; int main() { dbg_trace(); - auto obj = GameObject(0, "name", "tag", Vector2{0, 0}, 1, 1); - auto obj1 = GameObject(1, "name", "tag", Vector2{500, 0}, 1, 0.1); - auto obj2 = GameObject(2, "name", "tag", Vector2{800, 0}, 1, 0.1); + ComponentManager mgr{}; + RenderSystem sys{mgr}; + + GameObject obj = mgr.new_object("name", "tag", Vector2{0, 0}, 1, 1); + GameObject obj1 = mgr.new_object("name", "tag", Vector2{500, 0}, 1, 0.1); + GameObject obj2 = mgr.new_object("name", "tag", Vector2{800, 0}, 1, 0.1); // Normal adding components { Color color(0, 0, 0, 0); - obj.add_component<Sprite>( - make_shared<Texture>("/home/jaro/crepe/asset/texture/img.png"), color, - FlipSettings{false, false}); + obj.add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"), color, + FlipSettings{false, false}); obj.add_component<Camera>(Color::get_red()); } { Color color(0, 0, 0, 0); - obj1.add_component<Sprite>( - make_shared<Texture>("/home/jaro/crepe/asset/texture/img.png"), color, - FlipSettings{true, true}); + obj1.add_component<Sprite>(make_shared<Texture>("../asset/texture/second.png"), color, + FlipSettings{true, true}); } /* @@ -47,7 +48,6 @@ int main() { } */ - auto & sys = crepe::RenderSystem::get_instance(); auto start = std::chrono::steady_clock::now(); while (std::chrono::steady_clock::now() - start < std::chrono::seconds(5)) { sys.update(); diff --git a/src/example/savemgr.cpp b/src/example/savemgr.cpp index 436fb5a..65c4a34 100644 --- a/src/example/savemgr.cpp +++ b/src/example/savemgr.cpp @@ -6,8 +6,8 @@ #include <cassert> #include <crepe/api/Config.h> #include <crepe/api/SaveManager.h> +#include <crepe/util/Log.h> #include <crepe/util/Proxy.h> -#include <crepe/util/log.h> using namespace crepe; @@ -15,7 +15,7 @@ using namespace crepe; int _ = []() { // make sure all log messages get printed auto & cfg = Config::get_instance(); - cfg.log.level = LogLevel::TRACE; + cfg.log.level = Log::Level::TRACE; return 0; // satisfy compiler }(); @@ -25,19 +25,19 @@ int main() { SaveManager & mgr = SaveManager::get_instance(); - dbg_logf("has key = %s", mgr.has(key) ? "true" : "false"); + dbg_logf("has key = {}", mgr.has(key)); ValueBroker<int> prop = mgr.get<int>(key, 0); Proxy<int> val = mgr.get<int>(key, 0); - dbg_logf("val = %d", mgr.get<int>(key).get()); + dbg_logf("val = {}", mgr.get<int>(key).get()); prop.set(1); - dbg_logf("val = %d", mgr.get<int>(key).get()); + dbg_logf("val = {}", mgr.get<int>(key).get()); val = 2; - dbg_logf("val = %d", mgr.get<int>(key).get()); + dbg_logf("val = {}", mgr.get<int>(key).get()); mgr.set<int>(key, 3); - dbg_logf("val = %d", mgr.get<int>(key).get()); + dbg_logf("val = {}", mgr.get<int>(key).get()); - dbg_logf("has key = %s", mgr.has(key) ? "true" : "false"); + dbg_logf("has key = {}", mgr.has(key)); assert(true == mgr.has(key)); return 0; diff --git a/src/example/scene_manager.cpp b/src/example/scene_manager.cpp index f46dc36..accec7d 100644 --- a/src/example/scene_manager.cpp +++ b/src/example/scene_manager.cpp @@ -12,40 +12,44 @@ using namespace std; class ConcreteScene1 : public Scene { public: - ConcreteScene1(string name) : Scene(name) {} + using Scene::Scene; void load_scene() { - GameObject object1(0, "scene_1", "tag_scene_1", Vector2{0, 0}, 0, 1); - GameObject object2(1, "scene_1", "tag_scene_1", Vector2{1, 0}, 0, 1); - GameObject object3(2, "scene_1", "tag_scene_1", Vector2{2, 0}, 0, 1); + auto & mgr = this->component_manager; + GameObject object1 = mgr.new_object("scene_1", "tag_scene_1", Vector2{0, 0}, 0, 1); + 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); } }; class ConcreteScene2 : public Scene { public: - ConcreteScene2(string name) : Scene(name) {} + using Scene::Scene; void load_scene() { - GameObject object1(0, "scene_2", "tag_scene_2", Vector2{0, 0}, 0, 1); - GameObject object2(1, "scene_2", "tag_scene_2", Vector2{0, 1}, 0, 1); - GameObject object3(2, "scene_2", "tag_scene_2", Vector2{0, 2}, 0, 1); - GameObject object4(3, "scene_2", "tag_scene_2", Vector2{0, 3}, 0, 1); + auto & mgr = this->component_manager; + GameObject object1 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 0}, 0, 1); + GameObject object2 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 1}, 0, 1); + 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); } }; int main() { - SceneManager & scene_mgr = SceneManager::get_instance(); + ComponentManager component_mgr{}; + SceneManager scene_mgr{component_mgr}; // Add the scenes to the scene manager scene_mgr.add_scene<ConcreteScene1>("scene1"); scene_mgr.add_scene<ConcreteScene2>("scene2"); - // There is no need to call set_next_scene() at the beginnen, because the first scene will be automatically set as the next scene + // There is no need to call set_next_scene() at the beginnen, because the first scene will be + // automatically set as the next scene + // Load scene1 (the first scene added) scene_mgr.load_next_scene(); // Get the Metadata components of each GameObject of Scene1 - ComponentManager & component_mgr = ComponentManager::get_instance(); vector<reference_wrapper<Metadata>> metadata = component_mgr.get_components_by_type<Metadata>(); diff --git a/src/example/script.cpp b/src/example/script.cpp index 9e8b147..a23295b 100644 --- a/src/example/script.cpp +++ b/src/example/script.cpp @@ -5,7 +5,7 @@ #include <crepe/ComponentManager.h> #include <crepe/system/ScriptSystem.h> -#include <crepe/util/log.h> +#include <crepe/util/Log.h> #include <crepe/api/BehaviorScript.h> #include <crepe/api/Config.h> @@ -20,7 +20,7 @@ using namespace std; int _ = []() { // Show dbg_trace() output auto & cfg = Config::get_instance(); - cfg.log.level = LogLevel::TRACE; + cfg.log.level = Log::Level::TRACE; return 0; // satisfy compiler }(); @@ -30,20 +30,20 @@ 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); + 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 - auto obj = GameObject(0, "name", "tag", Vector2{1.2, 3.4}, 0, 1); + GameObject obj = component_manager.new_object("name"); obj.add_component<BehaviorScript>().set_script<MyScript>(); - // Get ScriptSystem singleton instance (this would normally be done from the - // game loop) - ScriptSystem sys; // Update all scripts. This should result in MyScript::update being called - sys.update(); + system.update(); return EXIT_SUCCESS; } diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 50802de..a41d097 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,9 +1,10 @@ target_sources(test_main PUBLIC dummy.cpp # audio.cpp - PhysicsTest.cpp CollisionTest.cpp - # PhysicsTest.cpp + main.cpp + PhysicsTest.cpp + ScriptTest.cpp ParticleTest.cpp ) diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp index 6fe3133..4e655a9 100644 --- a/src/test/ParticleTest.cpp +++ b/src/test/ParticleTest.cpp @@ -16,15 +16,16 @@ using namespace std::chrono_literals; using namespace crepe; class ParticlesTest : public ::testing::Test { -protected: - ParticleSystem particle_system; +public: + ComponentManager component_manager; + ParticleSystem particle_system{component_manager}; + void SetUp() override { - ComponentManager & mgr = ComponentManager::get_instance(); + ComponentManager & mgr = this->component_manager; std::vector<std::reference_wrapper<Transform>> transforms = mgr.get_components_by_id<Transform>(0); if (transforms.empty()) { - - GameObject game_object(0, "", "", Vector2{0, 0}, 0, 0); + 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>( @@ -77,9 +78,8 @@ protected: TEST_F(ParticlesTest, spawnParticle) { Config::get_instance().physics.gravity = 1; - ComponentManager & mgr = ComponentManager::get_instance(); - ParticleEmitter & emitter - = mgr.get_components_by_id<ParticleEmitter>(0).front().get(); + ComponentManager & mgr = this->component_manager; + ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get(); emitter.data.end_lifespan = 5; emitter.data.boundary.height = 100; emitter.data.boundary.width = 100; @@ -122,9 +122,8 @@ TEST_F(ParticlesTest, spawnParticle) { TEST_F(ParticlesTest, moveParticleHorizontal) { Config::get_instance().physics.gravity = 1; - ComponentManager & mgr = ComponentManager::get_instance(); - ParticleEmitter & emitter - = mgr.get_components_by_id<ParticleEmitter>(0).front().get(); + ComponentManager & mgr = this->component_manager; + ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get(); emitter.data.end_lifespan = 100; emitter.data.boundary.height = 100; emitter.data.boundary.width = 100; @@ -140,9 +139,8 @@ TEST_F(ParticlesTest, moveParticleHorizontal) { TEST_F(ParticlesTest, moveParticleVertical) { Config::get_instance().physics.gravity = 1; - ComponentManager & mgr = ComponentManager::get_instance(); - ParticleEmitter & emitter - = mgr.get_components_by_id<ParticleEmitter>(0).front().get(); + ComponentManager & mgr = this->component_manager; + ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get(); emitter.data.end_lifespan = 100; emitter.data.boundary.height = 100; emitter.data.boundary.width = 100; @@ -159,9 +157,8 @@ TEST_F(ParticlesTest, moveParticleVertical) { TEST_F(ParticlesTest, boundaryParticleReset) { Config::get_instance().physics.gravity = 1; - ComponentManager & mgr = ComponentManager::get_instance(); - ParticleEmitter & emitter - = mgr.get_components_by_id<ParticleEmitter>(0).front().get(); + ComponentManager & mgr = this->component_manager; + ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get(); emitter.data.end_lifespan = 100; emitter.data.boundary.height = 10; emitter.data.boundary.width = 10; @@ -179,9 +176,8 @@ TEST_F(ParticlesTest, boundaryParticleReset) { TEST_F(ParticlesTest, boundaryParticleStop) { Config::get_instance().physics.gravity = 1; - ComponentManager & mgr = ComponentManager::get_instance(); - ParticleEmitter & emitter - = mgr.get_components_by_id<ParticleEmitter>(0).front().get(); + ComponentManager & mgr = this->component_manager; + ParticleEmitter & emitter = mgr.get_components_by_id<ParticleEmitter>(0).front().get(); emitter.data.end_lifespan = 100; emitter.data.boundary.height = 10; emitter.data.boundary.width = 10; diff --git a/src/test/PhysicsTest.cpp b/src/test/PhysicsTest.cpp index 3332f74..1e37c26 100644 --- a/src/test/PhysicsTest.cpp +++ b/src/test/PhysicsTest.cpp @@ -11,17 +11,17 @@ using namespace std::chrono_literals; using namespace crepe; class PhysicsTest : public ::testing::Test { -protected: - GameObject * game_object; - PhysicsSystem physics_system; +public: + ComponentManager component_manager; + PhysicsSystem system{component_manager}; + void SetUp() override { - ComponentManager & mgr = ComponentManager::get_instance(); - mgr.delete_all_components(); - std::vector<std::reference_wrapper<Transform>> transforms + ComponentManager & mgr = this->component_manager; + vector<reference_wrapper<Transform>> transforms = mgr.get_components_by_id<Transform>(0); if (transforms.empty()) { - game_object = new GameObject(0, "", "", Vector2{0, 0}, 0, 0); - game_object->add_component<Rigidbody>(Rigidbody::Data{ + auto entity = mgr.new_object("", "", Vector2{0, 0}, 0, 0); + entity.add_component<Rigidbody>(Rigidbody::Data{ .mass = 1, .gravity_scale = 1, .body_type = Rigidbody::BodyType::DYNAMIC, @@ -37,7 +37,7 @@ protected: transform.position.x = 0.0; transform.position.y = 0.0; transform.rotation = 0.0; - std::vector<std::reference_wrapper<Rigidbody>> rigidbodies + vector<reference_wrapper<Rigidbody>> rigidbodies = mgr.get_components_by_id<Rigidbody>(0); Rigidbody & rigidbody = rigidbodies.front().get(); rigidbody.data.angular_velocity = 0; @@ -48,34 +48,36 @@ protected: TEST_F(PhysicsTest, gravity) { Config::get_instance().physics.gravity = 1; - ComponentManager & mgr = ComponentManager::get_instance(); - std::vector<std::reference_wrapper<Transform>> transforms - = mgr.get_components_by_id<Transform>(0); + ComponentManager & mgr = this->component_manager; + vector<reference_wrapper<Transform>> transforms = mgr.get_components_by_id<Transform>(0); const Transform & transform = transforms.front().get(); ASSERT_FALSE(transforms.empty()); EXPECT_EQ(transform.position.y, 0); - physics_system.update(); + + system.update(); EXPECT_EQ(transform.position.y, 1); - physics_system.update(); + + system.update(); EXPECT_EQ(transform.position.y, 3); } TEST_F(PhysicsTest, max_velocity) { - ComponentManager & mgr = ComponentManager::get_instance(); - std::vector<std::reference_wrapper<Rigidbody>> rigidbodies - = mgr.get_components_by_id<Rigidbody>(0); + ComponentManager & mgr = this->component_manager; + vector<reference_wrapper<Rigidbody>> rigidbodies = mgr.get_components_by_id<Rigidbody>(0); Rigidbody & rigidbody = rigidbodies.front().get(); ASSERT_FALSE(rigidbodies.empty()); EXPECT_EQ(rigidbody.data.linear_velocity.y, 0); + rigidbody.add_force_linear({100, 100}); rigidbody.add_force_angular(100); - physics_system.update(); + system.update(); EXPECT_EQ(rigidbody.data.linear_velocity.y, 10); EXPECT_EQ(rigidbody.data.linear_velocity.x, 10); EXPECT_EQ(rigidbody.data.angular_velocity, 10); + rigidbody.add_force_linear({-100, -100}); rigidbody.add_force_angular(-100); - physics_system.update(); + system.update(); EXPECT_EQ(rigidbody.data.linear_velocity.y, -10); EXPECT_EQ(rigidbody.data.linear_velocity.x, -10); EXPECT_EQ(rigidbody.data.angular_velocity, -10); @@ -83,39 +85,42 @@ TEST_F(PhysicsTest, max_velocity) { TEST_F(PhysicsTest, movement) { Config::get_instance().physics.gravity = 0; - ComponentManager & mgr = ComponentManager::get_instance(); - std::vector<std::reference_wrapper<Rigidbody>> rigidbodies - = mgr.get_components_by_id<Rigidbody>(0); + ComponentManager & mgr = this->component_manager; + vector<reference_wrapper<Rigidbody>> rigidbodies = mgr.get_components_by_id<Rigidbody>(0); Rigidbody & rigidbody = rigidbodies.front().get(); - std::vector<std::reference_wrapper<Transform>> transforms - = mgr.get_components_by_id<Transform>(0); + vector<reference_wrapper<Transform>> transforms = mgr.get_components_by_id<Transform>(0); const Transform & transform = transforms.front().get(); ASSERT_FALSE(rigidbodies.empty()); ASSERT_FALSE(transforms.empty()); + rigidbody.add_force_linear({1, 1}); rigidbody.add_force_angular(1); - physics_system.update(); + system.update(); EXPECT_EQ(transform.position.x, 1); EXPECT_EQ(transform.position.y, 1); EXPECT_EQ(transform.rotation, 1); + rigidbody.data.constraints = {1, 1, 1}; EXPECT_EQ(transform.position.x, 1); EXPECT_EQ(transform.position.y, 1); EXPECT_EQ(transform.rotation, 1); + rigidbody.data.linear_damping.x = 0.5; rigidbody.data.linear_damping.y = 0.5; rigidbody.data.angular_damping = 0.5; - physics_system.update(); + system.update(); EXPECT_EQ(rigidbody.data.linear_velocity.x, 0.5); EXPECT_EQ(rigidbody.data.linear_velocity.y, 0.5); EXPECT_EQ(rigidbody.data.angular_velocity, 0.5); + rigidbody.data.constraints = {1, 1, 0}; rigidbody.data.angular_damping = 0; rigidbody.data.max_angular_velocity = 1000; rigidbody.data.angular_velocity = 360; - physics_system.update(); + system.update(); EXPECT_EQ(transform.rotation, 1); + rigidbody.data.angular_velocity = -360; - physics_system.update(); + system.update(); EXPECT_EQ(transform.rotation, 1); } diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp new file mode 100644 index 0000000..19fef6d --- /dev/null +++ b/src/test/ScriptTest.cpp @@ -0,0 +1,72 @@ +#include <gtest/gtest.h> + +// stupid hack to allow access to private/protected members under test +#define private public +#define protected public + +#include <crepe/ComponentManager.h> +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Script.h> +#include <crepe/api/Vector2.h> +#include <crepe/system/ScriptSystem.h> + +using namespace std; +using namespace crepe; +using namespace testing; + +class ScriptTest : public Test { +public: + ComponentManager component_manager{}; + ScriptSystem system{component_manager}; + + class MyScript : public Script { + // NOTE: default (private) visibility of init and update shouldn't cause + // issues! + void init() { this->init_count++; } + void update() { this->update_count++; } + + public: + unsigned init_count = 0; + unsigned update_count = 0; + }; + + BehaviorScript * behaviorscript_ref = nullptr; + MyScript * script_ref = nullptr; + + void SetUp() override { + auto & mgr = this->component_manager; + GameObject entity = mgr.new_object("name"); + BehaviorScript & component = entity.add_component<BehaviorScript>(); + + this->behaviorscript_ref = &component; + EXPECT_EQ(this->behaviorscript_ref->script.get(), nullptr); + component.set_script<MyScript>(); + ASSERT_NE(this->behaviorscript_ref->script.get(), nullptr); + + this->script_ref = (MyScript *) this->behaviorscript_ref->script.get(); + ASSERT_NE(this->script_ref, nullptr); + } +}; + +TEST_F(ScriptTest, Default) { + EXPECT_EQ(0, this->script_ref->init_count); + EXPECT_EQ(0, this->script_ref->update_count); +} + +TEST_F(ScriptTest, UpdateOnce) { + EXPECT_EQ(0, this->script_ref->init_count); + EXPECT_EQ(0, this->script_ref->update_count); + + this->system.update(); + EXPECT_EQ(1, this->script_ref->init_count); + EXPECT_EQ(1, this->script_ref->update_count); +} + +TEST_F(ScriptTest, ListScripts) { + size_t script_count = 0; + for (auto & _ : this->system.get_scripts()) { + script_count++; + } + ASSERT_EQ(1, script_count); +} diff --git a/src/test/audio.cpp b/src/test/audio.cpp deleted file mode 100644 index d6ff689..0000000 --- a/src/test/audio.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include <gtest/gtest.h> - -using namespace std; -using namespace std::chrono_literals; - -// using namespace crepe; - -// TODO: mock internal audio class - -TEST(audio, play) { ASSERT_TRUE(true); } diff --git a/src/test/dummy.cpp b/src/test/dummy.cpp deleted file mode 100644 index a00a9c6..0000000 --- a/src/test/dummy.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include <gtest/gtest.h> - -TEST(dummy, foo) { ASSERT_TRUE(1); } diff --git a/src/test/main.cpp b/src/test/main.cpp new file mode 100644 index 0000000..241015d --- /dev/null +++ b/src/test/main.cpp @@ -0,0 +1,15 @@ +#include <crepe/api/Config.h> + +#include <gtest/gtest.h> + +using namespace crepe; +using namespace testing; + +int main(int argc, char ** argv) { + InitGoogleTest(&argc, argv); + + auto & cfg = Config::get_instance(); + cfg.log.level = Log::Level::ERROR; + + return RUN_ALL_TESTS(); +} |