From 9eff2e24fa4cf0ffad2b47cc922a6558bc1a9fa1 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 30 Nov 2024 16:42:21 +0100 Subject: `make format` --- src/crepe/manager/ComponentManager.cpp | 2 +- src/crepe/manager/Manager.cpp | 3 +-- src/crepe/manager/Manager.h | 3 +-- src/crepe/manager/Mediator.h | 4 ++-- src/crepe/manager/ResourceManager.cpp | 8 ++------ src/crepe/manager/ResourceManager.hpp | 13 +++++++------ src/crepe/manager/SaveManager.cpp | 4 ++-- 7 files changed, 16 insertions(+), 21 deletions(-) (limited to 'src/crepe/manager') diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp index 5a96158..80cf8b4 100644 --- a/src/crepe/manager/ComponentManager.cpp +++ b/src/crepe/manager/ComponentManager.cpp @@ -1,6 +1,6 @@ #include "../api/GameObject.h" -#include "../util/Log.h" #include "../types.h" +#include "../util/Log.h" #include "ComponentManager.h" diff --git a/src/crepe/manager/Manager.cpp b/src/crepe/manager/Manager.cpp index fe7c936..1182785 100644 --- a/src/crepe/manager/Manager.cpp +++ b/src/crepe/manager/Manager.cpp @@ -2,5 +2,4 @@ using namespace crepe; -Manager::Manager(Mediator & mediator) : mediator(mediator) { } - +Manager::Manager(Mediator & mediator) : mediator(mediator) {} diff --git a/src/crepe/manager/Manager.h b/src/crepe/manager/Manager.h index 9adfd0b..4f21ef4 100644 --- a/src/crepe/manager/Manager.h +++ b/src/crepe/manager/Manager.h @@ -13,5 +13,4 @@ protected: Mediator & mediator; }; -} - +} // namespace crepe diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index 475aed9..e9c10b1 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -3,8 +3,8 @@ #include "../util/OptionalRef.h" // TODO: remove these singletons: -#include "SaveManager.h" #include "EventManager.h" +#include "SaveManager.h" namespace crepe { @@ -32,4 +32,4 @@ struct Mediator { OptionalRef resource_manager; }; -} +} // namespace crepe diff --git a/src/crepe/manager/ResourceManager.cpp b/src/crepe/manager/ResourceManager.cpp index 87585ad..7c01808 100644 --- a/src/crepe/manager/ResourceManager.cpp +++ b/src/crepe/manager/ResourceManager.cpp @@ -18,17 +18,13 @@ void ResourceManager::clear() { }); } -void ResourceManager::clear_all() { - this->resources.clear(); -} +void ResourceManager::clear_all() { this->resources.clear(); } void ResourceManager::set_persistent(const Asset & asset, bool persistent) { this->get_entry(asset).persistent = persistent; } ResourceManager::CacheEntry & ResourceManager::get_entry(const Asset & asset) { - if (!this->resources.contains(asset)) - this->resources[asset] = {}; + if (!this->resources.contains(asset)) this->resources[asset] = {}; return this->resources.at(asset); } - diff --git a/src/crepe/manager/ResourceManager.hpp b/src/crepe/manager/ResourceManager.hpp index 8270bc5..5167d71 100644 --- a/src/crepe/manager/ResourceManager.hpp +++ b/src/crepe/manager/ResourceManager.hpp @@ -9,18 +9,19 @@ namespace crepe { template T & ResourceManager::get(const Asset & asset) { using namespace std; - static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); + static_assert(is_base_of::value, + "cache must recieve a derivative class of Resource"); CacheEntry & entry = this->get_entry(asset); - if (entry.resource == nullptr) - entry.resource = make_unique(asset); + if (entry.resource == nullptr) entry.resource = make_unique(asset); T * concrete_resource = dynamic_cast(entry.resource.get()); if (concrete_resource == nullptr) - throw runtime_error(format("ResourceManager: mismatch between requested type and actual type of resource ({})", asset.get_path())); + throw runtime_error(format("ResourceManager: mismatch between requested type and " + "actual type of resource ({})", + asset.get_path())); return *concrete_resource; } -} - +} // namespace crepe diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp index 121d017..d4ed1c1 100644 --- a/src/crepe/manager/SaveManager.cpp +++ b/src/crepe/manager/SaveManager.cpp @@ -1,7 +1,7 @@ +#include "../ValueBroker.h" +#include "../api/Config.h" #include "../facade/DB.h" #include "../util/Log.h" -#include "../api/Config.h" -#include "../ValueBroker.h" #include "SaveManager.h" -- cgit v1.2.3 From b8194e02679dc88f5c0a240da83a4700ec5200cf Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 30 Nov 2024 17:27:16 +0100 Subject: add doxygen comments + clean up --- src/crepe/Resource.h | 18 +++++------ src/crepe/api/AudioSource.h | 9 ++++-- src/crepe/facade/Sound.cpp | 34 --------------------- src/crepe/facade/Sound.h | 9 ++++-- src/crepe/facade/SoundContext.cpp | 7 ++--- src/crepe/facade/SoundContext.h | 48 +++++++++++++++++++++++++---- src/crepe/manager/ResourceManager.h | 44 ++++++++++++++++++++++----- src/crepe/system/AudioSystem.cpp | 4 +-- src/crepe/system/AudioSystem.h | 29 ++++++++++++++++-- src/crepe/util/Private.h | 60 +++++++++++++++++++++++++++++++++++-- src/crepe/util/Private.hpp | 2 +- src/test/AudioTest.cpp | 16 +++++----- 12 files changed, 197 insertions(+), 83 deletions(-) (limited to 'src/crepe/manager') diff --git a/src/crepe/Resource.h b/src/crepe/Resource.h index a0c8859..a2d65df 100644 --- a/src/crepe/Resource.h +++ b/src/crepe/Resource.h @@ -6,21 +6,19 @@ class ResourceManager; class Asset; /** - * Resource is an interface class used to represent a (deserialized) game - * resource (e.g. textures, sounds). + * \brief Resource interface + * + * Resource is an interface class used to represent a (deserialized) game resource (e.g. + * textures, sounds). Resources are always created from \ref Asset "assets" by ResourceManager. + * + * The game programmer has the ability to use the ResourceManager to keep instances of concrete + * resources between scenes, preventing them from being reinstantiated during a scene + * transition. */ class Resource { public: Resource(const Asset & src); virtual ~Resource() = default; - -private: - /** - * The resource manager uses \c clone to create new instances of the concrete - * resource class. This may be used to inherit references to classes that - * would otherwise need to be implemented as singletons. - */ - friend class ResourceManager; }; } // namespace crepe diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 63b4bc4..330e8e1 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -17,16 +17,19 @@ class AudioSource : public Component { friend class AudioSystem; protected: + /** + * \param source Sound sample to load + */ AudioSource(game_object_id_t id, const Asset & source); - //! Only ComponentManager can create components + //! Only ComponentManager creates components friend class ComponentManager; public: - // But std::unique_ptr needs to be able to destoy this component again + // std::unique_ptr needs to be able to destoy this component virtual ~AudioSource() = default; public: - //! Start or resume this audio source + //! Start this audio source void play(bool looping = false); //! Stop this audio source void stop(); diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 33a0c47..ad50637 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -2,7 +2,6 @@ #include "../util/Log.h" #include "Sound.h" -#include "SoundContext.h" using namespace crepe; using namespace std; @@ -12,36 +11,3 @@ Sound::Sound(const Asset & src) : Resource(src) { dbg_trace(); } Sound::~Sound() { dbg_trace(); } - -// void Sound::play(SoundContext & ctx) { -// if (ctx.engine.getPause(this->handle)) { -// // resume if paused -// ctx.engine.setPause(this->handle, false); -// } else { -// // or start new sound -// this->handle = ctx.engine.play(this->sample, this->volume); -// ctx.engine.setLooping(this->handle, this->looping); -// } -// } -// -// void Sound::pause(SoundContext & ctx) { -// if (ctx.engine.getPause(this->handle)) return; -// ctx.engine.setPause(this->handle, true); -// } -// -// void Sound::rewind(SoundContext & ctx) { -// if (!ctx.engine.isValidVoiceHandle(this->handle)) return; -// ctx.engine.seek(this->handle, 0); -// } -// -// void Sound::set_volume(SoundContext & ctx, float volume) { -// this->volume = volume; -// if (!ctx.engine.isValidVoiceHandle(this->handle)) return; -// ctx.engine.setVolume(this->handle, this->volume); -// } -// -// void Sound::set_looping(SoundContext & ctx, bool looping) { -// this->looping = looping; -// if (!ctx.engine.isValidVoiceHandle(this->handle)) return; -// ctx.engine.setLooping(this->handle, this->looping); -// } diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h index 35bccdb..a78a2a7 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -12,21 +12,24 @@ class SoundContext; /** * \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. + * 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 Resource { public: Sound(const Asset & src); ~Sound(); // dbg_trace + //! Voice handle wrapper struct Handle { + //! Voice handle (soloud), used by SoundContext SoLoud::handle handle; }; private: + //! Deserialized resource (soloud) SoLoud::Wav sample; - + //! SoundContext uses \c sample friend class SoundContext; }; diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp index 470b3cc..8bd7e74 100644 --- a/src/crepe/facade/SoundContext.cpp +++ b/src/crepe/facade/SoundContext.cpp @@ -17,17 +17,16 @@ SoundContext::~SoundContext() { Sound::Handle SoundContext::play(Sound & resource) { return { - .handle = this->engine.play(resource.sample, this->default_volume), + .handle = this->engine.play(resource.sample, 1.0f), }; } void SoundContext::stop(Sound::Handle & handle) { this->engine.stop(handle.handle); } -void SoundContext::set_volume(Sound & resource, Sound::Handle & handle, float volume) { +void SoundContext::set_volume(Sound::Handle & handle, float volume) { this->engine.setVolume(handle.handle, volume); - this->default_volume = volume; } -void SoundContext::set_loop(Sound & resource, Sound::Handle & handle, bool loop) { +void SoundContext::set_loop(Sound::Handle & handle, bool loop) { this->engine.setLooping(handle.handle, loop); } diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h index c651cd5..3bc8be5 100644 --- a/src/crepe/facade/SoundContext.h +++ b/src/crepe/facade/SoundContext.h @@ -11,8 +11,8 @@ 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. + * 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 { public: @@ -24,15 +24,51 @@ public: SoundContext & operator=(const SoundContext &) = delete; SoundContext & operator=(SoundContext &&) = delete; + /** + * \brief Play a sample + * + * Plays a Sound from the beginning of the sample and returns a handle to control it later. + * + * \param resource Sound instance to play + * + * \returns Handle to control this voice + */ virtual Sound::Handle play(Sound & resource); - virtual void stop(Sound::Handle &); - virtual void set_volume(Sound &, Sound::Handle &, float); - virtual void set_loop(Sound &, Sound::Handle &, bool); + /** + * \brief Stop a voice immediately if it is still playing + * + * \note This function does nothing if the handle is invalid or if the sound is already + * stopped / finished playing. + * + * \param handle Voice handle returned by SoundContext::play + */ + virtual void stop(Sound::Handle & handle); + /** + * \brief Change the volume of a voice + * + * \note This function does nothing if the handle is invalid or if the sound is already + * stopped / finished playing. + * + * \param handle Voice handle returned by SoundContext::play + * \param volume New gain value (0=silent, 1=default) + */ + virtual void set_volume(Sound::Handle & handle, float volume); + /** + * \brief Set the looping behavior of a voice + * + * \note This function does nothing if the handle is invalid or if the sound is already + * stopped / finished playing. + * + * \param handle Voice handle returned by SoundContext::play + * \param loop Looping behavior (false=oneshot, true=loop) + */ + virtual void set_loop(Sound::Handle & handle, bool loop); private: + //! Abstracted class SoLoud::Soloud engine; - float default_volume = 1.0f; + //! Config reference Config & config = Config::get_instance(); }; diff --git a/src/crepe/manager/ResourceManager.h b/src/crepe/manager/ResourceManager.h index e7e6abc..84b275d 100644 --- a/src/crepe/manager/ResourceManager.h +++ b/src/crepe/manager/ResourceManager.h @@ -11,13 +11,11 @@ namespace crepe { /** - * \brief The ResourceManager is responsible for storing and managing assets over - * multiple scenes. + * \brief Owner of concrete Resource instances * - * The ResourceManager 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 ResourceManager is - * destroyed, at which point the cached assets are cleared. + * ResourceManager caches concrete Resource instances per Asset. Concrete resources are + * destroyed at the end of scenes by default, unless the game programmer marks them as + * persistent. */ class ResourceManager : public Manager { public: @@ -25,21 +23,53 @@ public: virtual ~ResourceManager(); // dbg_trace private: + //! Cache entry struct CacheEntry { + //! Concrete resource instance std::unique_ptr resource = nullptr; + //! Prevent ResourceManager::clear from removing this entry bool persistent = false; }; - //! A cache that holds all the assets, accessible by their file path, over multiple scenes. + //! Internal cache std::unordered_map resources; + /** + * \brief Ensure a cache entry exists for this asset and return a mutable reference to it + * + * \param asset Asset the concrete resource is instantiated from + * + * \returns Mutable reference to cache entry + */ CacheEntry & get_entry(const Asset & asset); public: + /** + * \brief Mark a resource as persistent (i.e. used across multiple scenes) + * + * \param asset Asset the concrete resource is instantiated from + * \param persistent Whether this resource is persistent (true=keep, false=destroy) + */ void set_persistent(const Asset & asset, bool persistent); + /** + * \brief Retrieve reference to concrete Resource by Asset + * + * \param asset Asset the concrete resource is instantiated from + * \tparam Resource Concrete derivative of Resource + * + * This class instantiates the concrete resource if it is not yet stored in the internal + * cache, or returns a reference to the cached resource if it already exists. + * + * \returns Reference to concrete resource + * + * \throws std::runtime_error if the \c Resource parameter does not match with the actual + * type of the resource stored in the cache for this Asset + */ template Resource & get(const Asset & asset); + //! Clear non-persistent resources from cache void clear(); + //! Clear all resources from cache regardless of persistence void clear_all(); }; diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index 0696b34..26913c0 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -52,10 +52,10 @@ void AudioSystem::diff_update(AudioSource & component, ComponentPrivate & data, component.oneshot_stop = false; } if (component.volume != data.last_volume) { - context.set_volume(resource, data.handle, component.volume); + context.set_volume(data.handle, component.volume); } if (component.loop != data.last_loop) { - context.set_loop(resource, data.handle, component.loop); + context.set_loop(data.handle, component.loop); } } diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index c941470..4d21883 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -14,9 +14,7 @@ public: void update() override; private: - /** - * \brief Private data stored by AudioSystem on AudioSource component - */ + //! Private data stored by AudioSystem on AudioSource component struct ComponentPrivate { //! This sample's voice handle Sound::Handle handle; @@ -31,14 +29,39 @@ private: //! \} }; + /** + * \brief Update `last_*` members of \c data + * + * Copies all component properties stored for comparison between AudioSystem::update() calls + * + * \param component Source properties + * \param data Destination properties + */ void update_last(const AudioSource & component, ComponentPrivate & data); + /** + * \brief Compare update component + * + * Compares properties of \c component and \c data, and calls SoundContext functions where + * applicable. + * + * \param component AudioSource component to update + * \param data AudioSource's private data + * \param resource Sound instance for AudioSource's Asset + */ void diff_update(AudioSource & component, ComponentPrivate & data, Sound & resource); protected: + /** + * \brief Get SoundContext + * + * SoundContext is retrieved through this function instead of being a direct member of + * AudioSystem to aid with testability. + */ virtual SoundContext & get_context(); private: + //! Actually stores SoundContext if the base AudioSystem::get_context implementation is used Private context; }; diff --git a/src/crepe/util/Private.h b/src/crepe/util/Private.h index 62a2e1a..d725a5e 100644 --- a/src/crepe/util/Private.h +++ b/src/crepe/util/Private.h @@ -5,26 +5,82 @@ namespace crepe { +/** + * \brief Utility for storing type hidden from user + * + * This class can be used to store types which cannot be used in the API directly due to header + * distribution limitations. This class is similar to `std::any`, but provides a method for + * retrieving a mutable reference to the stored object. + */ class Private { public: Private() = default; ~Private(); + /** + * \name Copy + * + * \note These functions do not do anything, resulting in `*this` being an empty (default) + * instance. + * + * \{ + */ Private(const Private &); - Private(Private &&); Private & operator=(const Private &); + //! \} + /** + * \name Move + * + * These functions actually move the stored type if present. + * + * \{ + */ + Private(Private &&); Private & operator=(Private &&); + //! \} + /** + * \brief Get the stored object + * + * \tparam T Type of stored object + * + * \returns Mutable reference to stored object + * + * \throws std::out_of_range if this instance does not contain any object + * \throws std::logic_error if the stored type and requested type differ + */ template - T & get(); + T & get() const; + /** + * \brief Create and store an arbitrary object + * + * \tparam T Type of object + * \tparam Args Perfect forwarding arguments + * \param args Perfect forwarding arguments + * + * All arguments to this function are forwarded using `std::forward` to the constructor of T. + * + * \returns Mutable reference to stored object + * + * \note If this instance already contained an object, this function implicitly destroys the + * previous object. + */ template T & set(Args &&... args); + /** + * \brief Check if this instance contains an object + * + * \returns `true` if this instance is empty, `false` if it contains an object + */ bool empty() const noexcept; private: + //! Wrapper for destructor call of stored object type std::function destructor; + //! Stored object's type std::type_index type = typeid(void); + //! Stored object void * instance = nullptr; }; diff --git a/src/crepe/util/Private.hpp b/src/crepe/util/Private.hpp index 3a87a9f..b2174c0 100644 --- a/src/crepe/util/Private.hpp +++ b/src/crepe/util/Private.hpp @@ -18,7 +18,7 @@ T & Private::set(Args &&... args) { } template -T & Private::get() { +T & Private::get() const { using namespace std; if (this->empty()) throw out_of_range("Private: get() called on empty object"); type_index requested_type = typeid(T); diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index 14f57bd..7644ab7 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -18,8 +18,8 @@ private: public: MOCK_METHOD(Sound::Handle, play, (Sound & resource), (override)); MOCK_METHOD(void, stop, (Sound::Handle &), (override)); - MOCK_METHOD(void, set_volume, (Sound &, Sound::Handle &, float), (override)); - MOCK_METHOD(void, set_loop, (Sound &, Sound::Handle &, bool), (override)); + MOCK_METHOD(void, set_volume, (Sound::Handle &, float), (override)); + MOCK_METHOD(void, set_loop, (Sound::Handle &, bool), (override)); }; class TestAudioSystem : public AudioSystem { @@ -48,8 +48,8 @@ public: TEST_F(AudioTest, Default) { EXPECT_CALL(context, play(_)).Times(0); EXPECT_CALL(context, stop(_)).Times(0); - EXPECT_CALL(context, set_volume(_, _, _)).Times(0); - EXPECT_CALL(context, set_loop(_, _, _)).Times(0); + EXPECT_CALL(context, set_volume(_, _)).Times(0); + EXPECT_CALL(context, set_loop(_, _)).Times(0); system.update(); } @@ -95,14 +95,14 @@ TEST_F(AudioTest, Volume) { { InSequence seq; - EXPECT_CALL(context, set_volume(_, _, _)).Times(0); + EXPECT_CALL(context, set_volume(_, _)).Times(0); component.volume += 0.2; } { InSequence seq; - EXPECT_CALL(context, set_volume(_, _, component.volume)).Times(1); + EXPECT_CALL(context, set_volume(_, component.volume)).Times(1); system.update(); } } @@ -113,14 +113,14 @@ TEST_F(AudioTest, Looping) { { InSequence seq; - EXPECT_CALL(context, set_loop(_, _, _)).Times(0); + EXPECT_CALL(context, set_loop(_, _)).Times(0); component.loop = !component.loop; } { InSequence seq; - EXPECT_CALL(context, set_loop(_, _, component.loop)).Times(1); + EXPECT_CALL(context, set_loop(_, component.loop)).Times(1); system.update(); } } -- cgit v1.2.3 From f4824f5e7e6cee12bec602f3240770945a73d043 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Fri, 6 Dec 2024 17:13:59 +0100 Subject: add more fetching functions to ComponentManager --- src/crepe/api/Script.h | 7 +++++ src/crepe/manager/ComponentManager.cpp | 14 ++++++++++ src/crepe/manager/ComponentManager.h | 18 ++++++++++-- src/crepe/manager/ComponentManager.hpp | 51 ++++++++++++++++++++-------------- src/test/ECSTest.cpp | 34 +++++++++++++++++++++++ 5 files changed, 101 insertions(+), 23 deletions(-) (limited to 'src/crepe/manager') diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 1b339b0..5862bae 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -84,6 +84,13 @@ protected: template RefVector get_components() const; + /** + * \copydoc ComponentManager::get_components_by_id + * \see ComponentManager::get_components_by_id + */ + template + RefVector get_components_by_id(game_object_id_t id) const; + /** * \brief Log a message using Log::logf * diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp index 80cf8b4..44774d9 100644 --- a/src/crepe/manager/ComponentManager.cpp +++ b/src/crepe/manager/ComponentManager.cpp @@ -1,4 +1,5 @@ #include "../api/GameObject.h" +#include "../api/Metadata.h" #include "../types.h" #include "../util/Log.h" @@ -61,3 +62,16 @@ GameObject ComponentManager::new_object(const string & name, const string & tag, void ComponentManager::set_persistent(game_object_id_t id, bool persistent) { this->persistent[id] = persistent; } + +set ComponentManager::get_objects_by_name(const string & name) const { + return this->get_objects_by_predicate([name](const Metadata & data) { + return data.name == name; + }); +} + +set ComponentManager::get_objects_by_tag(const string & tag) const { + return this->get_objects_by_predicate([tag](const Metadata & data) { + return data.tag == tag; + }); +} + diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index ad37586..5f2cf3c 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "../Component.h" #include "../types.h" @@ -136,6 +137,17 @@ public: RefVector get_components_by_type() const; private: + template + std::set get_objects_by_predicate(const std::function & pred) const; + + std::set get_objects_by_name(const std::string & name) const; + std::set get_objects_by_tag(const std::string & tag) const; + +private: + template + using by_type = std::unordered_map; + template + using by_id_index = std::vector; /** * \brief The components * @@ -146,8 +158,7 @@ private: * 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>>> - components; + by_type>>> components; //! Persistent flag for each GameObject std::unordered_map persistent; @@ -156,6 +167,9 @@ private: game_object_id_t next_id = 0; }; +// template <> +// RefVector ComponentManager::get_components_by_id(game_object_id_t id) const; + } // namespace crepe #include "ComponentManager.hpp" diff --git a/src/crepe/manager/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp index ffb38ec..25c2747 100644 --- a/src/crepe/manager/ComponentManager.hpp +++ b/src/crepe/manager/ComponentManager.hpp @@ -95,32 +95,24 @@ template RefVector ComponentManager::get_components_by_id(game_object_id_t id) const { using namespace std; - // Determine the type of T (this is used as the key of the unordered_map<>) - type_index type = typeid(T); - - // Create an empty vector<> - RefVector component_vector; - - if (this->components.find(type) == this->components.end()) return component_vector; - - // Get the correct vector<> - const vector>> & 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; + static_assert(is_base_of::value, + "get_components_by_id must recieve a derivative class of Component"); - // Loop trough the whole vector<> - for (const unique_ptr & component_ptr : component_array[id]) { - // Cast the unique_ptr to a raw pointer - T * casted_component = static_cast(component_ptr.get()); + type_index type = typeid(T); + if (!this->components.contains(type)) return {}; - if (casted_component == nullptr) continue; + const by_id_index>> & components_by_id = this->components.at(type); + if (id >= components_by_id.size()) return {}; - // Add the dereferenced raw pointer to the vector<> - component_vector.push_back(*casted_component); + RefVector out = {}; + const vector> & components = components_by_id.at(id); + for (auto & component_ptr : components) { + if (component_ptr == nullptr) continue; + Component & component = *component_ptr.get(); + out.push_back(static_cast(component)); } - return component_vector; + return out; } template @@ -158,4 +150,21 @@ RefVector ComponentManager::get_components_by_type() const { return component_vector; } +template +std::set ComponentManager::get_objects_by_predicate(const std::function & pred) const { + using namespace std; + + set objects = {}; + RefVector components = this->get_components_by_type(); + + for (const T & component : components) { + game_object_id_t id = dynamic_cast(component).game_object_id; + if (objects.contains(id)) continue; + if (!pred(component)) continue; + objects.insert(id); + } + + return objects; +} + } // namespace crepe diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index 3e6c61c..a169b3b 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -1,6 +1,7 @@ #include #define protected public +#define private public #include #include @@ -387,3 +388,36 @@ TEST_F(ECSTest, resetPersistent) { EXPECT_EQ(metadata.size(), 0); EXPECT_EQ(transform.size(), 0); } + +TEST_F(ECSTest, GetByName) { + GameObject foo = mgr.new_object("foo"); + GameObject bar = mgr.new_object("bar"); + + { + auto objects = mgr.get_objects_by_name(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_objects_by_name("foo"); + EXPECT_EQ(objects.size(), 1); + EXPECT_TRUE(objects.contains(foo.id)); + } +} + +TEST_F(ECSTest, GetByTag) { + GameObject foo = mgr.new_object("foo", "common tag"); + GameObject bar = mgr.new_object("bar", "common tag"); + + { + auto objects = mgr.get_objects_by_tag(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_objects_by_tag("common tag"); + EXPECT_EQ(objects.size(), 2); + EXPECT_TRUE(objects.contains(foo.id)); + EXPECT_TRUE(objects.contains(bar.id)); + } +} -- cgit v1.2.3 From 90c6bf03e59fdec64f850310bcbff45ae86f69e3 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 7 Dec 2024 14:18:54 +0100 Subject: more script utilities --- src/crepe/api/Script.h | 12 ++++++++ src/crepe/api/Script.hpp | 27 +++++++++++++++--- src/crepe/manager/ComponentManager.h | 20 ++++++++++++++ src/crepe/manager/ComponentManager.hpp | 23 ++++++++++++++++ src/test/CMakeLists.txt | 1 + src/test/ECSTest.cpp | 50 ++++++++++++++++++++++++++++++++-- src/test/ScriptECSTest.cpp | 42 ++++++++++++++++++++++++++++ src/test/ScriptEventTest.cpp | 2 +- src/test/ScriptSceneTest.cpp | 3 +- src/test/ScriptTest.cpp | 3 +- src/test/ScriptTest.h | 2 ++ 11 files changed, 175 insertions(+), 10 deletions(-) create mode 100644 src/test/ScriptECSTest.cpp (limited to 'src/crepe/manager') diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 5862bae..a040608 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -90,6 +90,18 @@ protected: */ template RefVector get_components_by_id(game_object_id_t id) const; + /** + * \copydoc ComponentManager::get_components_by_name + * \see ComponentManager::get_components_by_name + */ + template + RefVector get_components_by_name(const std::string & name) const; + /** + * \copydoc ComponentManager::get_components_by_tag + * \see ComponentManager::get_components_by_tag + */ + template + RefVector get_components_by_tag(const std::string & tag) const; /** * \brief Log a message using Log::logf diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index 45f1ff1..16e0dc5 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -20,10 +20,7 @@ T & Script::get_component() const { template RefVector Script::get_components() const { - Mediator & mediator = this->mediator; - ComponentManager & mgr = mediator.component_manager; - - return mgr.get_components_by_id(this->game_object_id); + return this->get_components_by_id(this->game_object_id); } template @@ -56,4 +53,26 @@ void Script::subscribe(const EventHandler & callback) { this->subscribe_internal(callback, EventManager::CHANNEL_ALL); } +template +RefVector Script::get_components_by_id(game_object_id_t id) const { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + + return mgr.get_components_by_id(id); +} +template +RefVector Script::get_components_by_name(const std::string & name) const { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + + return mgr.get_components_by_name(name); +} +template +RefVector Script::get_components_by_tag(const std::string & tag) const { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + + return mgr.get_components_by_tag(tag); +} + } // namespace crepe diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index 5f2cf3c..4e53954 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -135,10 +135,30 @@ public: */ template RefVector get_components_by_type() const; + /** + * \brief Get all components of a specific type on a GameObject with name \c name + * + * \tparam T The type of the component + * \param name Metadata::name for the same game_object_id as the returned components + * \return Components matching criteria + */ + template + RefVector get_components_by_name(const std::string & name) const; + /** + * \brief Get all components of a specific type on a GameObject with tag \c tag + * + * \tparam T The type of the component + * \param name Metadata::tag for the same game_object_id as the returned components + * \return Components matching criteria + */ + template + RefVector get_components_by_tag(const std::string & tag) const; private: template std::set get_objects_by_predicate(const std::function & pred) const; + template + RefVector get_components_by_ids(const std::set & ids) const; std::set get_objects_by_name(const std::string & name) const; std::set get_objects_by_tag(const std::string & tag) const; diff --git a/src/crepe/manager/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp index 25c2747..52df368 100644 --- a/src/crepe/manager/ComponentManager.hpp +++ b/src/crepe/manager/ComponentManager.hpp @@ -167,4 +167,27 @@ std::set ComponentManager::get_objects_by_predicate(const std: return objects; } +template +RefVector ComponentManager::get_components_by_ids(const std::set & ids) const { + using namespace std; + + RefVector out = {}; + for (game_object_id_t id : ids) { + RefVector components = get_components_by_id(id); + out.insert(out.end(), components.begin(), components.end()); + } + + return out; +} + +template +RefVector ComponentManager::get_components_by_name(const std::string & name) const { + return this->get_components_by_ids(this->get_objects_by_name(name)); +} + +template +RefVector ComponentManager::get_components_by_tag(const std::string & tag) const { + return this->get_components_by_ids(this->get_objects_by_tag(tag)); +} + } // namespace crepe diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index e19d7de..43f564a 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -15,4 +15,5 @@ target_sources(test_main PUBLIC InputTest.cpp ScriptEventTest.cpp ScriptSceneTest.cpp + ScriptECSTest.cpp ) diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index a169b3b..ed5341e 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -17,6 +17,10 @@ class ECSTest : public ::testing::Test { public: ComponentManager mgr{m}; + + class TestComponent : public Component { + using Component::Component; + }; }; TEST_F(ECSTest, createGameObject) { @@ -389,7 +393,7 @@ TEST_F(ECSTest, resetPersistent) { EXPECT_EQ(transform.size(), 0); } -TEST_F(ECSTest, GetByName) { +TEST_F(ECSTest, IDByName) { GameObject foo = mgr.new_object("foo"); GameObject bar = mgr.new_object("bar"); @@ -405,7 +409,7 @@ TEST_F(ECSTest, GetByName) { } } -TEST_F(ECSTest, GetByTag) { +TEST_F(ECSTest, IDByTag) { GameObject foo = mgr.new_object("foo", "common tag"); GameObject bar = mgr.new_object("bar", "common tag"); @@ -421,3 +425,45 @@ TEST_F(ECSTest, GetByTag) { EXPECT_TRUE(objects.contains(bar.id)); } } + +TEST_F(ECSTest, ComponentsByName) { + GameObject foo = mgr.new_object("foo"); + foo.add_component(); + GameObject bar = mgr.new_object("bar"); + bar.add_component(); + bar.add_component(); + + { + auto objects = mgr.get_components_by_name(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_components_by_name("foo"); + EXPECT_EQ(objects.size(), 1); + } + + { + auto objects = mgr.get_components_by_name("bar"); + EXPECT_EQ(objects.size(), 2); + } +} + +TEST_F(ECSTest, ComponentsByTag) { + GameObject foo = mgr.new_object("foo", "common tag"); + foo.add_component(); + GameObject bar = mgr.new_object("bar", "common tag"); + bar.add_component(); + bar.add_component(); + + { + auto objects = mgr.get_components_by_tag(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_components_by_tag("common tag"); + EXPECT_EQ(objects.size(), 3); + } +} + diff --git a/src/test/ScriptECSTest.cpp b/src/test/ScriptECSTest.cpp new file mode 100644 index 0000000..4477e55 --- /dev/null +++ b/src/test/ScriptECSTest.cpp @@ -0,0 +1,42 @@ +#include + +#define protected public + +#include +#include +#include +#include +#include +#include + +#include "ScriptTest.h" + +using namespace std; +using namespace crepe; +using namespace testing; + +class ScriptECSTest : public ScriptTest { +public: + class TestComponent : public Component { + using Component::Component; + }; +}; + +TEST_F(ScriptECSTest, GetOwnComponent) { + MyScript & script = this->script; + Metadata & metadata = script.get_component(); + + EXPECT_EQ(metadata.name, OBJ_NAME); +} + +TEST_F(ScriptECSTest, GetOwnComponents) { + const unsigned COUNT = 4; + + for (unsigned i = 0; i < COUNT; i++) + entity.add_component(); + + MyScript & script = this->script; + RefVector components = script.get_components(); + + EXPECT_EQ(components.size(), COUNT); +} diff --git a/src/test/ScriptEventTest.cpp b/src/test/ScriptEventTest.cpp index 5da31e7..c1b4028 100644 --- a/src/test/ScriptEventTest.cpp +++ b/src/test/ScriptEventTest.cpp @@ -26,7 +26,7 @@ public: class MyEvent : public Event {}; }; -TEST_F(ScriptEventTest, Inactive) { +TEST_F(ScriptEventTest, Default) { BehaviorScript & behaviorscript = this->behaviorscript; MyScript & script = this->script; EventManager & evmgr = this->event_manager; diff --git a/src/test/ScriptSceneTest.cpp b/src/test/ScriptSceneTest.cpp index 9ee1e52..8e849c1 100644 --- a/src/test/ScriptSceneTest.cpp +++ b/src/test/ScriptSceneTest.cpp @@ -18,7 +18,7 @@ public: class MyScene : public Scene {}; }; -TEST_F(ScriptSceneTest, Inactive) { +TEST_F(ScriptSceneTest, Default) { BehaviorScript & behaviorscript = this->behaviorscript; MyScript & script = this->script; @@ -28,3 +28,4 @@ TEST_F(ScriptSceneTest, Inactive) { script.set_next_scene(non_default_value); EXPECT_EQ(non_default_value, scene_manager.next_scene); } + diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp index 1d2d6dd..b0b2546 100644 --- a/src/test/ScriptTest.cpp +++ b/src/test/ScriptTest.cpp @@ -6,7 +6,6 @@ #define protected public #include "ScriptTest.h" -#include using namespace std; using namespace crepe; @@ -14,7 +13,6 @@ using namespace testing; void ScriptTest::SetUp() { auto & mgr = this->component_manager; - GameObject entity = mgr.new_object("name"); BehaviorScript & component = entity.add_component(); this->behaviorscript = component; @@ -75,3 +73,4 @@ TEST_F(ScriptTest, UpdateInactive) { system.update(); } } + diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h index 1bbfdd3..309e016 100644 --- a/src/test/ScriptTest.h +++ b/src/test/ScriptTest.h @@ -11,10 +11,12 @@ class ScriptTest : public testing::Test { protected: crepe::Mediator mediator; + static constexpr const char * OBJ_NAME = "foo"; public: crepe::ComponentManager component_manager{mediator}; crepe::ScriptSystem system{mediator}; + crepe::GameObject entity = component_manager.new_object(OBJ_NAME); class MyScript : public crepe::Script { // NOTE: explicitly stating `public:` is not required on actual scripts -- cgit v1.2.3 From a73ff31b67faa7e6a922cfb5598f56f80bc01d62 Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Sat, 7 Dec 2024 14:19:16 +0100 Subject: added loopTimer and eventManager to mediator and removed the singletons --- src/crepe/api/CMakeLists.txt | 6 -- src/crepe/api/IKeyListener.cpp | 19 ----- src/crepe/api/IKeyListener.h | 50 ------------- src/crepe/api/IMouseListener.cpp | 29 ------- src/crepe/api/IMouseListener.h | 73 ------------------ src/crepe/api/LoopManager.cpp | 18 ++--- src/crepe/api/LoopManager.h | 9 ++- src/crepe/api/LoopTimer.cpp | 75 ------------------- src/crepe/api/LoopTimer.h | 133 --------------------------------- src/crepe/manager/CMakeLists.txt | 2 + src/crepe/manager/EventManager.cpp | 6 +- src/crepe/manager/EventManager.h | 24 ++---- src/crepe/manager/LoopTimerManager.cpp | 77 +++++++++++++++++++ src/crepe/manager/LoopTimerManager.h | 133 +++++++++++++++++++++++++++++++++ src/crepe/manager/Mediator.h | 8 +- src/test/EventTest.cpp | 88 +++++++++------------- src/test/LoopManagerTest.cpp | 29 ++++--- src/test/LoopTimerTest.cpp | 17 ++--- src/test/ScriptTest.h | 4 +- 19 files changed, 303 insertions(+), 497 deletions(-) delete mode 100644 src/crepe/api/IKeyListener.cpp delete mode 100644 src/crepe/api/IKeyListener.h delete mode 100644 src/crepe/api/IMouseListener.cpp delete mode 100644 src/crepe/api/IMouseListener.h delete mode 100644 src/crepe/api/LoopTimer.cpp delete mode 100644 src/crepe/api/LoopTimer.h create mode 100644 src/crepe/manager/LoopTimerManager.cpp create mode 100644 src/crepe/manager/LoopTimerManager.h (limited to 'src/crepe/manager') diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 7da9dca..60d9dc5 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -13,10 +13,7 @@ target_sources(crepe PUBLIC Metadata.cpp Camera.cpp Animator.cpp - IKeyListener.cpp - IMouseListener.cpp LoopManager.cpp - LoopTimer.cpp Asset.cpp EventHandler.cpp Script.cpp @@ -45,9 +42,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES EventHandler.h EventHandler.hpp Event.h - IKeyListener.h - IMouseListener.h LoopManager.h - LoopTimer.h Asset.h ) diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp deleted file mode 100644 index 8642655..0000000 --- a/src/crepe/api/IKeyListener.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "IKeyListener.h" - -using namespace crepe; - -// Constructor with specified channel -IKeyListener::IKeyListener(event_channel_t channel) - : event_manager(EventManager::get_instance()) { - this->press_id = event_manager.subscribe( - [this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel); - this->release_id = event_manager.subscribe( - [this](const KeyReleaseEvent & event) { return this->on_key_released(event); }, - channel); -} - -// Destructor, unsubscribe events -IKeyListener::~IKeyListener() { - event_manager.unsubscribe(this->press_id); - event_manager.unsubscribe(this->release_id); -} diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h deleted file mode 100644 index 6ded107..0000000 --- a/src/crepe/api/IKeyListener.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "../manager/EventManager.h" - -#include "Event.h" -#include "EventHandler.h" - -namespace crepe { - -/** - * \class IKeyListener - * \brief Interface for keyboard event handling in the application. - */ -class IKeyListener { -public: - /** - * \brief Constructs an IKeyListener with a specified channel. - * \param channel The channel ID for event handling. - */ - IKeyListener(event_channel_t channel = EventManager::CHANNEL_ALL); - virtual ~IKeyListener(); - IKeyListener(const IKeyListener &) = delete; - IKeyListener & operator=(const IKeyListener &) = delete; - IKeyListener & operator=(IKeyListener &&) = delete; - IKeyListener(IKeyListener &&) = delete; - - /** - * \brief Pure virtual function to handle key press events. - * \param event The key press event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_key_pressed(const KeyPressEvent & event) = 0; - - /** - * \brief Pure virtual function to handle key release events. - * \param event The key release event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_key_released(const KeyReleaseEvent & event) = 0; - -private: - //! Key press event id - subscription_t press_id = -1; - //! Key release event id - subscription_t release_id = -1; - //! EventManager reference - EventManager & event_manager; -}; - -} // namespace crepe diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp deleted file mode 100644 index 989aeb3..0000000 --- a/src/crepe/api/IMouseListener.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "IMouseListener.h" - -using namespace crepe; - -IMouseListener::IMouseListener(event_channel_t channel) - : event_manager(EventManager::get_instance()) { - this->click_id = event_manager.subscribe( - [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); }, - channel); - - this->press_id = event_manager.subscribe( - [this](const MousePressEvent & event) { return this->on_mouse_pressed(event); }, - channel); - - this->release_id = event_manager.subscribe( - [this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); }, - channel); - - this->move_id = event_manager.subscribe( - [this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }, channel); -} - -IMouseListener::~IMouseListener() { - // Unsubscribe event handlers - event_manager.unsubscribe(this->click_id); - event_manager.unsubscribe(this->press_id); - event_manager.unsubscribe(this->release_id); - event_manager.unsubscribe(this->move_id); -} diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h deleted file mode 100644 index 9e4fdf7..0000000 --- a/src/crepe/api/IMouseListener.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "../manager/EventManager.h" - -#include "Event.h" -#include "EventHandler.h" - -namespace crepe { - -/** - * \class IMouseListener - * \brief Interface for mouse event handling in the application. - */ -class IMouseListener { -public: - /** - * \brief Constructs an IMouseListener with a specified channel. - * \param channel The channel ID for event handling. - */ - IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL); - virtual ~IMouseListener(); - IMouseListener & operator=(const IMouseListener &) = delete; - IMouseListener(const IMouseListener &) = delete; - IMouseListener & operator=(const IMouseListener &&) = delete; - IMouseListener(IMouseListener &&) = delete; - - /** - * \brief Move assignment operator (deleted). - */ - IMouseListener & operator=(IMouseListener &&) = delete; - - /** - * \brief Handles a mouse click event. - * \param event The mouse click event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_clicked(const MouseClickEvent & event) = 0; - - /** - * \brief Handles a mouse press event. - * \param event The mouse press event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_pressed(const MousePressEvent & event) = 0; - - /** - * \brief Handles a mouse release event. - * \param event The mouse release event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_released(const MouseReleaseEvent & event) = 0; - - /** - * \brief Handles a mouse move event. - * \param event The mouse move event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0; - -private: - //! Mouse click event id - subscription_t click_id = -1; - //! Mouse press event id - subscription_t press_id = -1; - //! Mouse release event id - subscription_t release_id = -1; - //! Mouse move event id - subscription_t move_id = -1; - //! EventManager reference - EventManager & event_manager; -}; - -} //namespace crepe diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index 454afe8..69cbfaf 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,3 +1,4 @@ +#include #include "../facade/SDLContext.h" #include "../manager/EventManager.h" @@ -20,10 +21,8 @@ LoopManager::LoopManager() { this->load_system(); this->load_system(); this->load_system(); - EventManager::get_instance().subscribe( + this->event_manager.subscribe( [this](const ShutDownEvent & event) { return this->on_shutdown(event); }); - this->loop_timer = make_unique(); - this->mediator.loop_timer = *loop_timer; } void LoopManager::process_input() { @@ -38,27 +37,28 @@ void LoopManager::start() { void LoopManager::fixed_update() {} void LoopManager::loop() { - this->loop_timer->start(); + while (game_running) { - this->loop_timer->update(); + this->loop_timer.update(); - while (this->loop_timer->get_lag() >= this->loop_timer->get_fixed_delta_time()) { + while (this->loop_timer.get_lag() >= this->loop_timer.get_fixed_delta_time()) { this->process_input(); + event_manager.dispatch_events(); this->fixed_update(); - this->loop_timer->advance_fixed_update(); + this->loop_timer.advance_fixed_update(); } this->update(); this->render(); - this->loop_timer->enforce_frame_rate(); + this->loop_timer.enforce_frame_rate(); } } void LoopManager::setup() { this->game_running = true; - this->loop_timer->start(); + this->loop_timer.start(); } void LoopManager::render() { diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 2161dff..00f5409 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -5,10 +5,11 @@ #include "../facade/SDLContext.h" #include "../manager/ComponentManager.h" #include "../manager/SceneManager.h" +#include "../manager/EventManager.h" +#include "../manager/LoopTimerManager.h" #include "../system/System.h" #include "api/Event.h" -#include "api/LoopTimer.h" namespace crepe { /** @@ -96,8 +97,10 @@ private: //! SDL context \todo no more singletons! SDLContext & sdl_context = SDLContext::get_instance(); - //! loop timer instance - std::unique_ptr loop_timer; + //! LoopTimer instance + LoopTimerManager loop_timer{mediator}; + //! EventManager instance + EventManager event_manager{mediator}; private: /** diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp deleted file mode 100644 index 8fb7ce8..0000000 --- a/src/crepe/api/LoopTimer.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include - -#include "../facade/SDLContext.h" -#include "../util/Log.h" - -#include "LoopTimer.h" - -using namespace crepe; - -LoopTimer::LoopTimer() { dbg_trace(); } - -void LoopTimer::start() { - this->last_frame_time = std::chrono::steady_clock::now(); - - this->elapsed_time = std::chrono::milliseconds(0); - // by starting the elapsed_fixed_time at (0 - fixed_delta_time) in milliseconds it calls a fixed update at the start of the loop. - this->elapsed_fixed_time - = -std::chrono::duration_cast(fixed_delta_time); - this->delta_time = std::chrono::milliseconds(0); -} - -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>( - current_frame_time - last_frame_time); - - if (this->delta_time > this->maximum_delta_time) { - this->delta_time = this->maximum_delta_time; - } - this->actual_fps = 1.0 / this->delta_time.count(); - - this->delta_time *= this->game_scale; - this->elapsed_time += this->delta_time; - this->last_frame_time = current_frame_time; -} - -double LoopTimer::get_delta_time() const { return this->delta_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; } - -double LoopTimer::get_fixed_delta_time() const { return this->fixed_delta_time.count(); } - -void LoopTimer::set_target_fps(int fps) { - this->target_fps = fps; - // target time per frame in seconds - this->frame_target_time = std::chrono::duration(1.0) / this->target_fps; -} - -int LoopTimer::get_fps() const { return this->actual_fps; } - -void LoopTimer::set_game_scale(double value) { this->game_scale = value; } - -double LoopTimer::get_game_scale() const { return this->game_scale; } -void LoopTimer::enforce_frame_rate() { - auto current_frame_time = std::chrono::steady_clock::now(); - auto frame_duration = current_frame_time - this->last_frame_time; - - // Check if frame duration is less than the target frame time - if (frame_duration < this->frame_target_time) { - auto delay_time = std::chrono::duration_cast( - this->frame_target_time - frame_duration); - - if (delay_time.count() > 0) { - std::this_thread::sleep_for(delay_time); - } - } -} - -double LoopTimer::get_lag() const { - return (this->elapsed_time - this->elapsed_fixed_time).count(); -} diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h deleted file mode 100644 index c4294d7..0000000 --- a/src/crepe/api/LoopTimer.h +++ /dev/null @@ -1,133 +0,0 @@ -#pragma once - -#include - -namespace crepe { - -class LoopTimer { -public: - LoopTimer(); - /** - * \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. - */ - double get_current_time() const; - - /** - * \brief Set the target frames per second (FPS). - * - * \param fps The desired frames rendered per second. - */ - void set_target_fps(int 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. - */ - 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). - */ - void set_game_scale(double game_scale); - -private: - friend class LoopManager; - - /** - * \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. - */ - 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. - */ - 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. - */ - double get_lag() const; - - /** - * \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. - */ - void advance_fixed_update(); - -private: - //! Target frames per second - int target_fps = 50; - //! Actual frames per second - int actual_fps = 0; - //! Current game scale - double game_scale = 1; - //! Maximum delta time in seconds to avoid large jumps - std::chrono::duration maximum_delta_time{0.25}; - //! Delta time for the current frame in seconds - std::chrono::duration delta_time{0.0}; - //! Target time per frame in seconds - std::chrono::duration frame_target_time - = std::chrono::duration(1.0) / target_fps; - //! Fixed delta time for fixed updates in seconds - std::chrono::duration fixed_delta_time = std::chrono::duration(1.0) / 50.0; - //! Total elapsed game time in seconds - std::chrono::duration elapsed_time{0.0}; - //! Total elapsed time for fixed updates in seconds - std::chrono::duration elapsed_fixed_time{0.0}; - //! Time of the last frame - std::chrono::steady_clock::time_point last_frame_time; -}; - -} // namespace crepe diff --git a/src/crepe/manager/CMakeLists.txt b/src/crepe/manager/CMakeLists.txt index 517b8a2..29d6df0 100644 --- a/src/crepe/manager/CMakeLists.txt +++ b/src/crepe/manager/CMakeLists.txt @@ -4,6 +4,7 @@ target_sources(crepe PUBLIC Manager.cpp SaveManager.cpp SceneManager.cpp + LoopTimerManager.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -16,5 +17,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES SaveManager.h SceneManager.h SceneManager.hpp + LoopTimerManager.h ) diff --git a/src/crepe/manager/EventManager.cpp b/src/crepe/manager/EventManager.cpp index 20f0dd3..9b0fa95 100644 --- a/src/crepe/manager/EventManager.cpp +++ b/src/crepe/manager/EventManager.cpp @@ -3,11 +3,9 @@ using namespace crepe; using namespace std; -EventManager & EventManager::get_instance() { - static EventManager instance; - return instance; +EventManager::EventManager(Mediator & mediator) : Manager(mediator){ + this->mediator.event_manager = *this; } - void EventManager::dispatch_events() { for (auto & event : this->events_queue) { this->handle_event(event.type, event.channel, *event.event.get()); diff --git a/src/crepe/manager/EventManager.h b/src/crepe/manager/EventManager.h index d634f54..5f8b107 100644 --- a/src/crepe/manager/EventManager.h +++ b/src/crepe/manager/EventManager.h @@ -8,6 +8,8 @@ #include "../api/Event.h" #include "../api/EventHandler.h" +#include "Manager.h" + namespace crepe { //! Event listener unique ID @@ -22,27 +24,16 @@ typedef size_t subscription_t; typedef size_t event_channel_t; /** - * \class EventManager * \brief Manages event subscriptions, triggers, and queues, enabling decoupled event handling. * * The `EventManager` acts as a centralized event system. It allows for registering callbacks * for specific event types, triggering events synchronously, queueing events for later * processing, and managing subscriptions via unique identifiers. */ -class EventManager { +class EventManager : public Manager { public: static constexpr const event_channel_t CHANNEL_ALL = -1; - - /** - * \brief Get the singleton instance of the EventManager. - * - * This method returns the unique instance of the EventManager, creating it if it - * doesn't already exist. Ensures only one instance is active in the program. - * - * \return Reference to the singleton instance of the EventManager. - */ - static EventManager & get_instance(); - + EventManager(Mediator & mediator); /** * \brief Subscribe to a specific event type. * @@ -107,12 +98,7 @@ public: void clear(); private: - /** - * \brief Default constructor for the EventManager. - * - * Constructor is private to enforce the singleton pattern. - */ - EventManager() = default; + /** * \struct QueueEntry diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp new file mode 100644 index 0000000..8156c6d --- /dev/null +++ b/src/crepe/manager/LoopTimerManager.cpp @@ -0,0 +1,77 @@ +#include +#include + +#include "../facade/SDLContext.h" +#include "../util/Log.h" + +#include "LoopTimerManager.h" + +using namespace crepe; + +LoopTimerManager::LoopTimerManager(Mediator & mediator) : Manager(mediator) { + this->mediator.loop_timer = *this; + dbg_trace(); + } + +void LoopTimerManager::start() { + this->last_frame_time = std::chrono::steady_clock::now(); + + this->elapsed_time = std::chrono::milliseconds(0); + // by starting the elapsed_fixed_time at (0 - fixed_delta_time) in milliseconds it calls a fixed update at the start of the loop. + this->elapsed_fixed_time + = -std::chrono::duration_cast(fixed_delta_time); + this->delta_time = std::chrono::milliseconds(0); +} + +void LoopTimerManager::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>( + current_frame_time - last_frame_time); + + if (this->delta_time > this->maximum_delta_time) { + this->delta_time = this->maximum_delta_time; + } + this->actual_fps = 1.0 / this->delta_time.count(); + + this->elapsed_time += this->delta_time; + this->last_frame_time = current_frame_time; +} + +double LoopTimerManager::get_delta_time() const { return this->delta_time.count() * this->game_scale; } + +double LoopTimerManager::get_current_time() const { return this->elapsed_time.count(); } + +void LoopTimerManager::advance_fixed_update() { this->elapsed_fixed_time += this->fixed_delta_time; } + +double LoopTimerManager::get_fixed_delta_time() const { return this->fixed_delta_time.count(); } + +void LoopTimerManager::set_target_fps(int fps) { + this->target_fps = fps; + // target time per frame in seconds + this->frame_target_time = std::chrono::duration(1.0) / this->target_fps; +} + +int LoopTimerManager::get_fps() const { return this->actual_fps; } + +void LoopTimerManager::set_time_scale(double value) { this->game_scale = value; } + +double LoopTimerManager::get_time_scale() const { return this->game_scale; } +void LoopTimerManager::enforce_frame_rate() { + auto current_frame_time = std::chrono::steady_clock::now(); + auto frame_duration = current_frame_time - this->last_frame_time; + + // Check if frame duration is less than the target frame time + if (frame_duration < this->frame_target_time) { + auto delay_time = std::chrono::duration_cast( + this->frame_target_time - frame_duration); + + if (delay_time.count() > 0) { + std::this_thread::sleep_for(delay_time); + } + } +} + +double LoopTimerManager::get_lag() const { + return (this->elapsed_time - this->elapsed_fixed_time).count(); +} diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h new file mode 100644 index 0000000..dba0f66 --- /dev/null +++ b/src/crepe/manager/LoopTimerManager.h @@ -0,0 +1,133 @@ +#pragma once + +#include +#include "../manager/Manager.h" +namespace crepe { + +class LoopTimerManager : public Manager { +public: + LoopTimerManager(Mediator & mediator); + /** + * \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. + */ + double get_current_time() const; + + /** + * \brief Set the target frames per second (FPS). + * + * \param fps The desired frames rendered per second. + */ + void set_target_fps(int fps); + + /** + * \brief Get the current frames per second (FPS). + * + * \return Current FPS. + */ + int get_fps() const; + + /** + * \brief Get the current time scale. + * + * \return The current time scale, where 0 = paused, 1 = normal speed, and values > 1 speed + * up the game. + */ + double get_time_scale() const; + + /** + * \brief Set the time scale. + * + * \param game_scale The desired time scale (0 = pause, 1 = normal speed, > 1 = speed up). + */ + void set_time_scale(double game_scale); + +private: + friend class LoopManager; + + /** + * \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. + */ + 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. + */ + 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. + */ + double get_lag() const; + + /** + * \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. + */ + void advance_fixed_update(); + +private: + //! Target frames per second + int target_fps = 50; + //! Actual frames per second + int actual_fps = 0; + //! Current game scale + double game_scale = 1; + //! Maximum delta time in seconds to avoid large jumps + std::chrono::duration maximum_delta_time{0.25}; + //! Delta time for the current frame in seconds + std::chrono::duration delta_time{0.0}; + //! Target time per frame in seconds + std::chrono::duration frame_target_time + = std::chrono::duration(1.0) / target_fps; + //! Fixed delta time for fixed updates in seconds + std::chrono::duration fixed_delta_time = std::chrono::duration(1.0) / 50.0; + //! Total elapsed game time in seconds + std::chrono::duration elapsed_time{0.0}; + //! Total elapsed time for fixed updates in seconds + std::chrono::duration elapsed_fixed_time{0.0}; + //! Time of the last frame + std::chrono::steady_clock::time_point last_frame_time; +}; + +} // namespace crepe diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index cd96614..c72af8e 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -3,14 +3,14 @@ #include "../util/OptionalRef.h" // TODO: remove these singletons: -#include "EventManager.h" #include "SaveManager.h" namespace crepe { class ComponentManager; class SceneManager; -class LoopTimer; +class LoopTimerManager; +class EventManager; /** * Struct to pass references to classes that would otherwise need to be singletons down to * other classes within the engine hierarchy. Made to prevent constant changes to subclasses to @@ -27,8 +27,8 @@ struct Mediator { OptionalRef component_manager; OptionalRef scene_manager; OptionalRef save_manager = SaveManager::get_instance(); - OptionalRef event_manager = EventManager::get_instance(); - OptionalRef loop_timer; + OptionalRef event_manager; + OptionalRef loop_timer; }; } // namespace crepe diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index dccd554..8479998 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -1,56 +1,41 @@ #include #include - #include -#include -#include #include - +#include using namespace std; using namespace std::chrono_literals; using namespace crepe; class EventManagerTest : public ::testing::Test { protected: + Mediator mediator; + EventManager event_mgr{mediator}; void SetUp() override { // Clear any existing subscriptions or events before each test - EventManager::get_instance().clear(); + event_mgr.clear(); } void TearDown() override { // Ensure cleanup after each test - EventManager::get_instance().clear(); + event_mgr.clear(); } }; -class MockKeyListener : public IKeyListener { -public: - MOCK_METHOD(bool, on_key_pressed, (const KeyPressEvent & event), (override)); - MOCK_METHOD(bool, on_key_released, (const KeyReleaseEvent & event), (override)); -}; - -class MockMouseListener : public IMouseListener { -public: - MOCK_METHOD(bool, on_mouse_clicked, (const MouseClickEvent & event), (override)); - MOCK_METHOD(bool, on_mouse_pressed, (const MousePressEvent & event), (override)); - MOCK_METHOD(bool, on_mouse_released, (const MouseReleaseEvent & event), (override)); - MOCK_METHOD(bool, on_mouse_moved, (const MouseMoveEvent & event), (override)); -}; - TEST_F(EventManagerTest, EventSubscription) { EventHandler key_handler = [](const KeyPressEvent & e) { return true; }; // Subscribe to KeyPressEvent - EventManager::get_instance().subscribe(key_handler, 1); + event_mgr.subscribe(key_handler, 1); // Verify subscription (not directly verifiable; test by triggering event) - EventManager::get_instance().trigger_event( + event_mgr.trigger_event( KeyPressEvent{ .repeat = true, .key = Keycode::A, }, 1); - EventManager::get_instance().trigger_event( + event_mgr.trigger_event( KeyPressEvent{ .repeat = true, .key = Keycode::A, @@ -68,12 +53,12 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; - EventManager::get_instance().subscribe(mouse_handler, + event_mgr.subscribe(mouse_handler, EventManager::CHANNEL_ALL); MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - EventManager::get_instance().trigger_event(click_event, + event_mgr.trigger_event(click_event, EventManager::CHANNEL_ALL); EXPECT_TRUE(triggered); @@ -88,19 +73,18 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_one_channel) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; - EventManager::get_instance().subscribe(mouse_handler, test_channel); + event_mgr.subscribe(mouse_handler, test_channel); MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - EventManager::get_instance().trigger_event(click_event, + event_mgr.trigger_event(click_event, EventManager::CHANNEL_ALL); EXPECT_FALSE(triggered); - EventManager::get_instance().trigger_event(click_event, test_channel); + event_mgr.trigger_event(click_event, test_channel); } TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { - EventManager & event_manager = EventManager::get_instance(); // Flags to track handler calls bool triggered_true = false; @@ -126,11 +110,11 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { // Test event MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - event_manager.subscribe(mouse_handler_true, EventManager::CHANNEL_ALL); - event_manager.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); + event_mgr.subscribe(mouse_handler_true, EventManager::CHANNEL_ALL); + event_mgr.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); // Trigger event - event_manager.trigger_event(click_event, EventManager::CHANNEL_ALL); + event_mgr.trigger_event(click_event, EventManager::CHANNEL_ALL); // Check that only the true handler was triggered EXPECT_TRUE(triggered_true); @@ -139,12 +123,12 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { // Reset and clear triggered_true = false; triggered_false = false; - event_manager.clear(); - event_manager.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); - event_manager.subscribe(mouse_handler_true, EventManager::CHANNEL_ALL); + event_mgr.clear(); + event_mgr.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); + event_mgr.subscribe(mouse_handler_true, EventManager::CHANNEL_ALL); // Trigger event again - event_manager.trigger_event(click_event, EventManager::CHANNEL_ALL); + event_mgr.trigger_event(click_event, EventManager::CHANNEL_ALL); // Check that both handlers were triggered EXPECT_TRUE(triggered_true); @@ -152,7 +136,6 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { } TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) { - EventManager & event_manager = EventManager::get_instance(); bool triggered1 = false; bool triggered2 = false; int test_channel = 1; @@ -170,21 +153,20 @@ TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; - event_manager.subscribe(mouse_handler1); - event_manager.subscribe(mouse_handler2, test_channel); + event_mgr.subscribe(mouse_handler1); + event_mgr.subscribe(mouse_handler2, test_channel); - event_manager.queue_event( + event_mgr.queue_event( MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); - event_manager.queue_event( + event_mgr.queue_event( MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}, test_channel); - event_manager.dispatch_events(); + event_mgr.dispatch_events(); EXPECT_TRUE(triggered1); EXPECT_TRUE(triggered2); } TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { - EventManager & event_manager = EventManager::get_instance(); // Flags to track if handlers are triggered bool triggered1 = false; @@ -207,15 +189,15 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { return false; // Allows propagation }; // Subscribe handlers - subscription_t handler1_id = event_manager.subscribe(mouse_handler1); - subscription_t handler2_id = event_manager.subscribe(mouse_handler2); + subscription_t handler1_id = event_mgr.subscribe(mouse_handler1); + subscription_t handler2_id = event_mgr.subscribe(mouse_handler2); // Queue events - event_manager.queue_event( + event_mgr.queue_event( MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); // Dispatch events - both handlers should be triggered - event_manager.dispatch_events(); + event_mgr.dispatch_events(); EXPECT_TRUE(triggered1); // Handler 1 should be triggered EXPECT_TRUE(triggered2); // Handler 2 should be triggered @@ -224,14 +206,14 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { triggered2 = false; // Unsubscribe handler1 - event_manager.unsubscribe(handler1_id); + event_mgr.unsubscribe(handler1_id); // Queue the same event again - event_manager.queue_event( + event_mgr.queue_event( MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); // Dispatch events - only handler 2 should be triggered, handler 1 should NOT - event_manager.dispatch_events(); + event_mgr.dispatch_events(); EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered EXPECT_TRUE(triggered2); // Handler 2 should be triggered @@ -239,14 +221,14 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { triggered2 = false; // Unsubscribe handler2 - event_manager.unsubscribe(handler2_id); + event_mgr.unsubscribe(handler2_id); // Queue the event again - event_manager.queue_event( + event_mgr.queue_event( MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); // Dispatch events - no handler should be triggered - event_manager.dispatch_events(); + event_mgr.dispatch_events(); EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered EXPECT_FALSE(triggered2); // Handler 2 should NOT be triggered } diff --git a/src/test/LoopManagerTest.cpp b/src/test/LoopManagerTest.cpp index 503eb1f..57f7a2e 100644 --- a/src/test/LoopManagerTest.cpp +++ b/src/test/LoopManagerTest.cpp @@ -2,13 +2,11 @@ #include #include #include -#include #define private public #define protected public -#include "api/LoopManager.h" -#include "api/LoopTimer.h" -#include "manager/EventManager.h" -#include "api/Event.h" +#include +#include +#include using namespace std::chrono; using namespace crepe; @@ -24,19 +22,18 @@ protected: TestGameLoop test_loop; // LoopManager test_loop; void SetUp() override { - test_loop.loop_timer->set_target_fps(10); } }; TEST_F(LoopManagerTest, FixedUpdate) { // Arrange - test_loop.loop_timer->set_target_fps(60); + test_loop.loop_timer.set_target_fps(60); // Set expectations for the mock calls EXPECT_CALL(test_loop, render).Times(::testing::Exactly(60)); EXPECT_CALL(test_loop, update).Times(::testing::Exactly(60)); - EXPECT_CALL(test_loop, fixed_update).Times(::testing::AtLeast(50)); + EXPECT_CALL(test_loop, fixed_update).Times(::testing::Exactly(50)); // Start the loop in a separate thread std::thread loop_thread([&]() { test_loop.start(); }); @@ -46,12 +43,26 @@ TEST_F(LoopManagerTest, FixedUpdate) { // Stop the game loop test_loop.game_running = false; - // Wait for the loop thread to finish loop_thread.join(); // Test finished } +TEST_F(LoopManagerTest, ShutDown) { + // Arrange + test_loop.loop_timer.set_target_fps(60); + EXPECT_CALL(test_loop, render).Times(::testing::AtLeast(1)); + EXPECT_CALL(test_loop, update).Times(::testing::AtLeast(1)); + EXPECT_CALL(test_loop, fixed_update).Times(::testing::AtLeast(1)); + // Start the loop in a separate thread + std::thread loop_thread([&]() { test_loop.start(); }); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + test_loop.event_manager.trigger_event(ShutDownEvent{}); + // Wait for the loop thread to finish + loop_thread.join(); + + // Test finished +} diff --git a/src/test/LoopTimerTest.cpp b/src/test/LoopTimerTest.cpp index 7652093..09b4e00 100644 --- a/src/test/LoopTimerTest.cpp +++ b/src/test/LoopTimerTest.cpp @@ -1,16 +1,17 @@ #include -#include #include +#include #define private public #define protected public -#include "api/LoopTimer.h" - +#include +#include using namespace std::chrono; using namespace crepe; class LoopTimerTest : public ::testing::Test { protected: - LoopTimer loop_timer; + Mediator mediator; + LoopTimerManager loop_timer{mediator}; void SetUp() override { loop_timer.start(); } }; @@ -25,8 +26,7 @@ TEST_F(LoopTimerTest, EnforcesTargetFrameRate) { auto elapsed_ms = duration_cast(elapsed_time).count(); // For 60 FPS, the target frame time is around 16.67ms - ASSERT_GE(elapsed_ms, 16); // Make sure it's at least 16 ms (could be slightly more) - ASSERT_LE(elapsed_ms, 18); // Ensure it's not too much longer + ASSERT_NEAR(elapsed_ms,16.7,1); } TEST_F(LoopTimerTest, SetTargetFps) { // Set the target FPS to 120 @@ -48,11 +48,10 @@ TEST_F(LoopTimerTest, DeltaTimeCalculation) { // Check the delta time double delta_time = loop_timer.get_delta_time(); - auto elapsed_time = duration_cast(end_time - start_time).count(); + auto elapsed_time = duration_cast(end_time - start_time).count(); // Assert that delta_time is close to the elapsed time - ASSERT_GE(delta_time, elapsed_time / 1000.0); - ASSERT_LE(delta_time, (elapsed_time + 2) / 1000.0); + ASSERT_NEAR(delta_time, elapsed_time, 1); } TEST_F(LoopTimerTest, getCurrentTime) { diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h index 1bbfdd3..ee68c23 100644 --- a/src/test/ScriptTest.h +++ b/src/test/ScriptTest.h @@ -7,7 +7,7 @@ #include #include #include - +#include class ScriptTest : public testing::Test { protected: crepe::Mediator mediator; @@ -15,7 +15,7 @@ protected: public: crepe::ComponentManager component_manager{mediator}; crepe::ScriptSystem system{mediator}; - + crepe::EventManager event_mgr{mediator}; class MyScript : public crepe::Script { // NOTE: explicitly stating `public:` is not required on actual scripts -- cgit v1.2.3 From f3009ab8f0785a54d3fd83c0d758c8ebd901adda Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 7 Dec 2024 14:19:26 +0100 Subject: `make format` --- src/crepe/manager/ComponentManager.cpp | 11 ++++------- src/crepe/manager/ComponentManager.h | 5 +++-- src/crepe/manager/ComponentManager.hpp | 11 +++++++---- src/test/ECSTest.cpp | 1 - src/test/ScriptECSTest.cpp | 5 ++--- src/test/ScriptSceneTest.cpp | 1 - src/test/ScriptTest.cpp | 1 - 7 files changed, 16 insertions(+), 19 deletions(-) (limited to 'src/crepe/manager') diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp index 44774d9..df30d27 100644 --- a/src/crepe/manager/ComponentManager.cpp +++ b/src/crepe/manager/ComponentManager.cpp @@ -64,14 +64,11 @@ void ComponentManager::set_persistent(game_object_id_t id, bool persistent) { } set ComponentManager::get_objects_by_name(const string & name) const { - return this->get_objects_by_predicate([name](const Metadata & data) { - return data.name == name; - }); + return this->get_objects_by_predicate( + [name](const Metadata & data) { return data.name == name; }); } set ComponentManager::get_objects_by_tag(const string & tag) const { - return this->get_objects_by_predicate([tag](const Metadata & data) { - return data.tag == tag; - }); + return this->get_objects_by_predicate( + [tag](const Metadata & data) { return data.tag == tag; }); } - diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index 4e53954..685cae5 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -1,10 +1,10 @@ #pragma once #include +#include #include #include #include -#include #include "../Component.h" #include "../types.h" @@ -156,7 +156,8 @@ public: private: template - std::set get_objects_by_predicate(const std::function & pred) const; + std::set + get_objects_by_predicate(const std::function & pred) const; template RefVector get_components_by_ids(const std::set & ids) const; diff --git a/src/crepe/manager/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp index 52df368..9e70865 100644 --- a/src/crepe/manager/ComponentManager.hpp +++ b/src/crepe/manager/ComponentManager.hpp @@ -101,7 +101,8 @@ RefVector ComponentManager::get_components_by_id(game_object_id_t id) const { type_index type = typeid(T); if (!this->components.contains(type)) return {}; - const by_id_index>> & components_by_id = this->components.at(type); + const by_id_index>> & components_by_id + = this->components.at(type); if (id >= components_by_id.size()) return {}; RefVector out = {}; @@ -151,7 +152,8 @@ RefVector ComponentManager::get_components_by_type() const { } template -std::set ComponentManager::get_objects_by_predicate(const std::function & pred) const { +std::set +ComponentManager::get_objects_by_predicate(const std::function & pred) const { using namespace std; set objects = {}; @@ -168,9 +170,10 @@ std::set ComponentManager::get_objects_by_predicate(const std: } template -RefVector ComponentManager::get_components_by_ids(const std::set & ids) const { +RefVector +ComponentManager::get_components_by_ids(const std::set & ids) const { using namespace std; - + RefVector out = {}; for (game_object_id_t id : ids) { RefVector components = get_components_by_id(id); diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index ed5341e..af2b7b0 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -466,4 +466,3 @@ TEST_F(ECSTest, ComponentsByTag) { EXPECT_EQ(objects.size(), 3); } } - diff --git a/src/test/ScriptECSTest.cpp b/src/test/ScriptECSTest.cpp index 4477e55..1ec33ba 100644 --- a/src/test/ScriptECSTest.cpp +++ b/src/test/ScriptECSTest.cpp @@ -4,8 +4,8 @@ #include #include -#include #include +#include #include #include @@ -32,8 +32,7 @@ TEST_F(ScriptECSTest, GetOwnComponent) { TEST_F(ScriptECSTest, GetOwnComponents) { const unsigned COUNT = 4; - for (unsigned i = 0; i < COUNT; i++) - entity.add_component(); + for (unsigned i = 0; i < COUNT; i++) entity.add_component(); MyScript & script = this->script; RefVector components = script.get_components(); diff --git a/src/test/ScriptSceneTest.cpp b/src/test/ScriptSceneTest.cpp index 8e849c1..2568049 100644 --- a/src/test/ScriptSceneTest.cpp +++ b/src/test/ScriptSceneTest.cpp @@ -28,4 +28,3 @@ TEST_F(ScriptSceneTest, Default) { script.set_next_scene(non_default_value); EXPECT_EQ(non_default_value, scene_manager.next_scene); } - diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp index b0b2546..acdae70 100644 --- a/src/test/ScriptTest.cpp +++ b/src/test/ScriptTest.cpp @@ -73,4 +73,3 @@ TEST_F(ScriptTest, UpdateInactive) { system.update(); } } - -- cgit v1.2.3 From 76f94a8cdd2945e26d716191b664eb906ebb9032 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 7 Dec 2024 14:36:08 +0100 Subject: add doxygen comments --- src/crepe/manager/ComponentManager.h | 40 +++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) (limited to 'src/crepe/manager') diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index e7eeaa2..19a8e81 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -155,18 +155,55 @@ public: RefVector get_components_by_tag(const std::string & tag) const; private: + /** + * \brief Get object IDs by predicate function + * + * This function calls the predicate function \c pred for all components matching type \c T, + * and adds their parent game_object_id to a \c std::set if the predicate returns true. + * + * \tparam T The type of the component to check the predicate against + * \param pred Predicate function + * + * \note The predicate function may be called for multiple components with the same \c + * game_object_id. In this case, the ID is added if *any* call returns \c true. + * + * \returns game_object_id for all components where the predicate returned true + */ template std::set get_objects_by_predicate(const std::function & pred) const; + + /** + * \brief Get components of type \c T for multiple game object IDs + * + * \tparam T The type of the components to return + * \param ids The object IDs + * + * \return All components matching type \c T and one of the IDs in \c ids + */ template RefVector get_components_by_ids(const std::set & ids) const; + /** + * \brief Get object IDs for objects with name \c name + * + * \param name Object name to match + * \returns Object IDs where Metadata::name is equal to \c name + */ std::set get_objects_by_name(const std::string & name) const; + /** + * \brief Get object IDs for objects with tag \c tag + * + * \param tag Object tag to match + * \returns Object IDs where Metadata::tag is equal to \c tag + */ std::set get_objects_by_tag(const std::string & tag) const; private: + //! By Component \c std::type_index (readability helper type) template using by_type = std::unordered_map; + //! By \c game_object_id index (readability helper type) template using by_id_index = std::vector; /** @@ -188,9 +225,6 @@ private: game_object_id_t next_id = 0; }; -// template <> -// RefVector ComponentManager::get_components_by_id(game_object_id_t id) const; - } // namespace crepe #include "ComponentManager.hpp" -- cgit v1.2.3 From a06ea7d18008d0859818f7e8fd3e595e7dd05772 Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Sat, 7 Dec 2024 14:44:46 +0100 Subject: changed looptimerManager doxygen --- src/crepe/manager/LoopTimerManager.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src/crepe/manager') diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h index dba0f66..fee6310 100644 --- a/src/crepe/manager/LoopTimerManager.h +++ b/src/crepe/manager/LoopTimerManager.h @@ -1,9 +1,19 @@ #pragma once #include -#include "../manager/Manager.h" + +#include "Manager.h" + namespace crepe { +/** + * \brief Manages timing and frame rate for the game loop. + * + * The LoopTimerManager class is responsible for calculating and managing timing functions + * such as delta time, frames per second (FPS), fixed time steps, and time scaling. It ensures + * consistent frame updates and supports game loop operations, such as handling fixed updates + * for physics and other time-sensitive operations. + */ class LoopTimerManager : public Manager { public: LoopTimerManager(Mediator & mediator); @@ -49,6 +59,8 @@ public: /** * \brief Set the time scale. * + * time_scale is a value that changes the delta time that can be retrieved using get_delta_time function. + * * \param game_scale The desired time scale (0 = pause, 1 = normal speed, > 1 = speed up). */ void set_time_scale(double game_scale); -- cgit v1.2.3 From f05458cdbf68e8efe1ed812f57e957921921941d Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Sat, 7 Dec 2024 14:51:12 +0100 Subject: more doxygen changes --- src/crepe/api/LoopManager.cpp | 1 - src/crepe/api/LoopManager.h | 9 +++++---- src/crepe/manager/LoopTimerManager.cpp | 6 +++--- src/crepe/manager/LoopTimerManager.h | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) (limited to 'src/crepe/manager') diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index 69cbfaf..42a1e77 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,4 +1,3 @@ -#include #include "../facade/SDLContext.h" #include "../manager/EventManager.h" diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 00f5409..d07ef66 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -94,13 +94,14 @@ private: ComponentManager component_manager{mediator}; //! Scene manager instance SceneManager scene_manager{mediator}; - - //! SDL context \todo no more singletons! - SDLContext & sdl_context = SDLContext::get_instance(); - //! LoopTimer instance + //! LoopTimerManager instance LoopTimerManager loop_timer{mediator}; //! EventManager instance EventManager event_manager{mediator}; + + //! SDL context \todo no more singletons! + SDLContext & sdl_context = SDLContext::get_instance(); + private: /** diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp index 8156c6d..2379fdd 100644 --- a/src/crepe/manager/LoopTimerManager.cpp +++ b/src/crepe/manager/LoopTimerManager.cpp @@ -38,7 +38,7 @@ void LoopTimerManager::update() { this->last_frame_time = current_frame_time; } -double LoopTimerManager::get_delta_time() const { return this->delta_time.count() * this->game_scale; } +double LoopTimerManager::get_delta_time() const { return this->delta_time.count() * this->time_scale; } double LoopTimerManager::get_current_time() const { return this->elapsed_time.count(); } @@ -54,9 +54,9 @@ void LoopTimerManager::set_target_fps(int fps) { int LoopTimerManager::get_fps() const { return this->actual_fps; } -void LoopTimerManager::set_time_scale(double value) { this->game_scale = value; } +void LoopTimerManager::set_time_scale(double value) { this->time_scale = value; } -double LoopTimerManager::get_time_scale() const { return this->game_scale; } +double LoopTimerManager::get_time_scale() const { return this->time_scale; } void LoopTimerManager::enforce_frame_rate() { auto current_frame_time = std::chrono::steady_clock::now(); auto frame_duration = current_frame_time - this->last_frame_time; diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h index fee6310..cd05bf2 100644 --- a/src/crepe/manager/LoopTimerManager.h +++ b/src/crepe/manager/LoopTimerManager.h @@ -51,7 +51,7 @@ public: /** * \brief Get the current time scale. * - * \return The current time scale, where 0 = paused, 1 = normal speed, and values > 1 speed + * \return The current time scale, where (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up). * up the game. */ double get_time_scale() const; @@ -61,9 +61,9 @@ public: * * time_scale is a value that changes the delta time that can be retrieved using get_delta_time function. * - * \param game_scale The desired time scale (0 = pause, 1 = normal speed, > 1 = speed up). + * \param time_scale The desired time scale (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up). */ - void set_time_scale(double game_scale); + void set_time_scale(double time_scale); private: friend class LoopManager; @@ -123,8 +123,8 @@ private: int target_fps = 50; //! Actual frames per second int actual_fps = 0; - //! Current game scale - double game_scale = 1; + //! time scale for speeding up or slowing down the game (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up) + double time_scale = 1; //! Maximum delta time in seconds to avoid large jumps std::chrono::duration maximum_delta_time{0.25}; //! Delta time for the current frame in seconds -- cgit v1.2.3 From f19f37ae3eff84161f86e62a26fbd8b68f8f91a9 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 7 Dec 2024 15:29:33 +0100 Subject: make SaveManager no longer a singleton --- src/crepe/manager/Mediator.h | 4 ++-- src/crepe/manager/SaveManager.cpp | 32 +++++++++++++--------------- src/crepe/manager/SaveManager.h | 27 ++++++++++-------------- src/example/CMakeLists.txt | 2 -- src/example/asset_manager.cpp | 36 -------------------------------- src/example/savemgr.cpp | 44 --------------------------------------- src/test/CMakeLists.txt | 1 + src/test/SaveManagerTest.cpp | 41 ++++++++++++++++++++++++++++++++++++ 8 files changed, 69 insertions(+), 118 deletions(-) delete mode 100644 src/example/asset_manager.cpp delete mode 100644 src/example/savemgr.cpp create mode 100644 src/test/SaveManagerTest.cpp (limited to 'src/crepe/manager') diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index 8094d80..6507a74 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -5,13 +5,13 @@ // TODO: remove these singletons: #include "../facade/SDLContext.h" #include "EventManager.h" -#include "SaveManager.h" #include "api/LoopTimer.h" namespace crepe { class ComponentManager; class SceneManager; +class SaveManager; /** * Struct to pass references to classes that would otherwise need to be singletons down to @@ -28,7 +28,7 @@ class SceneManager; struct Mediator { OptionalRef component_manager; OptionalRef scene_manager; - OptionalRef save_manager = SaveManager::get_instance(); + OptionalRef save_manager; OptionalRef event_manager = EventManager::get_instance(); OptionalRef sdl_context = SDLContext::get_instance(); OptionalRef timer = LoopTimer::get_instance(); diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp index d4ed1c1..292e8fd 100644 --- a/src/crepe/manager/SaveManager.cpp +++ b/src/crepe/manager/SaveManager.cpp @@ -1,13 +1,24 @@ #include "../ValueBroker.h" #include "../api/Config.h" #include "../facade/DB.h" -#include "../util/Log.h" #include "SaveManager.h" using namespace std; using namespace crepe; +SaveManager::SaveManager(Mediator & mediator) : Manager(mediator) { + mediator.save_manager = *this; +} + +DB & SaveManager::get_db() { + if (this->db == nullptr) { + Config & cfg = Config::get_instance(); + this->db = make_unique(cfg.savemgr.location); + } + return *this->db; +} + template <> string SaveManager::serialize(const string & value) const noexcept { return value; @@ -90,22 +101,6 @@ int32_t SaveManager::deserialize(const string & value) const noexcept { return deserialize(value); } -SaveManager::SaveManager() { dbg_trace(); } - -SaveManager & SaveManager::get_instance() { - dbg_trace(); - static SaveManager instance; - return instance; -} - -DB & SaveManager::get_db() { - Config & cfg = Config::get_instance(); - // TODO: make this path relative to XDG_DATA_HOME on Linux and whatever the - // default equivalent is on Windows using some third party library - static DB db(cfg.savemgr.location); - return db; -} - bool SaveManager::has(const string & key) { DB & db = this->get_db(); return db.has(key); @@ -155,7 +150,8 @@ ValueBroker SaveManager::get(const string & key) { return { [this, key](const T & target) { this->set(key, target); }, [this, key, value]() mutable -> const T & { - value = this->deserialize(this->get_db().get(key)); + DB & db = this->get_db(); + value = this->deserialize(db.get(key)); return value; }, }; diff --git a/src/crepe/manager/SaveManager.h b/src/crepe/manager/SaveManager.h index 3d8c852..d13a97a 100644 --- a/src/crepe/manager/SaveManager.h +++ b/src/crepe/manager/SaveManager.h @@ -4,6 +4,8 @@ #include "../ValueBroker.h" +#include "Manager.h" + namespace crepe { class DB; @@ -18,7 +20,7 @@ class DB; * * The underlying database is a key-value store. */ -class SaveManager { +class SaveManager : public Manager { public: /** * \brief Get a read/write reference to a value and initialize it if it does not yet exist @@ -63,8 +65,8 @@ public: */ bool has(const std::string & key); -private: - SaveManager(); +public: + SaveManager(Mediator & mediator); virtual ~SaveManager() = default; private: @@ -90,25 +92,18 @@ private: T deserialize(const std::string & value) const noexcept; public: - // singleton - static SaveManager & get_instance(); SaveManager(const SaveManager &) = delete; SaveManager(SaveManager &&) = delete; SaveManager & operator=(const SaveManager &) = delete; SaveManager & operator=(SaveManager &&) = delete; +protected: + //! Create or return DB + virtual DB & get_db(); + private: - /** - * \brief Create an instance of DB and return its reference - * - * \returns DB instance - * - * This function exists because DB is a facade class, which can't directly be used in the API - * without workarounds - * - * TODO: better solution - */ - static DB & get_db(); + //! Database + std::unique_ptr db = nullptr; }; } // namespace crepe diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index 8ef71bb..5a93b1f 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -16,8 +16,6 @@ function(add_example target_name) add_dependencies(examples ${target_name}) endfunction() -add_example(asset_manager) -add_example(savemgr) add_example(rendering_particle) add_example(game) add_example(button) diff --git a/src/example/asset_manager.cpp b/src/example/asset_manager.cpp deleted file mode 100644 index 917b547..0000000 --- a/src/example/asset_manager.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include - -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 - { 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). - - auto & mgr = AssetManager::get_instance(); - - { - // TODO: [design] the Sound class can't be directly included by the user as it includes - // SoLoud headers. - auto bgm = mgr.cache("../mwe/audio/bgm.ogg"); - auto sfx1 = mgr.cache("../mwe/audio/sfx1.wav"); - auto sfx2 = mgr.cache("../mwe/audio/sfx2.wav"); - - auto img = mgr.cache("../asset/texture/img.png"); - auto img1 = mgr.cache("../asset/texture/second.png"); - } - - { - auto bgm = mgr.cache("../mwe/audio/bgm.ogg"); - auto sfx1 = mgr.cache("../mwe/audio/sfx1.wav"); - auto sfx2 = mgr.cache("../mwe/audio/sfx2.wav"); - - auto img = mgr.cache("../asset/texture/img.png"); - auto img1 = mgr.cache("../asset/texture/second.png"); - } -} diff --git a/src/example/savemgr.cpp b/src/example/savemgr.cpp deleted file mode 100644 index 65c4a34..0000000 --- a/src/example/savemgr.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/** \file - * - * Standalone example for usage of the save manager - */ - -#include -#include -#include -#include -#include - -using namespace crepe; - -// unrelated setup code -int _ = []() { - // make sure all log messages get printed - auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::TRACE; - - return 0; // satisfy compiler -}(); - -int main() { - const char * key = "mygame.test"; - - SaveManager & mgr = SaveManager::get_instance(); - - dbg_logf("has key = {}", mgr.has(key)); - ValueBroker prop = mgr.get(key, 0); - Proxy val = mgr.get(key, 0); - - dbg_logf("val = {}", mgr.get(key).get()); - prop.set(1); - dbg_logf("val = {}", mgr.get(key).get()); - val = 2; - dbg_logf("val = {}", mgr.get(key).get()); - mgr.set(key, 3); - dbg_logf("val = {}", mgr.get(key).get()); - - dbg_logf("has key = {}", mgr.has(key)); - assert(true == mgr.has(key)); - - return 0; -} diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index c9cbac5..734e3ee 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -17,4 +17,5 @@ target_sources(test_main PUBLIC ScriptEventTest.cpp ScriptSceneTest.cpp Profiling.cpp + SaveManagerTest.cpp ) diff --git a/src/test/SaveManagerTest.cpp b/src/test/SaveManagerTest.cpp new file mode 100644 index 0000000..a1efc33 --- /dev/null +++ b/src/test/SaveManagerTest.cpp @@ -0,0 +1,41 @@ +#include + +#include +#include +#include + +using namespace std; +using namespace crepe; +using namespace testing; + +class SaveManagerTest : public Test { + Mediator m; + class TestSaveManager : public SaveManager { + using SaveManager::SaveManager; + + // in-memory database for testing + DB db{}; + virtual DB & get_db() override { return this->db; } + }; + +public: + TestSaveManager mgr{m}; +}; + +TEST_F(SaveManagerTest, ReadWrite) { + ASSERT_FALSE(mgr.has("foo")); + mgr.set("foo", "bar"); + ASSERT_TRUE(mgr.has("foo")); + + ValueBroker value = mgr.get("foo"); + EXPECT_EQ(value.get(), "bar"); +} + +TEST_F(SaveManagerTest, DefaultValue) { + ValueBroker value = mgr.get("foo", 3); + + ASSERT_EQ(value.get(), 3); + value.set(5); + ASSERT_EQ(value.get(), 5); +} + -- cgit v1.2.3 From 24c9a9ab277897a7191d4e99213c2ab9f5d4ecd8 Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Sat, 7 Dec 2024 15:32:47 +0100 Subject: make format --- src/crepe/api/LoopManager.cpp | 5 +-- src/crepe/api/LoopManager.h | 5 +-- src/crepe/manager/EventManager.cpp | 2 +- src/crepe/manager/EventManager.h | 1 - src/crepe/manager/LoopTimerManager.cpp | 18 ++++++--- src/test/EventTest.cpp | 13 +++---- src/test/LoopManagerTest.cpp | 70 ++++++++++++++++------------------ src/test/LoopTimerTest.cpp | 4 +- src/test/ScriptTest.h | 2 +- 9 files changed, 57 insertions(+), 63 deletions(-) (limited to 'src/crepe/manager') diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index c25e31e..f41c357 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -27,9 +27,7 @@ LoopManager::LoopManager() { [this](const ShutDownEvent & event) { return this->on_shutdown(event); }); } -void LoopManager::process_input() { - this->get_system().update(); -} +void LoopManager::process_input() { this->get_system().update(); } void LoopManager::start() { this->setup(); @@ -45,7 +43,6 @@ void LoopManager::fixed_update() { } void LoopManager::loop() { - while (game_running) { this->loop_timer.update(); diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 9986aa5..6b2e857 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -4,11 +4,11 @@ #include "../facade/SDLContext.h" #include "../manager/ComponentManager.h" -#include "../manager/SceneManager.h" #include "../manager/EventManager.h" #include "../manager/LoopTimerManager.h" -#include "../system/System.h" #include "../manager/Mediator.h" +#include "../manager/SceneManager.h" +#include "../system/System.h" #include "api/Event.h" @@ -103,7 +103,6 @@ private: //! SDL context \todo no more singletons! SDLContext & sdl_context = SDLContext::get_instance(); - private: /** * \brief Callback function for ShutDownEvent diff --git a/src/crepe/manager/EventManager.cpp b/src/crepe/manager/EventManager.cpp index 9b0fa95..6aa49ee 100644 --- a/src/crepe/manager/EventManager.cpp +++ b/src/crepe/manager/EventManager.cpp @@ -3,7 +3,7 @@ using namespace crepe; using namespace std; -EventManager::EventManager(Mediator & mediator) : Manager(mediator){ +EventManager::EventManager(Mediator & mediator) : Manager(mediator) { this->mediator.event_manager = *this; } void EventManager::dispatch_events() { diff --git a/src/crepe/manager/EventManager.h b/src/crepe/manager/EventManager.h index 30b929c..ba55edf 100644 --- a/src/crepe/manager/EventManager.h +++ b/src/crepe/manager/EventManager.h @@ -98,7 +98,6 @@ public: void clear(); private: - /** * \struct QueueEntry * \brief Represents an entry in the event queue. diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp index 2379fdd..9bf30ae 100644 --- a/src/crepe/manager/LoopTimerManager.cpp +++ b/src/crepe/manager/LoopTimerManager.cpp @@ -8,10 +8,10 @@ using namespace crepe; -LoopTimerManager::LoopTimerManager(Mediator & mediator) : Manager(mediator) { +LoopTimerManager::LoopTimerManager(Mediator & mediator) : Manager(mediator) { this->mediator.loop_timer = *this; - dbg_trace(); - } + dbg_trace(); +} void LoopTimerManager::start() { this->last_frame_time = std::chrono::steady_clock::now(); @@ -38,13 +38,19 @@ void LoopTimerManager::update() { this->last_frame_time = current_frame_time; } -double LoopTimerManager::get_delta_time() const { return this->delta_time.count() * this->time_scale; } +double LoopTimerManager::get_delta_time() const { + return this->delta_time.count() * this->time_scale; +} double LoopTimerManager::get_current_time() const { return this->elapsed_time.count(); } -void LoopTimerManager::advance_fixed_update() { this->elapsed_fixed_time += this->fixed_delta_time; } +void LoopTimerManager::advance_fixed_update() { + this->elapsed_fixed_time += this->fixed_delta_time; +} -double LoopTimerManager::get_fixed_delta_time() const { return this->fixed_delta_time.count(); } +double LoopTimerManager::get_fixed_delta_time() const { + return this->fixed_delta_time.count(); +} void LoopTimerManager::set_target_fps(int fps) { this->target_fps = fps; diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index 8479998..82272b5 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -1,8 +1,8 @@ -#include -#include #include #include #include +#include +#include using namespace std; using namespace std::chrono_literals; using namespace crepe; @@ -53,13 +53,11 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; - event_mgr.subscribe(mouse_handler, - EventManager::CHANNEL_ALL); + event_mgr.subscribe(mouse_handler, EventManager::CHANNEL_ALL); MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - event_mgr.trigger_event(click_event, - EventManager::CHANNEL_ALL); + event_mgr.trigger_event(click_event, EventManager::CHANNEL_ALL); EXPECT_TRUE(triggered); } @@ -77,8 +75,7 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_one_channel) { MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - event_mgr.trigger_event(click_event, - EventManager::CHANNEL_ALL); + event_mgr.trigger_event(click_event, EventManager::CHANNEL_ALL); EXPECT_FALSE(triggered); event_mgr.trigger_event(click_event, test_channel); diff --git a/src/test/LoopManagerTest.cpp b/src/test/LoopManagerTest.cpp index 13a0ada..f73605e 100644 --- a/src/test/LoopManagerTest.cpp +++ b/src/test/LoopManagerTest.cpp @@ -1,67 +1,63 @@ #include -#include #include +#include #include #define private public #define protected public #include -#include #include +#include using namespace std::chrono; using namespace crepe; class LoopManagerTest : public ::testing::Test { protected: - class TestGameLoop : public crepe::LoopManager { - public: - MOCK_METHOD(void, fixed_update, (), (override)); - MOCK_METHOD(void, update, (), (override)); + class TestGameLoop : public crepe::LoopManager { + public: + MOCK_METHOD(void, fixed_update, (), (override)); + MOCK_METHOD(void, update, (), (override)); MOCK_METHOD(void, render, (), (override)); - }; + }; - TestGameLoop test_loop; - void SetUp() override { - - } + TestGameLoop test_loop; + void SetUp() override {} }; TEST_F(LoopManagerTest, FixedUpdate) { - // Arrange - test_loop.loop_timer.set_target_fps(60); + // Arrange + test_loop.loop_timer.set_target_fps(60); - // Set expectations for the mock calls - EXPECT_CALL(test_loop, render).Times(::testing::Exactly(60)); + // Set expectations for the mock calls + EXPECT_CALL(test_loop, render).Times(::testing::Exactly(60)); EXPECT_CALL(test_loop, update).Times(::testing::Exactly(60)); - EXPECT_CALL(test_loop, fixed_update).Times(::testing::Exactly(50)); + EXPECT_CALL(test_loop, fixed_update).Times(::testing::Exactly(50)); - // Start the loop in a separate thread - std::thread loop_thread([&]() { test_loop.start(); }); + // Start the loop in a separate thread + std::thread loop_thread([&]() { test_loop.start(); }); - // Let the loop run for exactly 1 second - std::this_thread::sleep_for(std::chrono::seconds(1)); + // Let the loop run for exactly 1 second + std::this_thread::sleep_for(std::chrono::seconds(1)); - // Stop the game loop - test_loop.game_running = false; - // Wait for the loop thread to finish - loop_thread.join(); + // Stop the game loop + test_loop.game_running = false; + // Wait for the loop thread to finish + loop_thread.join(); - // Test finished + // Test finished } TEST_F(LoopManagerTest, ShutDown) { - // Arrange - test_loop.loop_timer.set_target_fps(60); + // Arrange + test_loop.loop_timer.set_target_fps(60); - EXPECT_CALL(test_loop, render).Times(::testing::AtLeast(1)); + EXPECT_CALL(test_loop, render).Times(::testing::AtLeast(1)); EXPECT_CALL(test_loop, update).Times(::testing::AtLeast(1)); - EXPECT_CALL(test_loop, fixed_update).Times(::testing::AtLeast(1)); - // Start the loop in a separate thread - std::thread loop_thread([&]() { test_loop.start(); }); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + EXPECT_CALL(test_loop, fixed_update).Times(::testing::AtLeast(1)); + // Start the loop in a separate thread + std::thread loop_thread([&]() { test_loop.start(); }); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); test_loop.event_manager.trigger_event(ShutDownEvent{}); - // Wait for the loop thread to finish - loop_thread.join(); + // Wait for the loop thread to finish + loop_thread.join(); - // Test finished + // Test finished } - - diff --git a/src/test/LoopTimerTest.cpp b/src/test/LoopTimerTest.cpp index 09b4e00..c6655d9 100644 --- a/src/test/LoopTimerTest.cpp +++ b/src/test/LoopTimerTest.cpp @@ -1,6 +1,6 @@ #include -#include #include +#include #define private public #define protected public #include @@ -26,7 +26,7 @@ TEST_F(LoopTimerTest, EnforcesTargetFrameRate) { auto elapsed_ms = duration_cast(elapsed_time).count(); // For 60 FPS, the target frame time is around 16.67ms - ASSERT_NEAR(elapsed_ms,16.7,1); + ASSERT_NEAR(elapsed_ms, 16.7, 1); } TEST_F(LoopTimerTest, SetTargetFps) { // Set the target FPS to 120 diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h index ee68c23..e0205ff 100644 --- a/src/test/ScriptTest.h +++ b/src/test/ScriptTest.h @@ -6,8 +6,8 @@ #include #include #include -#include #include +#include class ScriptTest : public testing::Test { protected: crepe::Mediator mediator; -- cgit v1.2.3 From f0fba1667882764d4fee426e0c14f6ec3e11cdbc Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Sat, 7 Dec 2024 15:39:23 +0100 Subject: added line break --- src/crepe/manager/LoopTimerManager.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/crepe/manager') diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp index 9bf30ae..e30b90d 100644 --- a/src/crepe/manager/LoopTimerManager.cpp +++ b/src/crepe/manager/LoopTimerManager.cpp @@ -63,6 +63,7 @@ int LoopTimerManager::get_fps() const { return this->actual_fps; } void LoopTimerManager::set_time_scale(double value) { this->time_scale = value; } double LoopTimerManager::get_time_scale() const { return this->time_scale; } + void LoopTimerManager::enforce_frame_rate() { auto current_frame_time = std::chrono::steady_clock::now(); auto frame_duration = current_frame_time - this->last_frame_time; -- cgit v1.2.3 From 3467c96eff775fccb24a6ecd7c15a7f42660a7ff Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Sat, 7 Dec 2024 15:40:32 +0100 Subject: removed comments --- src/crepe/manager/Mediator.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/crepe/manager') diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index 987f4d4..eb8a7a5 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -4,9 +4,8 @@ // TODO: remove these singletons: #include "../facade/SDLContext.h" -//#include "EventManager.h" + #include "SaveManager.h" -//#include "LoopTimerManager.h" namespace crepe { -- cgit v1.2.3 From b5bf36aec3caa879d77457e3b2d1d29b6b625f27 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 7 Dec 2024 16:03:47 +0100 Subject: minor cleanup --- src/crepe/api/Script.h | 1 - src/crepe/manager/SaveManager.h | 7 ------- 2 files changed, 8 deletions(-) (limited to 'src/crepe/manager') diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 0d59ab6..3417bb4 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -2,7 +2,6 @@ #include -#include "../ValueBroker.h" #include "../manager/EventManager.h" #include "../manager/Mediator.h" #include "../system/CollisionSystem.h" diff --git a/src/crepe/manager/SaveManager.h b/src/crepe/manager/SaveManager.h index d13a97a..1b55a22 100644 --- a/src/crepe/manager/SaveManager.h +++ b/src/crepe/manager/SaveManager.h @@ -91,16 +91,9 @@ private: template T deserialize(const std::string & value) const noexcept; -public: - SaveManager(const SaveManager &) = delete; - SaveManager(SaveManager &&) = delete; - SaveManager & operator=(const SaveManager &) = delete; - SaveManager & operator=(SaveManager &&) = delete; - protected: //! Create or return DB virtual DB & get_db(); - private: //! Database std::unique_ptr db = nullptr; -- cgit v1.2.3 From 63bcd54e0e0ea64cfef5950568b4253d5dae5c1e Mon Sep 17 00:00:00 2001 From: heavydemon21 Date: Sun, 8 Dec 2024 14:29:25 +0100 Subject: removed singleton from SDLContext, problem now is cannot call functionalities in the constructor sprite and animator and texture because it can only be filled at rendersystem call --- src/crepe/api/Config.h | 2 +- src/crepe/api/LoopManager.h | 7 ++-- src/crepe/api/LoopTimer.cpp | 3 +- src/crepe/api/Sprite.cpp | 12 +++---- src/crepe/api/Sprite.h | 5 +-- src/crepe/api/Texture.cpp | 23 ++++--------- src/crepe/api/Texture.h | 15 ++++----- src/crepe/facade/SDLContext.cpp | 18 +++++----- src/crepe/facade/SDLContext.h | 62 +++++++++++++++-------------------- src/crepe/manager/EventManager.cpp | 2 ++ src/crepe/manager/Mediator.h | 4 +-- src/crepe/manager/ResourceManager.cpp | 2 +- src/crepe/manager/SceneManager.cpp | 3 ++ src/crepe/system/InputSystem.cpp | 6 +++- src/crepe/system/RenderSystem.cpp | 11 +++++++ src/example/rendering_particle.cpp | 9 +++-- 16 files changed, 95 insertions(+), 89 deletions(-) (limited to 'src/crepe/manager') diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 6472270..73c9a4e 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -26,7 +26,7 @@ struct Config final { * * Only messages with equal or higher priority than this value will be logged. */ - Log::Level level = Log::Level::INFO; + Log::Level level = Log::Level::TRACE; /** * \brief Colored log output * diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index d8910a0..8a30602 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -8,6 +8,7 @@ #include "../system/System.h" #include "LoopTimer.h" +#include "manager/ResourceManager.h" namespace crepe { @@ -96,8 +97,10 @@ private: //! Scene manager instance SceneManager scene_manager{mediator}; - //! SDL context \todo no more singletons! - SDLContext & sdl_context = SDLContext::get_instance(); + SDLContext sdl_context {mediator}; + + ResourceManager res_man {mediator}; + //! Loop timer \todo no more singletons! LoopTimer & loop_timer = LoopTimer::get_instance(); diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp index 15a0e3a..40fc94e 100644 --- a/src/crepe/api/LoopTimer.cpp +++ b/src/crepe/api/LoopTimer.cpp @@ -1,6 +1,5 @@ #include -#include "../facade/SDLContext.h" #include "../util/Log.h" #include "LoopTimer.h" @@ -67,7 +66,7 @@ void LoopTimer::enforce_frame_rate() { = std::chrono::duration_cast(this->frame_target_time - frame_duration); if (delay_time.count() > 0) { - SDLContext::get_instance().delay(delay_time.count()); + //SDLContext::get_instance().delay(delay_time.count()); } } diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp index cc0e20a..bae5ad9 100644 --- a/src/crepe/api/Sprite.cpp +++ b/src/crepe/api/Sprite.cpp @@ -2,25 +2,25 @@ #include #include "../util/Log.h" +#include "api/Asset.h" #include "Component.h" #include "Sprite.h" -#include "Texture.h" #include "types.h" using namespace std; using namespace crepe; -Sprite::Sprite(game_object_id_t id, Texture & texture, const Sprite::Data & data) +Sprite::Sprite(game_object_id_t id, const Asset & texture, const Sprite::Data & data) : Component(id), - texture(std::move(texture)), + source(texture), data(data) { dbg_trace(); - this->mask.w = this->texture.get_size().x; - this->mask.h = this->texture.get_size().y; - this->aspect_ratio = static_cast(this->mask.w) / this->mask.h; + //this->mask.w = this->texture.get_size().x; + //this->mask.h = this->texture.get_size().y; + //this->aspect_ratio = static_cast(this->mask.w) / this->mask.h; } Sprite::~Sprite() { dbg_trace(); } diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h index dbf41e4..ec120c0 100644 --- a/src/crepe/api/Sprite.h +++ b/src/crepe/api/Sprite.h @@ -4,6 +4,7 @@ #include "Color.h" #include "Texture.h" +#include "api/Asset.h" #include "types.h" namespace crepe { @@ -74,11 +75,11 @@ public: * \param texture asset of the image * \param ctx all the sprite data */ - Sprite(game_object_id_t id, Texture & texture, const Data & data); + Sprite(game_object_id_t id, const Asset & texture, const Data & data); ~Sprite(); //! Texture used for the sprite - const Texture texture; + const Asset source; Data data; diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp index 2b56271..9d8e02d 100644 --- a/src/crepe/api/Texture.cpp +++ b/src/crepe/api/Texture.cpp @@ -1,16 +1,16 @@ -#include "../facade/SDLContext.h" #include "../util/Log.h" #include "Asset.h" +#include "Resource.h" #include "Texture.h" #include "types.h" +#include using namespace crepe; using namespace std; -Texture::Texture(const Asset & src) { +Texture::Texture(const Asset & src) : Resource(src) { dbg_trace(); - this->load(src); } Texture::~Texture() { @@ -18,21 +18,12 @@ Texture::~Texture() { this->texture.reset(); } -Texture::Texture(Texture && other) noexcept : texture(std::move(other.texture)) {} - -Texture & Texture::operator=(Texture && other) noexcept { - if (this != &other) { - texture = std::move(other.texture); - } - return *this; -} - -void Texture::load(const Asset & res) { - SDLContext & ctx = SDLContext::get_instance(); - this->texture = ctx.texture_from_path(res.get_path()); +void Texture::load(std::unique_ptr> texture) { + this->texture = std::move(texture); + this->loaded = true; } ivec2 Texture::get_size() const { if (this->texture == nullptr) return {}; - return SDLContext::get_instance().get_size(*this); + return {}; } diff --git a/src/crepe/api/Texture.h b/src/crepe/api/Texture.h index 1817910..f9c7919 100644 --- a/src/crepe/api/Texture.h +++ b/src/crepe/api/Texture.h @@ -8,6 +8,7 @@ #include #include "Asset.h" +#include "Resource.h" #include "types.h" namespace crepe { @@ -22,7 +23,7 @@ class Animator; * 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 { +class Texture : public Resource { public: /** @@ -35,12 +36,6 @@ public: * \brief Destroys the Texture instance, freeing associated resources. */ ~Texture(); - // FIXME: this constructor shouldn't be necessary because this class doesn't manage memory - - Texture(Texture && other) noexcept; - Texture & operator=(Texture && other) noexcept; - Texture(const Texture &) = delete; - Texture & operator=(const Texture &) = delete; /** * \brief Gets the width and height of the texture. @@ -53,12 +48,14 @@ private: * \brief Loads the texture from an Asset resource. * \param res Unique pointer to an Asset resource to load the texture from. */ - void load(const Asset & res); - + void load(std::unique_ptr> texture); + private: //! The texture of the class from the library std::unique_ptr> texture; + bool loaded = false; + //! Grants SDLContext access to private members. friend class SDLContext; diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 4cc2206..85257d6 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -22,19 +23,16 @@ #include "../util/Log.h" #include "SDLContext.h" +#include "manager/Manager.h" +#include "manager/Mediator.h" #include "types.h" using namespace crepe; using namespace std; -SDLContext & SDLContext::get_instance() { - static SDLContext instance; - return instance; -} - -SDLContext::SDLContext() { +SDLContext::SDLContext(Mediator & mediator) : Manager(mediator){ dbg_trace(); - + mediator.sdl_context = *this; if (SDL_Init(SDL_INIT_VIDEO) != 0) { throw runtime_error(format("SDLContext: SDL_Init error: {}", SDL_GetError())); } @@ -261,6 +259,8 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const { void SDLContext::draw(const RenderContext & ctx) { + if (!ctx.texture.loaded) ctx.texture.load(this->texture_from_path(ctx.sprite.source.get_path())); + const Sprite::Data & data = ctx.sprite.data; SDL_RendererFlip render_flip = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * data.flip.flip_x) @@ -276,8 +276,8 @@ void SDLContext::draw(const RenderContext & ctx) { double angle = ctx.angle + data.angle_offset; - this->set_color_texture(ctx.sprite.texture, ctx.sprite.data.color); - SDL_RenderCopyExF(this->game_renderer.get(), ctx.sprite.texture.texture.get(), &srcrect, + this->set_color_texture(ctx.texture, ctx.sprite.data.color); + SDL_RenderCopyExF(this->game_renderer.get(), ctx.texture.texture.get(), &srcrect, &dstrect, angle, NULL, render_flip); } diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index e232511..d95ebec 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -14,14 +14,16 @@ #include "api/Color.h" #include "api/KeyCodes.h" #include "api/Sprite.h" -#include "api/Texture.h" #include "api/Transform.h" + +#include "manager/Manager.h" +#include "manager/Mediator.h" #include "types.h" namespace crepe { -class LoopManager; -class InputSystem; +class Texture; + /** * \class SDLContext * \brief Facade for the SDL library @@ -29,7 +31,7 @@ class InputSystem; * 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 { +class SDLContext : public Manager { public: //! data that the camera component cannot hold struct CameraValues { @@ -62,6 +64,7 @@ public: //! rendering data needed to render on screen struct RenderContext { const Sprite & sprite; + Texture & texture; const CameraValues & cam; const vec2 & pos; const double & angle; @@ -92,20 +95,27 @@ public: float scroll_delta = INFINITY; ivec2 rel_mouse_move = {-1, -1}; }; - /** - * \brief Gets the singleton instance of SDLContext. - * \return Reference to the SDLContext instance. - */ - static SDLContext & get_instance(); +public: SDLContext(const SDLContext &) = delete; SDLContext(SDLContext &&) = delete; SDLContext & operator=(const SDLContext &) = delete; SDLContext & operator=(SDLContext &&) = delete; -private: - //! will only use get_events - friend class InputSystem; +public: + /** + * \brief Constructs an SDLContext instance. + * Initializes SDL, creates a window and renderer. + */ + SDLContext(Mediator & mediator); + + /** + * \brief Destroys the SDLContext instance. + * Cleans up SDL resources, including the window and renderer. + */ + ~SDLContext(); + +public: /** * \brief Retrieves a list of all events from the SDL context. * @@ -139,9 +149,7 @@ private: */ MouseButton sdl_to_mousebutton(Uint8 sdl_button); -private: - //! Will only use delay - friend class LoopTimer; +public: /** * \brief Gets the current SDL ticks since the program started. * \return Current ticks in milliseconds as a constant uint64_t. @@ -157,23 +165,8 @@ private: */ void delay(int ms) const; -private: - /** - * \brief Constructs an SDLContext instance. - * Initializes SDL, creates a window and renderer. - */ - SDLContext(); - - /** - * \brief Destroys the SDLContext instance. - * Cleans up SDL resources, including the window and renderer. - */ - ~SDLContext(); - -private: - //! Will use the funtions: texture_from_path, get_width,get_height. - friend class Texture; +public: /** * \brief Loads a texture from a file path. * \param path Path to the image file. @@ -188,10 +181,7 @@ private: */ ivec2 get_size(const Texture & ctx); -private: - //! Will use draw,clear_screen, present_screen, camera. - friend class RenderSystem; - +public: /** * \brief Draws a sprite to the screen using the specified transform and camera. * \param RenderContext Reference to rendering data to draw @@ -210,7 +200,7 @@ private: */ CameraValues set_camera(const Camera & camera); -private: +public: //! the data needed to construct a sdl dst rectangle struct DestinationRectangleData { const Sprite & sprite; diff --git a/src/crepe/manager/EventManager.cpp b/src/crepe/manager/EventManager.cpp index 20f0dd3..17fe528 100644 --- a/src/crepe/manager/EventManager.cpp +++ b/src/crepe/manager/EventManager.cpp @@ -1,9 +1,11 @@ #include "EventManager.h" +#include "util/Log.h" using namespace crepe; using namespace std; EventManager & EventManager::get_instance() { + dbg_trace(); static EventManager instance; return instance; } diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index ba0b41f..6a9e113 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -3,7 +3,6 @@ #include "../util/OptionalRef.h" // TODO: remove these singletons: -#include "../facade/SDLContext.h" #include "EventManager.h" #include "SaveManager.h" #include "api/LoopTimer.h" @@ -13,6 +12,7 @@ namespace crepe { class ComponentManager; class SceneManager; class ResourceManager; +class SDLContext; /** * Struct to pass references to classes that would otherwise need to be singletons down to @@ -27,12 +27,12 @@ class ResourceManager; * \warning This class should never be directly accessible from the API */ struct Mediator { + OptionalRef sdl_context; OptionalRef component_manager; OptionalRef scene_manager; OptionalRef save_manager = SaveManager::get_instance(); OptionalRef event_manager = EventManager::get_instance(); OptionalRef resource_manager; - OptionalRef sdl_context = SDLContext::get_instance(); OptionalRef timer = LoopTimer::get_instance(); }; diff --git a/src/crepe/manager/ResourceManager.cpp b/src/crepe/manager/ResourceManager.cpp index 7c01808..a141a46 100644 --- a/src/crepe/manager/ResourceManager.cpp +++ b/src/crepe/manager/ResourceManager.cpp @@ -6,8 +6,8 @@ using namespace crepe; using namespace std; ResourceManager::ResourceManager(Mediator & mediator) : Manager(mediator) { - mediator.resource_manager = *this; dbg_trace(); + mediator.resource_manager = *this; } ResourceManager::~ResourceManager() { dbg_trace(); } diff --git a/src/crepe/manager/SceneManager.cpp b/src/crepe/manager/SceneManager.cpp index 50a9fbb..a788c51 100644 --- a/src/crepe/manager/SceneManager.cpp +++ b/src/crepe/manager/SceneManager.cpp @@ -4,10 +4,13 @@ #include "ComponentManager.h" #include "SceneManager.h" +#include "util/Log.h" + using namespace crepe; using namespace std; SceneManager::SceneManager(Mediator & mediator) : Manager(mediator) { + dbg_trace(); mediator.scene_manager = *this; } diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp index aaa8bdf..b36ec09 100644 --- a/src/crepe/system/InputSystem.cpp +++ b/src/crepe/system/InputSystem.cpp @@ -1,15 +1,19 @@ #include "../api/Button.h" #include "../manager/ComponentManager.h" #include "../manager/EventManager.h" +#include "facade/SDLContext.h" +#include "util/Log.h" #include "InputSystem.h" using namespace crepe; void InputSystem::update() { + dbg_trace(); ComponentManager & mgr = this->mediator.component_manager; EventManager & event_mgr = this->mediator.event_manager; - std::vector event_list = SDLContext::get_instance().get_events(); + SDLContext & context = this->mediator.sdl_context; + std::vector event_list = context.get_events(); RefVector