diff options
Diffstat (limited to 'src/crepe')
82 files changed, 1304 insertions, 504 deletions
diff --git a/src/crepe/Component.cpp b/src/crepe/Component.cpp index acfd35c..ae76e65 100644 --- a/src/crepe/Component.cpp +++ b/src/crepe/Component.cpp @@ -1,5 +1,17 @@ #include "Component.h" using namespace crepe; +using namespace std; Component::Component(game_object_id_t id) : game_object_id(id) {} + +Component & Component::operator=(const Component & other) { + this->active = other.active; + return *this; +} + +unique_ptr<Component> Component::save() const { + return unique_ptr<Component>(new Component(*this)); +} + +void Component::restore(const Component & snapshot) { *this = snapshot; } diff --git a/src/crepe/Component.h b/src/crepe/Component.h index eff5a58..52e06d5 100644 --- a/src/crepe/Component.h +++ b/src/crepe/Component.h @@ -1,5 +1,7 @@ #pragma once +#include <memory> + #include "types.h" namespace crepe { @@ -32,11 +34,33 @@ protected: //! Only ComponentManager can create components friend class ComponentManager; - Component(const Component &) = delete; + // components are never moved Component(Component &&) = delete; - virtual Component & operator=(const Component &) = delete; virtual Component & operator=(Component &&) = delete; +protected: + /** + * \name ReplayManager (Memento) functions + * \{ + */ + /** + * \brief Save a snapshot of this component's state + * \note This function should only be implemented on components that should be saved/restored + * by ReplayManager. + * \returns Unique pointer to a deep copy of this component + */ + virtual std::unique_ptr<Component> save() const; + //! Copy constructor (used by \c save()) + Component(const Component &) = default; + /** + * \brief Restore this component from a snapshot + * \param snapshot Data to fill this component with (as returned by \c save()) + */ + virtual void restore(const Component & snapshot); + //! Copy assignment operator (used by \c restore()) + virtual Component & operator=(const Component &); + //! \} + public: virtual ~Component() = default; diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp index 485a0d4..b340826 100644 --- a/src/crepe/Particle.cpp +++ b/src/crepe/Particle.cpp @@ -2,8 +2,8 @@ using namespace crepe; -void Particle::reset(uint32_t lifespan, const vec2 & position, const vec2 & velocity, - double angle) { +void Particle::reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity, + float angle) { // Initialize the particle state this->time_in_life = 0; this->lifespan = lifespan; @@ -15,16 +15,17 @@ void Particle::reset(uint32_t lifespan, const vec2 & position, const vec2 & velo this->force_over_time = {0, 0}; } -void Particle::update() { +void Particle::update(double dt) { // Deactivate particle if it has exceeded its lifespan - if (++time_in_life >= lifespan) { + time_in_life += dt; + if (time_in_life >= lifespan) { this->active = false; return; } // Update velocity based on accumulated force and update position - this->velocity += force_over_time; - this->position += velocity; + this->velocity += force_over_time * dt; + this->position += velocity * dt; } void Particle::stop_movement() { diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h index d0397c9..ee0cd66 100644 --- a/src/crepe/Particle.h +++ b/src/crepe/Particle.h @@ -14,8 +14,6 @@ namespace crepe { * can also be reset or stopped. */ class Particle { - // TODO: add friend particleSsytem and rendersystem. Unit test will fail. - public: //! Position of the particle in 2D space. vec2 position; @@ -24,13 +22,13 @@ public: //! Accumulated force affecting the particle over time. vec2 force_over_time; //! Total lifespan of the particle in milliseconds. - uint32_t lifespan; + float lifespan; //! Active state of the particle; true if it is in use, false otherwise. bool active = false; //! The time the particle has been alive, in milliseconds. - uint32_t time_in_life = 0; + float time_in_life = 0; //! The angle at which the particle is oriented or moving. - double angle = 0; + float angle = 0; /** * \brief Resets the particle with new properties. @@ -43,14 +41,16 @@ public: * \param velocity The initial velocity of the particle. * \param angle The angle of the particle's trajectory or orientation. */ - void reset(uint32_t lifespan, const vec2 & position, const vec2 & velocity, double angle); + void reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity, + float angle); /** * \brief Updates the particle's state. * * Advances the particle's position based on its velocity and applies accumulated forces. * Deactivates the particle if its lifespan has expired. + * \param dt The amount of fixed delta time that has passed. */ - void update(); + void update(double dt); /** * \brief Stops the particle's movement. * diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp index 4ce4bf0..203cef3 100644 --- a/src/crepe/api/Animator.cpp +++ b/src/crepe/api/Animator.cpp @@ -1,5 +1,5 @@ -#include "util/Log.h" +#include "util/dbg.h" #include "Animator.h" #include "Component.h" diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h index bfd0ac7..d802e83 100644 --- a/src/crepe/api/Asset.h +++ b/src/crepe/api/Asset.h @@ -43,13 +43,13 @@ private: /** * \brief Locate asset path, or throw exception if it cannot be found * - * This function resolves asset locations relative to crepe::Config::root_pattern if it is + * This function resolves asset locations relative to Config::asset::root_pattern if it is * set and \p src is a relative path. If \p src is an absolute path, it is canonicalized. * This function only returns if the file can be found. * * \param src Arbitrary path to resource file * - * \returns \p src if crepe::Config::root_pattern is empty + * \returns \p src if Config::asset::root_pattern is empty * \returns Canonical path to \p src * * \throws std::runtime_error if root_pattern cannot be found diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp index d22afdf..af7572c 100644 --- a/src/crepe/api/BehaviorScript.cpp +++ b/src/crepe/api/BehaviorScript.cpp @@ -10,6 +10,6 @@ BehaviorScript::BehaviorScript(game_object_id_t id, Mediator & mediator) template <> BehaviorScript & GameObject::add_component<BehaviorScript>() { - ComponentManager & mgr = this->component_manager; - return mgr.add_component<BehaviorScript>(this->id, mgr.mediator); + ComponentManager & mgr = this->mediator.component_manager; + return mgr.add_component<BehaviorScript>(this->id, this->mediator); } diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp index b9bb1e2..353d5e2 100644 --- a/src/crepe/api/BehaviorScript.hpp +++ b/src/crepe/api/BehaviorScript.hpp @@ -2,8 +2,6 @@ #include <type_traits> -#include "../util/Log.h" - #include "BehaviorScript.h" #include "Script.h" @@ -11,7 +9,6 @@ namespace crepe { template <class T, typename... Args> BehaviorScript & BehaviorScript::set_script(Args &&... args) { - dbg_trace(); static_assert(std::is_base_of<Script, T>::value); this->script = std::unique_ptr<Script>(new T(std::forward<Args>(args)...)); diff --git a/src/crepe/api/BoxCollider.cpp b/src/crepe/api/BoxCollider.cpp index c097a24..a893d41 100644 --- a/src/crepe/api/BoxCollider.cpp +++ b/src/crepe/api/BoxCollider.cpp @@ -4,7 +4,7 @@ using namespace crepe; -BoxCollider::BoxCollider(game_object_id_t game_object_id, const vec2 & offset, - const vec2 & dimensions) +BoxCollider::BoxCollider(game_object_id_t game_object_id, const vec2 & dimensions, + const vec2 & offset) : Collider(game_object_id, offset), dimensions(dimensions) {} diff --git a/src/crepe/api/BoxCollider.h b/src/crepe/api/BoxCollider.h index 1ac4d46..3835e2c 100644 --- a/src/crepe/api/BoxCollider.h +++ b/src/crepe/api/BoxCollider.h @@ -13,7 +13,7 @@ namespace crepe { */ class BoxCollider : public Collider { public: - BoxCollider(game_object_id_t game_object_id, const vec2 & offset, const vec2 & dimensions); + BoxCollider(game_object_id_t game_object_id, const vec2 & dimensions, const vec2 & offset = { 0, 0 }); //! Width and height of the box collider vec2 dimensions; diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index fb11c8d..18d6942 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -13,13 +13,14 @@ target_sources(crepe PUBLIC Animator.cpp BoxCollider.cpp CircleCollider.cpp - LoopManager.cpp + Engine.cpp Asset.cpp EventHandler.cpp Script.cpp Button.cpp UIObject.cpp AI.cpp + Scene.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -36,6 +37,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Vector2.hpp Color.h Scene.h + Scene.hpp Metadata.h Camera.h Animator.h @@ -44,7 +46,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES EventHandler.h EventHandler.hpp Event.h - LoopManager.h + Engine.h + Engine.hpp Asset.h Button.h UIObject.h diff --git a/src/crepe/api/Camera.cpp b/src/crepe/api/Camera.cpp index 179dc18..9befc3f 100644 --- a/src/crepe/api/Camera.cpp +++ b/src/crepe/api/Camera.cpp @@ -1,4 +1,4 @@ -#include "util/Log.h" +#include "util/dbg.h" #include "Camera.h" #include "Component.h" @@ -6,10 +6,8 @@ using namespace crepe; -Camera::Camera(game_object_id_t id, const ivec2 & screen, const vec2 & viewport_size, - const Data & data) +Camera::Camera(game_object_id_t id, const vec2 & viewport_size, const Data & data) : Component(id), - screen(screen), viewport_size(viewport_size), data(data) { dbg_trace(); diff --git a/src/crepe/api/Camera.h b/src/crepe/api/Camera.h index 54d9a73..2fea9f3 100644 --- a/src/crepe/api/Camera.h +++ b/src/crepe/api/Camera.h @@ -30,7 +30,7 @@ public: * zoom < 1 --> zoom out * zoom > 1 --> zoom in */ - double zoom = 1; + float zoom = 1.0; //! offset postion from the game object transform component vec2 postion_offset; @@ -40,19 +40,14 @@ public: /** * \brief Constructs a Camera with the specified ID and background color. * \param id Unique identifier for the camera component. - * \param screen is the actual screen size in pixels * \param viewport_size is the view of the world in game units * \param data the camera component data */ - Camera(game_object_id_t id, const ivec2 & screen, const vec2 & viewport_size, - const Camera::Data & data); + Camera(game_object_id_t id, const vec2 & viewport_size, const Data & data); ~Camera(); // dbg_trace only public: - Camera::Data data; - - //! screen the display size in pixels ( output resolution ) - const ivec2 screen; + Data data; //! viewport is the area of the world visible through the camera (in world units) const vec2 viewport_size; diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index ed1cf38..3cc67c2 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -3,24 +3,21 @@ #include <string> #include "../util/Log.h" - -#include "types.h" +#include "../types.h" namespace crepe { /** * \brief Global configuration interface * - * This class stores engine default settings. Properties on this class are only supposed to be - * modified *before* execution is handed over from the game programmer to the engine (i.e. the - * main loop is started). + * This struct stores both engine default settings and global configuration parameters. */ struct Config final { //! Retrieve handle to global Config instance static Config & get_instance(); //! Logging-related settings - struct { + struct log { // NOLINT /** * \brief Log level * @@ -28,7 +25,7 @@ struct Config final { */ Log::Level level = Log::Level::INFO; /** - * \brief Colored log output + * \brief Enable colored log output * * Enables log coloring using ANSI escape codes. */ @@ -36,7 +33,7 @@ struct Config final { } log; //! Save manager - struct { + struct savemgr { // NOLINT /** * \brief Save file location * @@ -46,8 +43,8 @@ struct Config final { std::string location = "save.crepe.db"; } savemgr; - //! physics-related settings - struct { + //! Physics-related settings + struct physics { // NOLINT /** * \brief gravity value of physics system * @@ -56,15 +53,16 @@ struct Config final { float gravity = 10; } physics; - //! default window settings - struct { - //! default screen size in pixels - ivec2 default_size = {1280, 720}; - std::string window_title = "Jetpack joyride clone"; - } window_settings; + //! Default window settings + struct window { // NOLINT + //! Default window size (in pixels) + ivec2 size = {1280, 720}; + //! Default window title + std::string title = "Jetpack joyride clone"; + } window; //! Asset loading options - struct { + struct asset { // NOLINT /** * \brief Pattern to match for Asset base directory * diff --git a/src/crepe/api/Engine.cpp b/src/crepe/api/Engine.cpp new file mode 100644 index 0000000..bbb4494 --- /dev/null +++ b/src/crepe/api/Engine.cpp @@ -0,0 +1,63 @@ +#include "../util/Log.h" + +#include "Engine.h" + +using namespace crepe; +using namespace std; + +int Engine::main() noexcept { + try { + this->setup(); + } catch (const exception & e) { + Log::logf(Log::Level::ERROR, "Uncaught exception in setup: {}\n", e.what()); + return EXIT_FAILURE; + } + + try { + this->loop(); + } catch (const exception & e) { + Log::logf(Log::Level::ERROR, "Uncaught exception in main loop: {}\n", e.what()); + this->event_manager.trigger_event<ShutDownEvent>(); + } + + return EXIT_SUCCESS; +} + +void Engine::setup() { + this->loop_timer.start(); + this->scene_manager.load_next_scene(); + + this->event_manager.subscribe<ShutDownEvent>([this](const ShutDownEvent & event) { + this->game_running = false; + + // propagate to possible user ShutDownEvent listeners + return false; + }); +} + +void Engine::loop() { + LoopTimerManager & timer = this->loop_timer; + SystemManager & systems = this->system_manager; + + while (game_running) { + timer.update(); + + while (timer.get_lag() >= timer.get_fixed_delta_time()) { + try { + systems.fixed_update(); + } catch (const exception & e) { + Log::logf(Log::Level::WARNING, + "Uncaught exception in fixed update function: {}\n", e.what()); + } + timer.advance_fixed_elapsed_time(); + } + + try { + systems.frame_update(); + } catch (const exception & e) { + Log::logf(Log::Level::WARNING, "Uncaught exception in frame update function: {}\n", + e.what()); + } + timer.enforce_frame_rate(); + } +} diff --git a/src/crepe/api/Engine.h b/src/crepe/api/Engine.h new file mode 100644 index 0000000..3145723 --- /dev/null +++ b/src/crepe/api/Engine.h @@ -0,0 +1,78 @@ +#pragma once + +#include "../facade/SDLContext.h" +#include "../manager/ComponentManager.h" +#include "../manager/EventManager.h" +#include "../manager/LoopTimerManager.h" +#include "../manager/ReplayManager.h" +#include "../manager/ResourceManager.h" +#include "../manager/SaveManager.h" +#include "../manager/SceneManager.h" +#include "../manager/SystemManager.h" + +namespace crepe { + +/** + * \brief Main game entrypoint + * + * This class is responsible for managing the game loop, including initialization and updating. + */ +class Engine { +public: + /** + * \brief Engine entrypoint + * + * This function is called by the game programmer after registering all scenes + * + * \returns process exit code + */ + int main() noexcept; + + //! \copydoc SceneManager::add_scene + template <typename T> + void add_scene(); + +private: + /** + * \brief Setup function for one-time initialization. + * + * This function initializes necessary components for the game. + */ + void setup(); + /** + * \brief Main game loop function. + * + * This function runs the main loop, handling game updates and rendering. + */ + void loop(); + + //! Game loop condition + bool game_running = true; + +private: + //! Global context + Mediator mediator; + + //! Component manager instance + ComponentManager component_manager{mediator}; + //! Scene manager instance + SceneManager scene_manager{mediator}; + //! LoopTimerManager instance + LoopTimerManager loop_timer{mediator}; + //! EventManager instance + EventManager event_manager{mediator}; + //! Resource manager instance + ResourceManager resource_manager{mediator}; + //! Save manager instance + SaveManager save_manager{mediator}; + //! SDLContext instance + SDLContext sdl_context{mediator}; + //! ReplayManager instance + ReplayManager replay_manager{mediator}; + //! SystemManager + SystemManager system_manager{mediator}; +}; + +} // namespace crepe + +#include "Engine.hpp" diff --git a/src/crepe/api/Engine.hpp b/src/crepe/api/Engine.hpp new file mode 100644 index 0000000..f2fdc0a --- /dev/null +++ b/src/crepe/api/Engine.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "Engine.h" + +namespace crepe { + +template <class T> +void Engine::add_scene() { + this->scene_manager.add_scene<T>(); +} + +} // namespace crepe diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp index 9ef4682..9b94cad 100644 --- a/src/crepe/api/GameObject.cpp +++ b/src/crepe/api/GameObject.cpp @@ -7,20 +7,17 @@ using namespace crepe; using namespace std; -GameObject::GameObject(ComponentManager & component_manager, game_object_id_t id, - const std::string & name, const std::string & tag, - const vec2 & position, double rotation, double scale) +GameObject::GameObject(Mediator & mediator, game_object_id_t id, const std::string & name, + const std::string & tag, const vec2 & position, double rotation, + double scale) : id(id), - component_manager(component_manager) { - - // Add Transform and Metadata components - ComponentManager & mgr = this->component_manager; - mgr.add_component<Transform>(this->id, position, rotation, scale); - mgr.add_component<Metadata>(this->id, name, tag); -} + mediator(mediator), + transform(mediator.component_manager->add_component<Transform>(this->id, position, + rotation, scale)), + metadata(mediator.component_manager->add_component<Metadata>(this->id, name, tag)) {} void GameObject::set_parent(const GameObject & parent) { - ComponentManager & mgr = this->component_manager; + ComponentManager & mgr = this->mediator.component_manager; // Set parent on own Metadata component RefVector<Metadata> this_metadata = mgr.get_components_by_id<Metadata>(this->id); @@ -32,7 +29,7 @@ void GameObject::set_parent(const GameObject & parent) { } void GameObject::set_persistent(bool persistent) { - ComponentManager & mgr = this->component_manager; + ComponentManager & mgr = this->mediator.component_manager; mgr.set_persistent(this->id, persistent); } diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h index ff80f49..572ce3a 100644 --- a/src/crepe/api/GameObject.h +++ b/src/crepe/api/GameObject.h @@ -6,7 +6,9 @@ namespace crepe { -class ComponentManager; +class Mediator; +class Transform; +class Metadata; /** * \brief Represents a GameObject @@ -20,7 +22,7 @@ private: * This constructor creates a new GameObject. It creates a new Transform and Metadata * component and adds them to the ComponentManager. * - * \param component_manager Reference to component_manager + * \param mediator Reference to mediator * \param id The id of the GameObject * \param name The name of the GameObject * \param tag The tag of the GameObject @@ -28,13 +30,20 @@ private: * \param rotation The rotation of the GameObject * \param scale The scale of the GameObject */ - GameObject(ComponentManager & component_manager, game_object_id_t id, - const std::string & name, const std::string & tag, const vec2 & position, - double rotation, double scale); + GameObject(Mediator & mediator, game_object_id_t id, const std::string & name, + const std::string & tag, const vec2 & position, double rotation, double scale); //! ComponentManager instances GameObject friend class ComponentManager; public: + //! The id of the GameObject + const game_object_id_t id; + //! This entity's transform + Transform & transform; + //! This entity's metadata + Metadata & metadata; + +public: /** * \brief Set the parent of this GameObject * @@ -68,12 +77,8 @@ public: */ void set_persistent(bool persistent = true); -public: - //! The id of the GameObject - const game_object_id_t id; - protected: - ComponentManager & component_manager; + Mediator & mediator; }; } // namespace crepe diff --git a/src/crepe/api/GameObject.hpp b/src/crepe/api/GameObject.hpp index a6b45b0..69f7d73 100644 --- a/src/crepe/api/GameObject.hpp +++ b/src/crepe/api/GameObject.hpp @@ -8,7 +8,7 @@ namespace crepe { template <typename T, typename... Args> T & GameObject::add_component(Args &&... args) { - ComponentManager & mgr = this->component_manager; + ComponentManager & mgr = this->mediator.component_manager; return mgr.add_component<T>(this->id, std::forward<Args>(args)...); } diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h index dc3219a..748e8f6 100644 --- a/src/crepe/api/KeyCodes.h +++ b/src/crepe/api/KeyCodes.h @@ -3,6 +3,7 @@ #include <unordered_map> namespace crepe { + //! Enumeration for mouse button inputs, including standard and extended buttons. enum class MouseButton { NONE = 0, //!< No mouse button input. @@ -155,4 +156,6 @@ enum class Keycode { MENU = 348, //!< Menu key. }; +typedef std::unordered_map<Keycode, bool> keyboard_state_t; + } // namespace crepe diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index b5e5ff7..7a78019 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -65,6 +65,7 @@ void LoopManager::fixed_update() { this->get_system<InputSystem>().update(); this->event_manager.dispatch_events(); this->get_system<ScriptSystem>().update(); + this->get_system<ParticleSystem>().update(); this->get_system<AISystem>().update(); this->get_system<PhysicsSystem>().update(); this->get_system<CollisionSystem>().update(); diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h deleted file mode 100644 index 40e6b38..0000000 --- a/src/crepe/api/LoopManager.h +++ /dev/null @@ -1,122 +0,0 @@ -#pragma once - -#include <memory> - -#include "../facade/SDLContext.h" -#include "../manager/ComponentManager.h" -#include "../manager/EventManager.h" -#include "../manager/LoopTimerManager.h" -#include "../manager/Mediator.h" -#include "../manager/ResourceManager.h" -#include "../manager/SaveManager.h" -#include "../manager/SceneManager.h" -#include "../system/System.h" - -namespace crepe { -/** - * \brief Main game loop manager - * - * This class is responsible for managing the game loop, including initialization and updating. - */ -class LoopManager { -public: - LoopManager(); - /** - * \brief Start the gameloop - * - * This is the start of the engine where the setup is called and then the loop keeps running until the game stops running. - * The Game programmer needs to call this function to run the game. This should be done after creating and adding all scenes. - */ - void start(); - - /** - * \brief Add a new concrete scene to the scene manager - * - * \tparam T Type of concrete scene - */ - template <typename T> - void add_scene(); - -private: - /** - * \brief Setup function for one-time initialization. - * - * This function initializes necessary components for the game. - */ - void setup(); - /** - * \brief Main game loop function. - * - * This function runs the main loop, handling game updates and rendering. - */ - void loop(); - - /** - * \brief Per-frame update. - * - * Updates the game state based on the elapsed time since the last frame. - */ - virtual void frame_update(); - - /** - * \brief Fixed update executed at a fixed rate. - * - * This function updates physics and game logic based on LoopTimer's fixed_delta_time. - */ - virtual void fixed_update(); - - //! Indicates whether the game is running. - bool game_running = false; - -private: - //! Global context - Mediator mediator; - - //! Component manager instance - ComponentManager component_manager{mediator}; - //! Scene manager instance - SceneManager scene_manager{mediator}; - //! LoopTimerManager instance - LoopTimerManager loop_timer{mediator}; - //! EventManager instance - EventManager event_manager{mediator}; - //! Resource manager instance - ResourceManager resource_manager{mediator}; - //! Save manager instance - SaveManager save_manager{mediator}; - //! SDLContext instance - SDLContext sdl_context{mediator}; - -private: - /** - * \brief Callback function for ShutDownEvent - * - * This function sets the game_running variable to false, stopping the gameloop and therefor quitting the game. - */ - bool on_shutdown(const ShutDownEvent & e); - /** - * \brief Collection of System instances - * - * This map holds System instances indexed by the system's class typeid. It is filled in the - * constructor of LoopManager using LoopManager::load_system. - */ - std::unordered_map<std::type_index, std::unique_ptr<System>> systems; - /** - * \brief Initialize a system - * \tparam T System type (must be derivative of \c System) - */ - template <class T> - void load_system(); - /** - * \brief Retrieve a reference to ECS system - * \tparam T System type - * \returns Reference to system instance - * \throws std::runtime_error if the System is not initialized - */ - template <class T> - T & get_system(); -}; - -} // namespace crepe - -#include "LoopManager.hpp" diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp index 90b77a0..9a70334 100644 --- a/src/crepe/api/ParticleEmitter.cpp +++ b/src/crepe/api/ParticleEmitter.cpp @@ -1,11 +1,28 @@ #include "ParticleEmitter.h" +#include "api/Sprite.h" using namespace crepe; +using namespace std; -ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Data & data) +ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite, + const Data & data) : Component(game_object_id), + sprite(sprite), data(data) { for (size_t i = 0; i < this->data.max_particles; i++) { - this->data.particles.emplace_back(); + this->particles.emplace_back(); } } + +unique_ptr<Component> ParticleEmitter::save() const { + return unique_ptr<Component>{new ParticleEmitter(*this)}; +} + +void ParticleEmitter::restore(const Component & snapshot) { + *this = static_cast<const ParticleEmitter &>(snapshot); +} + +ParticleEmitter & ParticleEmitter::operator=(const ParticleEmitter & other) { + this->particles = other.particles; + return *this; +} diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h index b83fd61..626b356 100644 --- a/src/crepe/api/ParticleEmitter.h +++ b/src/crepe/api/ParticleEmitter.h @@ -1,7 +1,11 @@ #pragma once +#include <cmath> #include <vector> +#include "system/ParticleSystem.h" +#include "system/RenderSystem.h" + #include "Component.h" #include "Particle.h" #include "types.h" @@ -26,15 +30,18 @@ public: */ struct Boundary { //! boundary width (midpoint is emitter location) - double width = 0.0; + float width = INFINITY; //! boundary height (midpoint is emitter location) - double height = 0.0; + float height = INFINITY; //! boundary offset from particle emitter location vec2 offset; //! reset on exit or stop velocity and set max postion bool reset_on_exit = false; }; + //! sprite reference of displayed sprite + const Sprite & sprite; + /** * \brief Holds parameters that control particle emission. * @@ -45,29 +52,25 @@ public: //! position of the emitter vec2 position; //! maximum number of particles - const unsigned int max_particles = 0; - //! rate of particle emission per update (Lowest value = 0.001 any lower is ignored) - double emission_rate = 0; + const unsigned int max_particles = 256; + //! rate of particle emission per second + float emission_rate = 50; //! min speed of the particles - double min_speed = 0; + float min_speed = 100; //! min speed of the particles - double max_speed = 0; + float max_speed = 100; //! min angle of particle emission - double min_angle = 0; + float min_angle = 0; //! max angle of particle emission - double max_angle = 0; - //! begin Lifespan of particle (only visual) - double begin_lifespan = 0.0; - //! end Lifespan of particle - double end_lifespan = 0.0; + float max_angle = 0; + //! begin Lifespan of particle in seconds (only visual) + float begin_lifespan = 0.0; + //! end Lifespan of particle in seconds + float end_lifespan = 10.0; //! force over time (physics) vec2 force_over_time; //! particle boundary Boundary boundary; - //! collection of particles - std::vector<Particle> particles; - //! sprite reference - const Sprite & sprite; }; public: @@ -75,11 +78,27 @@ public: * \param game_object_id Identifier for the game object using this emitter. * \param data Configuration data defining particle properties. */ - ParticleEmitter(game_object_id_t game_object_id, const Data & data); + ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite, const Data & data); public: //! Configuration data for particle emission settings. Data data; + +protected: + virtual std::unique_ptr<Component> save() const; + ParticleEmitter(const ParticleEmitter &) = default; + virtual void restore(const Component & snapshot); + virtual ParticleEmitter & operator=(const ParticleEmitter &); + +private: + //! Only ParticleSystem can move and read particles + friend ParticleSystem; + //! Only RenderSystem can read particles + friend RenderSystem; + //! Saves time left over from last update event. + float spawn_accumulator = 0; + //! collection of particles + std::vector<Particle> particles; }; } // namespace crepe diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h index b08c8db..b2bfc0d 100644 --- a/src/crepe/api/Rigidbody.h +++ b/src/crepe/api/Rigidbody.h @@ -61,7 +61,7 @@ public: * gravity force, allowing for fine-grained control over how the object responds to gravity. * */ - float gravity_scale = 0; + float gravity_scale = 1.0; //! Defines the type of the physics body, which determines how the physics system interacts with the object. BodyType body_type = BodyType::DYNAMIC; @@ -139,7 +139,7 @@ public: * Each element represents a layer ID, and the GameObject will only detect * collisions with other GameObjects that belong to these layers. */ - std::set<int> collision_layers; + std::set<int> collision_layers = {0}; }; public: diff --git a/src/crepe/api/Scene.cpp b/src/crepe/api/Scene.cpp new file mode 100644 index 0000000..ad729d2 --- /dev/null +++ b/src/crepe/api/Scene.cpp @@ -0,0 +1,15 @@ +#include "Scene.h" + +using namespace crepe; + +SaveManager & Scene::get_save_manager() const { return mediator->save_manager; } + +GameObject Scene::new_object(const std::string & name, const std::string & tag, + const vec2 & position, double rotation, double scale) { + // Forward the call to ComponentManager's new_object method + return mediator->component_manager->new_object(name, tag, position, rotation, scale); +} + +void Scene::set_persistent(const Asset & asset, bool persistent) { + mediator->resource_manager->set_persistent(asset, persistent); +} diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h index ba9bb76..5c34b36 100644 --- a/src/crepe/api/Scene.h +++ b/src/crepe/api/Scene.h @@ -2,13 +2,19 @@ #include <string> +#include "../manager/ComponentManager.h" #include "../manager/Mediator.h" +#include "../manager/ResourceManager.h" +#include "../util/Log.h" #include "../util/OptionalRef.h" +#include "GameObject.h" + namespace crepe { class SceneManager; class ComponentManager; +class Asset; /** * \brief Represents a Scene @@ -33,12 +39,12 @@ public: * \brief Get the scene's name * \return The scene's name */ - virtual std::string get_name() const = 0; + virtual std::string get_name() const { return ""; }; // TODO: Late references should ALWAYS be private! This is currently kept as-is so unit tests // keep passing, but this reference should not be directly accessible by the user!!! -protected: +private: /** * \name Late references * @@ -53,6 +59,36 @@ protected: //! Mediator reference OptionalRef<Mediator> mediator; //! \} + +protected: + /** + * \brief Retrieve the reference to the SaveManager instance + * + * \returns A reference to the SaveManager instance held by the Mediator. + */ + SaveManager & get_save_manager() const; + + //! \copydoc ComponentManager::new_object + GameObject new_object(const std::string & name, const std::string & tag = "", + const vec2 & position = {0, 0}, double rotation = 0, + double scale = 1); + + //! \copydoc ResourceManager::set_persistent + void set_persistent(const Asset & asset, bool persistent); + /** + * \name Logging functions + * \see Log + * \{ + */ + //! \copydoc Log::logf + template <class... Args> + void logf(const Log::Level & level, std::format_string<Args...> fmt, Args &&... args); + //! \copydoc Log::logf + template <class... Args> + void logf(std::format_string<Args...> fmt, Args &&... args); + //! \} }; } // namespace crepe + +#include "Scene.hpp" diff --git a/src/crepe/api/Scene.hpp b/src/crepe/api/Scene.hpp new file mode 100644 index 0000000..14635df --- /dev/null +++ b/src/crepe/api/Scene.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "../util/Log.h" + +#include "Scene.h" + +namespace crepe { + +template <class... Args> +void Scene::logf(const Log::Level & level, std::format_string<Args...> fmt, Args &&... args) { + Log::logf(level, fmt, std::forward<Args>(args)...); +} + +template <class... Args> +void Scene::logf(std::format_string<Args...> fmt, Args &&... args) { + Log::logf(fmt, std::forward<Args>(args)...); +} + +} // namespace crepe diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index 753a9e3..d5f3f06 100644 --- a/src/crepe/api/Script.cpp +++ b/src/crepe/api/Script.cpp @@ -1,6 +1,7 @@ #include <string> #include "../manager/SceneManager.h" +#include "../facade/SDLContext.h" #include "Script.h" @@ -25,3 +26,39 @@ void Script::set_next_scene(const string & name) { } SaveManager & Script::get_save_manager() const { return this->mediator->save_manager; } + +void Script::replay::record_start() { + ReplayManager & mgr = this->mediator->replay_manager; + return mgr.record_start(); +} + +recording_t Script::replay::record_end() { + ReplayManager & mgr = this->mediator->replay_manager; + return mgr.record_end(); +} + +void Script::replay::play(recording_t recording) { + ReplayManager & mgr = this->mediator->replay_manager; + return mgr.play(recording); +} + +void Script::replay::release(recording_t recording) { + ReplayManager & mgr = this->mediator->replay_manager; + return mgr.release(recording); +} + +LoopTimerManager & Script::get_loop_timer() const { return this->mediator->loop_timer; } + +const keyboard_state_t & Script::get_keyboard_state() const { + SDLContext & sdl_context = this->mediator->sdl_context; + return sdl_context.get_keyboard_state(); +} + +bool Script::get_key_state(Keycode key) const noexcept { + try { + return this->get_keyboard_state().at(key); + } catch (...) { + return false; + } +} + diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 668e5d1..634a459 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -3,9 +3,12 @@ #include <vector> #include "../manager/EventManager.h" +#include "../manager/LoopTimerManager.h" #include "../manager/Mediator.h" +#include "../manager/ReplayManager.h" #include "../system/CollisionSystem.h" #include "../types.h" +#include "../util/Log.h" #include "../util/OptionalRef.h" namespace crepe { @@ -46,9 +49,17 @@ protected: /** * \brief Script update function (empty by default) * + * \param delta_time Time since last fixed update + * * This function is called during the ScriptSystem::update() routine if the \c BehaviorScript * component holding this script instance is active. */ + virtual void update(duration_t delta_time) { return this->update(); } + /** + * \brief Fallback script update function (empty by default) + * + * Allows the game programmer to ignore parameters passed to \c update() + */ virtual void update() {} //! \} @@ -57,84 +68,116 @@ protected: protected: /** - * \name Utility functions + * \name Component query functions + * \see ComponentManager * \{ */ - /** * \brief Get single component of type \c T on this game object - * * \tparam T Type of component - * * \returns Reference to component - * * \throws std::runtime_error if this game object does not have a component with type \c T */ template <typename T> T & get_component() const; - // TODO: make get_component calls for component types that can have more than 1 instance - // cause compile-time errors - /** * \brief Get all components of type \c T on this game object - * * \tparam T Type of component - * * \returns List of component references */ template <typename T> RefVector<T> get_components() const; - - /** - * \copydoc ComponentManager::get_components_by_id - * \see ComponentManager::get_components_by_id - */ + //! \copydoc ComponentManager::get_components_by_id template <typename T> RefVector<T> get_components_by_id(game_object_id_t id) const; - /** - * \copydoc ComponentManager::get_components_by_name - * \see ComponentManager::get_components_by_name - */ + //! \copydoc ComponentManager::get_components_by_name template <typename T> RefVector<T> get_components_by_name(const std::string & name) const; - /** - * \copydoc ComponentManager::get_components_by_tag - * \see ComponentManager::get_components_by_tag - */ + //! \copydoc ComponentManager::get_components_by_tag template <typename T> RefVector<T> get_components_by_tag(const std::string & tag) const; + //! \} /** - * \brief Log a message using Log::logf - * - * \tparam Args Log::logf parameters - * \param args Log::logf parameters + * \name Logging functions + * \see Log + * \{ */ - template <typename... Args> - void logf(Args &&... args); + //! \copydoc Log::logf + template <class... Args> + void logf(const Log::Level & level, std::format_string<Args...> fmt, Args &&... args); + //! \copydoc Log::logf + template <class... Args> + void logf(std::format_string<Args...> fmt, Args &&... args); + // \} /** - * \brief Subscribe to an event with an explicit channel - * \see EventManager::subscribe + * \name Event manager functions + * \see EventManager + * \{ */ + //! \copydoc EventManager::subscribe template <typename EventType> void subscribe(const EventHandler<EventType> & callback, event_channel_t channel); - /** - * \brief Subscribe to an event on EventManager::CHANNEL_ALL - * \see EventManager::subscribe - */ + //! \copydoc EventManager::subscribe template <typename EventType> void subscribe(const EventHandler<EventType> & callback); + //! \copydoc EventManager::trigger_event + template <typename EventType> + void trigger_event(const EventType & event = {}, + event_channel_t channel = EventManager::CHANNEL_ALL); + //! \copydoc EventManager::queue_event + template <typename EventType> + void queue_event(const EventType & event = {}, + event_channel_t channel = EventManager::CHANNEL_ALL); + //! \} /** - * \brief Set the next scene using SceneManager - * \see SceneManager::set_next_scene + * \name Scene-related functions + * \see SceneManager + * \{ */ + //! \copydoc SceneManager::set_next_scene void set_next_scene(const std::string & name); + //! \} + /** + * \name Save data management functions + * \see SaveManager + * \{ + */ //! Retrieve SaveManager reference SaveManager & get_save_manager() const; + //! \} + + //! Replay management functions + struct replay { // NOLINT + //! \copydoc ReplayManager::record_start + void record_start(); + //! \copydoc ReplayManager::record_end + recording_t record_end(); + //! \copydoc ReplayManager::play + void play(recording_t); + //! \copydoc ReplayManager::release + void release(recording_t); + + private: + OptionalRef<Mediator> & mediator; + replay(OptionalRef<Mediator> & mediator) : mediator(mediator) {} + friend class Script; + } replay{mediator}; + //! Retrieve LoopTimerManager reference + LoopTimerManager & get_loop_timer() const; + + /** + * \name Input + * \{ + */ + //! Get keyboard state + const keyboard_state_t & get_keyboard_state() const; + //! Get state of specific key + bool get_key_state(Keycode key) const noexcept; //! \} private: diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index 225a51c..4462a41 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -1,6 +1,7 @@ #pragma once #include "../manager/ComponentManager.h" +#include "../manager/ReplayManager.h" #include "BehaviorScript.h" #include "Script.h" @@ -23,9 +24,14 @@ RefVector<T> Script::get_components() const { return this->get_components_by_id<T>(this->game_object_id); } -template <typename... Args> -void Script::logf(Args &&... args) { - Log::logf(std::forward<Args>(args)...); +template <class... Args> +void Script::logf(const Log::Level & level, std::format_string<Args...> fmt, Args &&... args) { + Log::logf(level, fmt, std::forward<Args>(args)...); +} + +template <class... Args> +void Script::logf(std::format_string<Args...> fmt, Args &&... args) { + Log::logf(fmt, std::forward<Args>(args)...); } template <typename EventType> @@ -34,8 +40,18 @@ void Script::subscribe_internal(const EventHandler<EventType> & callback, EventManager & mgr = this->mediator->event_manager; subscription_t listener = mgr.subscribe<EventType>( [this, callback](const EventType & data) -> bool { + // check if (parent) BehaviorScript component is active bool & active = this->active; if (!active) return false; + + // check if replay manager is playing (if initialized) + try { + ReplayManager & replay = this->mediator->replay_manager; + if (replay.get_state() == ReplayManager::PLAYING) return false; + } catch (const std::runtime_error &) { + } + + // call user-provided callback return callback(data); }, channel); @@ -52,6 +68,18 @@ void Script::subscribe(const EventHandler<EventType> & callback) { this->subscribe_internal(callback, EventManager::CHANNEL_ALL); } +template <typename EventType> +void Script::trigger_event(const EventType & event, event_channel_t channel) { + EventManager & mgr = this->mediator->event_manager; + mgr.trigger_event(event, channel); +} + +template <typename EventType> +void Script::queue_event(const EventType & event, event_channel_t channel) { + EventManager & mgr = this->mediator->event_manager; + mgr.queue_event(event, channel); +} + template <typename T> RefVector<T> Script::get_components_by_id(game_object_id_t id) const { Mediator & mediator = this->mediator; diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp index ba684ba..0107c7b 100644 --- a/src/crepe/api/Sprite.cpp +++ b/src/crepe/api/Sprite.cpp @@ -1,6 +1,6 @@ #include <cmath> -#include "../util/Log.h" +#include "../util/dbg.h" #include "api/Asset.h" #include "Component.h" diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp index a85b792..fcfce14 100644 --- a/src/crepe/api/Transform.cpp +++ b/src/crepe/api/Transform.cpp @@ -1,8 +1,9 @@ -#include "../util/Log.h" +#include "../util/dbg.h" #include "Transform.h" using namespace crepe; +using namespace std; Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, double scale) : Component(id), @@ -11,3 +12,11 @@ Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, d scale(scale) { dbg_trace(); } + +unique_ptr<Component> Transform::save() const { + return unique_ptr<Component>{new Transform(*this)}; +} + +void Transform::restore(const Component & snapshot) { + *this = static_cast<const Transform &>(snapshot); +} diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h index 7ee6d65..a6f3486 100644 --- a/src/crepe/api/Transform.h +++ b/src/crepe/api/Transform.h @@ -35,6 +35,12 @@ protected: virtual int get_instances_max() const { return 1; } //! ComponentManager instantiates all components friend class ComponentManager; + +protected: + virtual std::unique_ptr<Component> save() const; + Transform(const Transform &) = default; + virtual void restore(const Component & snapshot); + virtual Transform & operator=(const Transform &) = default; }; } // namespace crepe diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp index 95cf606..d45cd82 100644 --- a/src/crepe/facade/DB.cpp +++ b/src/crepe/facade/DB.cpp @@ -1,6 +1,6 @@ #include <cstring> -#include "util/Log.h" +#include "util/dbg.h" #include "DB.h" diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 7e19ede..d6e3159 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -19,7 +19,7 @@ #include "../api/Color.h" #include "../api/Config.h" #include "../api/Sprite.h" -#include "../util/Log.h" +#include "../util/dbg.h" #include "manager/Mediator.h" #include "SDLContext.h" @@ -35,10 +35,10 @@ SDLContext::SDLContext(Mediator & mediator) { throw runtime_error(format("SDLContext: SDL_Init error: {}", SDL_GetError())); } - auto & cfg = Config::get_instance().window_settings; + auto & cfg = Config::get_instance().window; SDL_Window * tmp_window - = SDL_CreateWindow(cfg.window_title.c_str(), SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, cfg.default_size.x, cfg.default_size.y, 0); + = SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, cfg.size.x, cfg.size.y, 0); if (!tmp_window) { throw runtime_error(format("SDLContext: SDL_Window error: {}", SDL_GetError())); } @@ -76,12 +76,10 @@ SDLContext::~SDLContext() { } Keycode SDLContext::sdl_to_keycode(SDL_Scancode sdl_key) { - auto it = LOOKUP_TABLE.find(sdl_key); - if (it != LOOKUP_TABLE.end()) { - return it->second; - } + if (!LOOKUP_TABLE.contains(sdl_key)) + return Keycode::NONE; - return Keycode::NONE; + return LOOKUP_TABLE.at(sdl_key); } void SDLContext::update_keyboard_state() { // Array to hold the key states (true if pressed, false if not) @@ -90,10 +88,8 @@ void SDLContext::update_keyboard_state() { for (int i = 0; i < SDL_NUM_SCANCODES; ++i) { Keycode key = sdl_to_keycode(static_cast<SDL_Scancode>(i)); - - if (key != Keycode::NONE) { - this->keyboard_state[key] = current_state[i] != 0; - } + if (key == Keycode::NONE) continue; + this->keyboard_state[key] = current_state[i] != 0; } } @@ -187,13 +183,13 @@ void SDLContext::draw(const RenderContext & ctx) { } void SDLContext::update_camera_view(const Camera & cam, const vec2 & new_pos) { - const Camera::Data & cam_data = cam.data; + const Config & config = Config::get_instance(); // resize window int w, h; SDL_GetWindowSize(this->game_window.get(), &w, &h); - if (w != cam.screen.x || h != cam.screen.y) { - SDL_SetWindowSize(this->game_window.get(), cam.screen.x, cam.screen.y); + if (w != config.window.size.x || h != config.window.size.y) { + SDL_SetWindowSize(this->game_window.get(), config.window.size.x, config.window.size.y); } vec2 & zoomed_viewport = this->cam_aux_data.zoomed_viewport; @@ -202,28 +198,28 @@ void SDLContext::update_camera_view(const Camera & cam, const vec2 & new_pos) { this->cam_aux_data.cam_pos = new_pos; zoomed_viewport = cam.viewport_size * cam_data.zoom; - float screen_aspect = static_cast<float>(cam.screen.x) / cam.screen.y; + float screen_aspect = static_cast<float>(config.window.size.x) / config.window.size.y; float viewport_aspect = zoomed_viewport.x / zoomed_viewport.y; // calculate black bars if (screen_aspect > viewport_aspect) { // pillarboxing - float scale = cam.screen.y / zoomed_viewport.y; + float scale = config.window.size.y / zoomed_viewport.y; float adj_width = zoomed_viewport.x * scale; - float bar_width = (cam.screen.x - adj_width) / 2; - this->black_bars[0] = {0, 0, bar_width, (float) cam.screen.y}; - this->black_bars[1] = {(cam.screen.x - bar_width), 0, bar_width, (float) cam.screen.y}; + float bar_width = (config.window.size.x - adj_width) / 2; + this->black_bars[0] = {0, 0, bar_width, (float) config.window.size.y}; + this->black_bars[1] = {(config.window.size.x - bar_width), 0, bar_width, (float) config.window.size.y}; bar_size = {bar_width, 0}; render_scale.x = render_scale.y = scale; } else { // letterboxing - float scale = cam.screen.x / (cam.viewport_size.x * cam_data.zoom); + float scale = config.window.size.x / (cam.viewport_size.x * cam_data.zoom); float adj_height = cam.viewport_size.y * scale; - float bar_height = (cam.screen.y - adj_height) / 2; - this->black_bars[0] = {0, 0, (float) cam.screen.x, bar_height}; + float bar_height = (config.window.size.y - adj_height) / 2; + this->black_bars[0] = {0, 0, (float) config.window.size.x, bar_height}; this->black_bars[1] - = {0, (cam.screen.y - bar_height), (float) cam.screen.x, bar_height}; + = {0, (config.window.size.y - bar_height), (float) config.window.size.x, bar_height}; bar_size = {0, bar_height}; render_scale.x = render_scale.y = scale; @@ -235,8 +231,8 @@ void SDLContext::update_camera_view(const Camera & cam, const vec2 & new_pos) { SDL_Rect bg = { .x = 0, .y = 0, - .w = cam.screen.x, - .h = cam.screen.y, + .w = config.window.size.x, + .h = config.window.size.y, }; // fill bg color @@ -402,3 +398,8 @@ void SDLContext::set_color_texture(const Texture & texture, const Color & color) SDL_SetTextureColorMod(texture.get_img(), color.r, color.g, color.b); SDL_SetTextureAlphaMod(texture.get_img(), color.a); } + +const keyboard_state_t & SDLContext::get_keyboard_state() const { + return this->keyboard_state; +} + diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index 7ccefeb..176982f 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -270,6 +270,8 @@ public: */ void set_color_texture(const Texture & texture, const Color & color); + const keyboard_state_t & get_keyboard_state() const; + private: //! sdl Window std::unique_ptr<SDL_Window, std::function<void(SDL_Window *)>> game_window; @@ -288,7 +290,7 @@ private: CameraAuxiliaryData cam_aux_data; private: - std::unordered_map<Keycode, bool> keyboard_state; + keyboard_state_t keyboard_state; const std::unordered_map<SDL_Scancode, Keycode> LOOKUP_TABLE = {{SDL_SCANCODE_SPACE, Keycode::SPACE}, {SDL_SCANCODE_APOSTROPHE, Keycode::APOSTROPHE}, diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 97e455e..b1e6463 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -1,5 +1,5 @@ #include "../api/Asset.h" -#include "../util/Log.h" +#include "../util/dbg.h" #include "Sound.h" diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp index b1f8cb3..5091e07 100644 --- a/src/crepe/facade/SoundContext.cpp +++ b/src/crepe/facade/SoundContext.cpp @@ -1,4 +1,4 @@ -#include "../util/Log.h" +#include "../util/dbg.h" #include "SoundContext.h" diff --git a/src/crepe/facade/Texture.cpp b/src/crepe/facade/Texture.cpp index b63403d..23a9c8e 100644 --- a/src/crepe/facade/Texture.cpp +++ b/src/crepe/facade/Texture.cpp @@ -1,4 +1,4 @@ -#include "../util/Log.h" +#include "../util/dbg.h" #include "facade/SDLContext.h" #include "manager/Mediator.h" diff --git a/src/crepe/manager/CMakeLists.txt b/src/crepe/manager/CMakeLists.txt index f73e165..48e444f 100644 --- a/src/crepe/manager/CMakeLists.txt +++ b/src/crepe/manager/CMakeLists.txt @@ -6,6 +6,8 @@ target_sources(crepe PUBLIC SceneManager.cpp LoopTimerManager.cpp ResourceManager.cpp + ReplayManager.cpp + SystemManager.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -21,5 +23,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES LoopTimerManager.h ResourceManager.h ResourceManager.hpp + ReplayManager.h + SystemManager.h + SystemManager.hpp ) diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp index df30d27..745ddae 100644 --- a/src/crepe/manager/ComponentManager.cpp +++ b/src/crepe/manager/ComponentManager.cpp @@ -1,7 +1,7 @@ #include "../api/GameObject.h" #include "../api/Metadata.h" #include "../types.h" -#include "../util/Log.h" +#include "../util/dbg.h" #include "ComponentManager.h" @@ -53,7 +53,7 @@ GameObject ComponentManager::new_object(const string & name, const string & tag, this->next_id++; } - GameObject object{*this, this->next_id, name, tag, position, rotation, scale}; + GameObject object{this->mediator, this->next_id, name, tag, position, rotation, scale}; this->next_id++; return object; @@ -72,3 +72,28 @@ set<game_object_id_t> ComponentManager::get_objects_by_tag(const string & tag) c return this->get_objects_by_predicate<Metadata>( [tag](const Metadata & data) { return data.tag == tag; }); } + +ComponentManager::Snapshot ComponentManager::save() { + Snapshot snapshot{}; + for (const auto & [type, by_id_index] : this->components) { + for (game_object_id_t id = 0; id < by_id_index.size(); id++) { + const auto & components = by_id_index[id]; + for (size_t index = 0; index < components.size(); index++) { + const Component & component = *components[index]; + snapshot.components.push_back(SnapshotComponent{ + .type = type, + .id = id, + .index = index, + .component = component.save(), + }); + } + } + } + return snapshot; +} + +void ComponentManager::restore(const Snapshot & snapshot) { + for (const SnapshotComponent & info : snapshot.components) { + this->components[info.type][info.id][info.index]->restore(*info.component); + } +} diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index 19a8e81..c3a5b4a 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -21,13 +21,6 @@ class GameObject; * This class manages all components. It provides methods to add, delete and get components. */ class ComponentManager : public Manager { - // TODO: This relation should be removed! I (loek) believe that the scene manager should - // create/destroy components because the GameObject's are stored in concrete Scene classes, - // which will in turn call GameObject's destructor, which will in turn call - // ComponentManager::delete_components_by_id or something. This is a pretty major change, so - // here is a comment and temporary fix instead :tada: - friend class SceneManager; - public: ComponentManager(Mediator & mediator); ~ComponentManager(); // dbg_trace @@ -49,12 +42,7 @@ public: const vec2 & position = {0, 0}, double rotation = 0, double scale = 1); -protected: - /** - * GameObject is used as an interface to add/remove components, and the game programmer is - * supposed to use it instead of interfacing with the component manager directly. - */ - friend class GameObject; +public: /** * \brief Add a component to the ComponentManager * @@ -154,6 +142,40 @@ public: template <typename T> RefVector<T> get_components_by_tag(const std::string & tag) const; + //! Snapshot of single component (including path in \c components) + struct SnapshotComponent { + //! \c components path + std::type_index type; + //! \c components path + game_object_id_t id; + //! \c components path + size_t index; + //! Actual component snapshot + std::unique_ptr<Component> component; + }; + //! Snapshot of the entire component manager state + struct Snapshot { + //! All components + std::vector<SnapshotComponent> components; + // TODO: some kind of hash code that ensures components exist in all the same places as + // this snapshot + }; + /** + * \name ReplayManager (Memento) functions + * \{ + */ + /** + * \brief Save a snapshot of the component manager state + * \returns Deep copy of the component manager's internal state + */ + Snapshot save(); + /** + * \brief Restore component manager from a snapshot + * \param snapshot Snapshot to restore from (as returned by \c save()) + */ + void restore(const Snapshot & snapshot); + //! \} + private: /** * \brief Get object IDs by predicate function diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp index 9819632..e78f92f 100644 --- a/src/crepe/manager/LoopTimerManager.cpp +++ b/src/crepe/manager/LoopTimerManager.cpp @@ -1,7 +1,7 @@ #include <chrono> #include <thread> -#include "../util/Log.h" +#include "../util/dbg.h" #include "LoopTimerManager.h" @@ -31,7 +31,7 @@ void LoopTimerManager::update() { this->delta_time = this->maximum_delta_time; } if (this->delta_time > 0s) { - this->actual_fps = 1.0 / duration_cast<seconds>(this->delta_time).count(); + this->actual_fps = static_cast<unsigned>(1.0 / this->delta_time.count()); } else { this->actual_fps = 0; } diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h index 91403e4..2f1e6b6 100644 --- a/src/crepe/manager/LoopTimerManager.h +++ b/src/crepe/manager/LoopTimerManager.h @@ -6,7 +6,9 @@ namespace crepe { -typedef std::chrono::duration<double> duration_t; +class Engine; + +typedef std::chrono::duration<float> duration_t; typedef std::chrono::duration<unsigned long long, std::micro> elapsed_time_t; /** @@ -55,7 +57,7 @@ public: * * \return Current FPS. */ - unsigned get_fps() const; + unsigned int get_fps() const; /** * \brief Get the current time scale. @@ -107,7 +109,7 @@ public: private: //! Friend relation to use start,enforce_frame_rate,get_lag,update,advance_fixed_update. - friend class LoopManager; + friend class Engine; /** * \brief Start the loop timer. * @@ -149,9 +151,9 @@ private: private: //! Target frames per second. - unsigned target_fps = 60; + unsigned int target_fps = 60; //! Actual frames per second. - unsigned actual_fps = 0; + unsigned int actual_fps = 0; //! Time scale for speeding up or slowing down the game (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up). float time_scale = 1; //! Maximum delta time in seconds to avoid large jumps. diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index a336410..842f1de 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -11,6 +11,8 @@ class LoopTimerManager; class SaveManager; class ResourceManager; class SDLContext; +class ReplayManager; +class SystemManager; /** * Struct to pass references to classes that would otherwise need to be singletons down to @@ -32,6 +34,8 @@ struct Mediator { OptionalRef<LoopTimerManager> loop_timer; OptionalRef<SaveManager> save_manager; OptionalRef<ResourceManager> resource_manager; + OptionalRef<ReplayManager> replay_manager; + OptionalRef<SystemManager> system_manager; }; } // namespace crepe diff --git a/src/crepe/manager/ReplayManager.cpp b/src/crepe/manager/ReplayManager.cpp new file mode 100644 index 0000000..090a94e --- /dev/null +++ b/src/crepe/manager/ReplayManager.cpp @@ -0,0 +1,70 @@ +#include <format> + +#include "Manager.h" +#include "ReplayManager.h" + +using namespace crepe; +using namespace std; + +ReplayManager::ReplayManager(Mediator & mediator) : Manager(mediator) { + mediator.replay_manager = *this; +} + +void ReplayManager::record_start() { + if (this->state == RECORDING) this->release(this->id); + this->id++; + this->memory[this->id] = make_unique<Recording>(); + this->recording = *this->memory.at(this->id); + this->state = RECORDING; +} + +recording_t ReplayManager::record_end() { + this->state = IDLE; + return this->id; +} + +void ReplayManager::play(recording_t handle) { + if (!this->memory.contains(handle)) + throw out_of_range(format("ReplayManager: no recording for handle {}", handle)); + this->recording = *this->memory.at(handle); + this->recording->frame = 0; + this->state = PLAYING; +} + +void ReplayManager::release(recording_t handle) { + if (!this->memory.contains(handle)) return; + this->memory.erase(handle); +} + +void ReplayManager::frame_record() { + if (this->state != RECORDING) + throw runtime_error("ReplayManager: frame_step called while not playing"); + + ComponentManager & components = this->mediator.component_manager; + Recording & recording = this->recording; + + recording.frames.push_back(components.save()); + recording.frame++; +} + +bool ReplayManager::frame_step() { + if (this->state != PLAYING) + throw runtime_error("ReplayManager: frame_step called while not playing"); + + ComponentManager & components = this->mediator.component_manager; + Recording & recording = this->recording; + + ComponentManager::Snapshot & frame = recording.frames.at(recording.frame); + + components.restore(frame); + recording.frame++; + + if (recording.frame < recording.frames.size()) return false; + // end of recording + recording.frame = 0; + this->state = IDLE; + this->recording.clear(); + return true; +} + +ReplayManager::State ReplayManager::get_state() const { return this->state; } diff --git a/src/crepe/manager/ReplayManager.h b/src/crepe/manager/ReplayManager.h new file mode 100644 index 0000000..ab15b27 --- /dev/null +++ b/src/crepe/manager/ReplayManager.h @@ -0,0 +1,94 @@ +#pragma once + +#include <unordered_map> + +#include "ComponentManager.h" +#include "Manager.h" +#include "util/OptionalRef.h" + +namespace crepe { + +typedef size_t recording_t; + +/** + * \brief Replay manager + * + * The replay manager is responsible for creating, storing and restoring ComponentManager + * snapshots. Sequential snapshots can be recorded and replayed in combination with + * ReplaySystem. + */ +class ReplayManager : public Manager { + // TODO: Delete recordings at end of scene + +public: + ReplayManager(Mediator & mediator); + +public: + //! Start a new recording + void record_start(); + /** + * \brief End the latest recording started by \c record_start() + * \returns Handle to recording + */ + recording_t record_end(); + /** + * \brief Play a recording + * \param handle Handle to recording (as returned by \c record_end()) + */ + void play(recording_t handle); + /** + * \brief Delete a recording from memory + * \param handle Handle to recording (as returned by \c record_end()) + */ + void release(recording_t handle); + +public: + //! Internal state + enum State { + IDLE, //!< Not doing anything + RECORDING, //!< Currently recording + PLAYING, //!< Currently playing back a recording + }; + //! Get current internal state + State get_state() const; + +public: + /** + * \brief Record a single frame to the current recording + * + * This function is called by ReplaySystem after the game programmer has called \c + * record_start() + */ + void frame_record(); + /** + * \brief Play the next frame of the current recording + * + * \returns `true` if the recording is finished playing + * \returns `false` if there are more frames + * + * This function also automatically resets the internal state from PLAYING to IDLE at the end + * of a recording. + */ + bool frame_step(); + +private: + /** + * \brief Recording data + */ + struct Recording { + //! Current frame being shown + size_t frame = 0; + //! All frames in recording + std::vector<ComponentManager::Snapshot> frames; + }; + //! Internal state + State state = IDLE; + //! Current recording handle + recording_t id = -1; + //! Current recording data + OptionalRef<Recording> recording; + //! Recording storage + std::unordered_map<recording_t, std::unique_ptr<Recording>> memory; +}; + +} // namespace crepe diff --git a/src/crepe/manager/ResourceManager.cpp b/src/crepe/manager/ResourceManager.cpp index a141a46..5713183 100644 --- a/src/crepe/manager/ResourceManager.cpp +++ b/src/crepe/manager/ResourceManager.cpp @@ -1,4 +1,4 @@ -#include "util/Log.h" +#include "util/dbg.h" #include "ResourceManager.h" diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp index 691ea2f..f313ed2 100644 --- a/src/crepe/manager/SaveManager.cpp +++ b/src/crepe/manager/SaveManager.cpp @@ -129,9 +129,32 @@ template void SaveManager::set(const string &, const float &); template void SaveManager::set(const string &, const double &); template <typename T> +T SaveManager::get(const string & key) { + return this->deserialize<T>(this->get_db().get(key)); +} +template uint8_t SaveManager::get(const string &); +template int8_t SaveManager::get(const string &); +template uint16_t SaveManager::get(const string &); +template int16_t SaveManager::get(const string &); +template uint32_t SaveManager::get(const string &); +template int32_t SaveManager::get(const string &); +template uint64_t SaveManager::get(const string &); +template int64_t SaveManager::get(const string &); +template float SaveManager::get(const string &); +template double SaveManager::get(const string &); +template string SaveManager::get(const string &); + +template <typename T> ValueBroker<T> SaveManager::get(const string & key, const T & default_value) { if (!this->has(key)) this->set<T>(key, default_value); - return this->get<T>(key); + T value; + return { + [this, key](const T & target) { this->set<T>(key, target); }, + [this, key, value]() mutable -> const T & { + value = this->get<T>(key); + return value; + }, + }; } template ValueBroker<uint8_t> SaveManager::get(const string &, const uint8_t &); template ValueBroker<int8_t> SaveManager::get(const string &, const int8_t &); @@ -144,27 +167,3 @@ template ValueBroker<int64_t> SaveManager::get(const string &, const int64_t &); template ValueBroker<float> SaveManager::get(const string &, const float &); template ValueBroker<double> SaveManager::get(const string &, const double &); template ValueBroker<string> SaveManager::get(const string &, const string &); - -template <typename T> -ValueBroker<T> SaveManager::get(const string & key) { - T value; - return { - [this, key](const T & target) { this->set<T>(key, target); }, - [this, key, value]() mutable -> const T & { - DB & db = this->get_db(); - value = this->deserialize<T>(db.get(key)); - return value; - }, - }; -} -template ValueBroker<uint8_t> SaveManager::get(const string &); -template ValueBroker<int8_t> SaveManager::get(const string &); -template ValueBroker<uint16_t> SaveManager::get(const string &); -template ValueBroker<int16_t> SaveManager::get(const string &); -template ValueBroker<uint32_t> SaveManager::get(const string &); -template ValueBroker<int32_t> SaveManager::get(const string &); -template ValueBroker<uint64_t> SaveManager::get(const string &); -template ValueBroker<int64_t> SaveManager::get(const string &); -template ValueBroker<float> SaveManager::get(const string &); -template ValueBroker<double> SaveManager::get(const string &); -template ValueBroker<string> SaveManager::get(const string &); diff --git a/src/crepe/manager/SaveManager.h b/src/crepe/manager/SaveManager.h index 61a978d..1e34bc0 100644 --- a/src/crepe/manager/SaveManager.h +++ b/src/crepe/manager/SaveManager.h @@ -36,17 +36,17 @@ public: ValueBroker<T> get(const std::string & key, const T & default_value); /** - * \brief Get a read/write reference to a value + * \brief Get a value directly * * \param key The value key * - * \return Read/write reference to the value + * \return The value * * \note Attempting to read this value before it is initialized (i.e. set) will result in an * exception */ template <typename T> - ValueBroker<T> get(const std::string & key); + T get(const std::string & key); /** * \brief Set a value directly diff --git a/src/crepe/manager/SystemManager.cpp b/src/crepe/manager/SystemManager.cpp new file mode 100644 index 0000000..5ada30f --- /dev/null +++ b/src/crepe/manager/SystemManager.cpp @@ -0,0 +1,66 @@ +#include "../system/AISystem.h" +#include "../system/AnimatorSystem.h" +#include "../system/AudioSystem.h" +#include "../system/CollisionSystem.h" +#include "../system/EventSystem.h" +#include "../system/InputSystem.h" +#include "../system/ParticleSystem.h" +#include "../system/PhysicsSystem.h" +#include "../system/RenderSystem.h" +#include "../system/ReplaySystem.h" +#include "../system/ScriptSystem.h" + +#include "SystemManager.h" + +using namespace crepe; +using namespace std; + +SystemManager::SystemManager(Mediator & mediator) : Manager(mediator) { + this->load_system<InputSystem>(); + this->load_system<EventSystem>(); + this->load_system<ScriptSystem>(); + this->load_system<AISystem>(); + this->load_system<PhysicsSystem>(); + this->load_system<CollisionSystem>(); + this->load_system<AudioSystem>(); + this->load_system<AnimatorSystem>(); + this->load_system<ParticleSystem>(); + this->load_system<RenderSystem>(); + this->load_system<ReplaySystem>(); + + this->mediator.system_manager = *this; +} + +void SystemManager::fixed_update() { + for (auto & [type, system] : this->systems) { + if (!system->active) continue; + system->fixed_update(); + } +} + +void SystemManager::frame_update() { + for (auto & [type, system] : this->systems) { + if (!system->active) continue; + system->frame_update(); + } +} + +SystemManager::Snapshot SystemManager::save() { + Snapshot snapshot; + for (auto & [type, system] : this->systems) { + snapshot[type] = system->active; + } + return snapshot; +} + +void SystemManager::restore(const Snapshot & snapshot) { + for (auto & [type, active] : snapshot) { + this->systems[type]->active = active; + } +} + +void SystemManager::disable_all() { + for (auto & [type, system] : this->systems) { + system->active = false; + } +} diff --git a/src/crepe/manager/SystemManager.h b/src/crepe/manager/SystemManager.h new file mode 100644 index 0000000..50acf77 --- /dev/null +++ b/src/crepe/manager/SystemManager.h @@ -0,0 +1,85 @@ +#pragma once + +#include <memory> +#include <typeindex> +#include <unordered_map> + +#include "../system/System.h" + +#include "Manager.h" + +namespace crepe { + +/** + * \brief Collection of all systems + * + * This manager aggregates all systems and provides utility functions to retrieve references to + * and update systems. + */ +class SystemManager : public Manager { +public: + SystemManager(Mediator &); + + /** + * \brief Per-frame update. + * + * Updates the game state based on the elapsed time since the last frame. + */ + void frame_update(); + + /** + * \brief Fixed update executed at a fixed rate. + * + * This function updates physics and game logic based on LoopTimer's fixed_delta_time. + */ + void fixed_update(); + +private: + /** + * \brief Collection of System instances + * + * This map holds System instances indexed by the system's class typeid. It is filled in the + * constructor of \c SystemManager using SystemManager::load_system. + */ + std::unordered_map<std::type_index, std::unique_ptr<System>> systems; + /** + * \brief Initialize a system + * \tparam T System type (must be derivative of \c System) + */ + template <class T> + void load_system(); + +public: + /** + * \brief Retrieve a reference to ECS system + * \tparam T System type + * \returns Reference to system instance + * \throws std::runtime_error if the System is not initialized + */ + template <class T> + T & get_system(); + +public: + /** + * \brief SystemManager snapshot + * + * The SystemManager snapshot only stores which systems are active + */ + typedef std::unordered_map<std::type_index, bool> Snapshot; + /** + * \brief Save a snapshot of the systems' state + * \returns Copy of each system's active property + */ + Snapshot save(); + /** + * \brief Restore system active state from a snapshot + * \param snapshot Snapshot to restore from (as returned by \c save()) + */ + void restore(const Snapshot & snapshot); + //! Disable all systems + void disable_all(); +}; + +} // namespace crepe + +#include "SystemManager.hpp" diff --git a/src/crepe/api/LoopManager.hpp b/src/crepe/manager/SystemManager.hpp index 266758a..3d26e4c 100644 --- a/src/crepe/api/LoopManager.hpp +++ b/src/crepe/manager/SystemManager.hpp @@ -4,26 +4,19 @@ #include <format> #include <memory> -#include "../system/System.h" - -#include "LoopManager.h" +#include "SystemManager.h" namespace crepe { template <class T> -void LoopManager::add_scene() { - this->scene_manager.add_scene<T>(); -} - -template <class T> -T & LoopManager::get_system() { +T & SystemManager::get_system() { using namespace std; static_assert(is_base_of<System, T>::value, "get_system must recieve a derivative class of System"); const type_info & type = typeid(T); if (!this->systems.contains(type)) - throw runtime_error(format("LoopManager: {} is not initialized", type.name())); + throw runtime_error(format("SystemManager: {} is not initialized", type.name())); System * system = this->systems.at(type).get(); T * concrete_system = dynamic_cast<T *>(system); @@ -33,14 +26,14 @@ T & LoopManager::get_system() { } template <class T> -void LoopManager::load_system() { +void SystemManager::load_system() { using namespace std; static_assert(is_base_of<System, T>::value, "load_system must recieve a derivative class of System"); const type_info & type = typeid(T); if (this->systems.contains(type)) - throw runtime_error(format("LoopManager: {} is already initialized", type.name())); + throw runtime_error(format("SystemManager: {} is already initialized", type.name())); System * system = new T(this->mediator); this->systems[type] = unique_ptr<System>(system); } diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp index 680dbb8..0f35010 100644 --- a/src/crepe/system/AISystem.cpp +++ b/src/crepe/system/AISystem.cpp @@ -10,7 +10,7 @@ using namespace crepe; using namespace std::chrono; -void AISystem::update() { +void AISystem::fixed_update() { const Mediator & mediator = this->mediator; ComponentManager & mgr = mediator.component_manager; LoopTimerManager & loop_timer = mediator.loop_timer; diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h index d5f8a8e..04807cf 100644 --- a/src/crepe/system/AISystem.h +++ b/src/crepe/system/AISystem.h @@ -20,7 +20,7 @@ public: using System::System; //! Update the AI system - void update() override; + void fixed_update() override; private: /** diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp index 31eb85c..e5ab2fa 100644 --- a/src/crepe/system/AnimatorSystem.cpp +++ b/src/crepe/system/AnimatorSystem.cpp @@ -1,22 +1,23 @@ - - #include "../api/Animator.h" #include "../manager/ComponentManager.h" #include "../manager/LoopTimerManager.h" +#include <chrono> #include "AnimatorSystem.h" using namespace crepe; +using namespace std::chrono; -void AnimatorSystem::update() { +void AnimatorSystem::frame_update() { ComponentManager & mgr = this->mediator.component_manager; LoopTimerManager & timer = this->mediator.loop_timer; RefVector<Animator> animations = mgr.get_components_by_type<Animator>(); - unsigned long long elapsed_time = timer.get_elapsed_time().count(); + float elapsed_time = duration_cast<duration<float>>(timer.get_elapsed_time()).count(); for (Animator & a : animations) { if (!a.active) continue; + if (a.data.fps == 0) continue; Animator::Data & ctx = a.data; float frame_duration = 1.0f / ctx.fps; diff --git a/src/crepe/system/AnimatorSystem.h b/src/crepe/system/AnimatorSystem.h index 7d3f565..092e131 100644 --- a/src/crepe/system/AnimatorSystem.h +++ b/src/crepe/system/AnimatorSystem.h @@ -22,7 +22,7 @@ public: * Animator components, moving the animations forward and managing their behavior (e.g., * looping). */ - void update() override; + void frame_update() override; }; } // namespace crepe diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index b1aa0f8..d4e8b9f 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -7,7 +7,7 @@ using namespace crepe; using namespace std; -void AudioSystem::update() { +void AudioSystem::fixed_update() { ComponentManager & component_manager = this->mediator.component_manager; ResourceManager & resource_manager = this->mediator.resource_manager; RefVector<AudioSource> components diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index 2ddc443..56fc98c 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -11,7 +11,7 @@ namespace crepe { class AudioSystem : public System { public: using System::System; - void update() override; + void fixed_update() override; private: /** diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt index 0e2db76..52369d0 100644 --- a/src/crepe/system/CMakeLists.txt +++ b/src/crepe/system/CMakeLists.txt @@ -8,6 +8,8 @@ target_sources(crepe PUBLIC AudioSystem.cpp AnimatorSystem.cpp InputSystem.cpp + EventSystem.cpp + ReplaySystem.cpp AISystem.cpp ) @@ -20,5 +22,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES AudioSystem.h AnimatorSystem.h InputSystem.h + EventSystem.h + ReplaySystem.h AISystem.h ) diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index 44a0431..9d88d9f 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -23,7 +23,7 @@ using namespace crepe; -void CollisionSystem::update() { +void CollisionSystem::fixed_update() { std::vector<CollisionInternal> all_colliders; game_object_id_t id = 0; ComponentManager & mgr = this->mediator.component_manager; @@ -192,13 +192,17 @@ CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal resolution_direction = Direction::BOTH; } else if (resolution.x != 0) { resolution_direction = Direction::X_DIRECTION; - if (data1.rigidbody.data.linear_velocity.y != 0) - resolution.y = data1.rigidbody.data.linear_velocity.y + //checks if the other velocity has a value and if this object moved + if (data1.rigidbody.data.linear_velocity.x != 0 + && data1.rigidbody.data.linear_velocity.y != 0) + resolution.y = -data1.rigidbody.data.linear_velocity.y * (resolution.x / data1.rigidbody.data.linear_velocity.x); } else if (resolution.y != 0) { resolution_direction = Direction::Y_DIRECTION; - if (data1.rigidbody.data.linear_velocity.x != 0) - resolution.x = data1.rigidbody.data.linear_velocity.x + //checks if the other velocity has a value and if this object moved + if (data1.rigidbody.data.linear_velocity.x != 0 + && data1.rigidbody.data.linear_velocity.y != 0) + resolution.x = -data1.rigidbody.data.linear_velocity.x * (resolution.y / data1.rigidbody.data.linear_velocity.y); } @@ -314,28 +318,48 @@ void CollisionSystem::static_collision_handler(CollisionInfo & info) { // Move object back using calculate move back value info.this_transform.position += info.resolution; - // If bounce is enabled mirror velocity - if (info.this_rigidbody.data.elastisity_coefficient > 0) { - if (info.resolution_direction == Direction::BOTH) { - info.this_rigidbody.data.linear_velocity.y - = -info.this_rigidbody.data.linear_velocity.y - * info.this_rigidbody.data.elastisity_coefficient; - info.this_rigidbody.data.linear_velocity.x - = -info.this_rigidbody.data.linear_velocity.x - * info.this_rigidbody.data.elastisity_coefficient; - } else if (info.resolution_direction == Direction::Y_DIRECTION) { - info.this_rigidbody.data.linear_velocity.y - = -info.this_rigidbody.data.linear_velocity.y - * info.this_rigidbody.data.elastisity_coefficient; - } else if (info.resolution_direction == Direction::X_DIRECTION) { - info.this_rigidbody.data.linear_velocity.x - = -info.this_rigidbody.data.linear_velocity.x - * info.this_rigidbody.data.elastisity_coefficient; - } - } - // Stop movement if bounce is disabled - else { - info.this_rigidbody.data.linear_velocity = {0, 0}; + switch (info.resolution_direction) { + case Direction::BOTH: + //bounce + if (info.this_rigidbody.data.elastisity_coefficient > 0) { + info.this_rigidbody.data.linear_velocity + = -info.this_rigidbody.data.linear_velocity + * info.this_rigidbody.data.elastisity_coefficient; + } + //stop movement + else { + info.this_rigidbody.data.linear_velocity = {0, 0}; + } + break; + case Direction::Y_DIRECTION: + // Bounce + if (info.this_rigidbody.data.elastisity_coefficient > 0) { + info.this_rigidbody.data.linear_velocity.y + = -info.this_rigidbody.data.linear_velocity.y + * info.this_rigidbody.data.elastisity_coefficient; + } + // Stop movement + else { + info.this_rigidbody.data.linear_velocity.y = 0; + info.this_transform.position.x -= info.resolution.x; + } + break; + case Direction::X_DIRECTION: + // Bounce + if (info.this_rigidbody.data.elastisity_coefficient > 0) { + info.this_rigidbody.data.linear_velocity.x + = -info.this_rigidbody.data.linear_velocity.x + * info.this_rigidbody.data.elastisity_coefficient; + } + // Stop movement + else { + info.this_rigidbody.data.linear_velocity.x = 0; + info.this_transform.position.y -= info.resolution.y; + } + break; + case Direction::NONE: + // Not possible + break; } } diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h index 5b136c6..48a8f86 100644 --- a/src/crepe/system/CollisionSystem.h +++ b/src/crepe/system/CollisionSystem.h @@ -85,7 +85,7 @@ public: public: //! Updates the collision system by checking for collisions between colliders and handling them. - void update() override; + void fixed_update() override; private: /** diff --git a/src/crepe/system/EventSystem.cpp b/src/crepe/system/EventSystem.cpp new file mode 100644 index 0000000..7e168ab --- /dev/null +++ b/src/crepe/system/EventSystem.cpp @@ -0,0 +1,9 @@ +#include "EventSystem.h" +#include "../manager/EventManager.h" + +using namespace crepe; + +void EventSystem::fixed_update() { + EventManager & ev = this->mediator.event_manager; + ev.dispatch_events(); +} diff --git a/src/crepe/system/EventSystem.h b/src/crepe/system/EventSystem.h new file mode 100644 index 0000000..0ae48d2 --- /dev/null +++ b/src/crepe/system/EventSystem.h @@ -0,0 +1,21 @@ +#pragma once + +#include "System.h" + +namespace crepe { + +/** + * \brief EventManager dispatch helper system + */ +class EventSystem : public System { +public: + using System::System; + + /** + * \brief Dispatch queued events + * \see EventManager::dispatch_events + */ + void fixed_update() override; +}; + +} // namespace crepe diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp index 8de700b..38d12c5 100644 --- a/src/crepe/system/InputSystem.cpp +++ b/src/crepe/system/InputSystem.cpp @@ -8,10 +8,11 @@ using namespace crepe; -void InputSystem::update() { +void InputSystem::fixed_update() { ComponentManager & mgr = this->mediator.component_manager; SDLContext & context = this->mediator.sdl_context; + context.update_keyboard_state(); std::vector<SDLContext::EventData> event_list = context.get_events(); RefVector<Button> buttons = mgr.get_components_by_type<Button>(); RefVector<Camera> cameras = mgr.get_components_by_type<Camera>(); diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h index 63dfae5..b03805a 100644 --- a/src/crepe/system/InputSystem.h +++ b/src/crepe/system/InputSystem.h @@ -28,7 +28,7 @@ public: * \brief Updates the system, processing all input events. * This method processes all events and triggers corresponding actions. */ - void update() override; + void fixed_update() override; private: //! Stores the last position of the mouse when the button was pressed. diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp index b14c52f..4684ada 100644 --- a/src/crepe/system/ParticleSystem.cpp +++ b/src/crepe/system/ParticleSystem.cpp @@ -1,3 +1,4 @@ +#include <chrono> #include <cmath> #include <cstdlib> #include <ctime> @@ -5,14 +6,19 @@ #include "../api/ParticleEmitter.h" #include "../api/Transform.h" #include "../manager/ComponentManager.h" +#include "../manager/LoopTimerManager.h" #include "ParticleSystem.h" using namespace crepe; -void ParticleSystem::update() { +void ParticleSystem::frame_update() { // Get all emitters - ComponentManager & mgr = this->mediator.component_manager; + const Mediator & mediator = this->mediator; + LoopTimerManager & loop_timer = mediator.loop_timer; + ComponentManager & mgr = mediator.component_manager; + float dt = loop_timer.get_scaled_fixed_delta_time().count(); + RefVector<ParticleEmitter> emitters = mgr.get_components_by_type<ParticleEmitter>(); for (ParticleEmitter & emitter : emitters) { @@ -21,38 +27,39 @@ void ParticleSystem::update() { = mgr.get_components_by_id<Transform>(emitter.game_object_id).front().get(); // Emit particles based on emission_rate - int updates = calculate_update(this->update_count, emitter.data.emission_rate); - for (size_t i = 0; i < updates; i++) { - emit_particle(emitter, transform); + emitter.spawn_accumulator = emitter.data.emission_rate * dt; + while (emitter.spawn_accumulator >= 1.0) { + this->emit_particle(emitter, transform); + emitter.spawn_accumulator -= 1.0; } // Update all particles - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { if (particle.active) { - particle.update(); + particle.update(dt); } } // Check if within boundary - check_bounds(emitter, transform); + this->check_bounds(emitter, transform); } - - this->update_count = (this->update_count + 1) % this->MAX_UPDATE_COUNT; } void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & transform) { constexpr float DEG_TO_RAD = M_PI / 180.0; vec2 initial_position = emitter.data.position + transform.position; - float random_angle = generate_random_angle(emitter.data.min_angle, emitter.data.max_angle); + float random_angle + = this->generate_random_angle(emitter.data.min_angle, emitter.data.max_angle); - float random_speed = generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); + float random_speed + = this->generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); float angle_radians = random_angle * DEG_TO_RAD; vec2 velocity = {random_speed * std::cos(angle_radians), random_speed * std::sin(angle_radians)}; - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { if (!particle.active) { particle.reset(emitter.data.end_lifespan, initial_position, velocity, random_angle); @@ -61,66 +68,54 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & } } -int ParticleSystem::calculate_update(int count, double emission) const { - double integer_part = std::floor(emission); - double fractional_part = emission - integer_part; - - if (fractional_part > 0) { - int denominator = static_cast<int>(1.0 / fractional_part); - return (count % denominator == 0) ? 1 : 0; - } - - return static_cast<int>(emission); -} - void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform & transform) { vec2 offset = emitter.data.boundary.offset + transform.position + emitter.data.position; - double half_width = emitter.data.boundary.width / 2.0; - double half_height = emitter.data.boundary.height / 2.0; + float half_width = emitter.data.boundary.width / 2.0; + float half_height = emitter.data.boundary.height / 2.0; - const double LEFT = offset.x - half_width; - const double RIGHT = offset.x + half_width; - const double TOP = offset.y - half_height; - const double BOTTOM = offset.y + half_height; + float left = offset.x - half_width; + float right = offset.x + half_width; + float top = offset.y - half_height; + float bottom = offset.y + half_height; - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { const vec2 & position = particle.position; - bool within_bounds = (position.x >= LEFT && position.x <= RIGHT && position.y >= TOP - && position.y <= BOTTOM); - + bool within_bounds = (position.x >= left && position.x <= right && position.y >= top + && position.y <= bottom); + //if not within bounds do a reset or stop velocity if (!within_bounds) { if (emitter.data.boundary.reset_on_exit) { particle.active = false; } else { particle.velocity = {0, 0}; - if (position.x < LEFT) particle.position.x = LEFT; - else if (position.x > RIGHT) particle.position.x = RIGHT; - if (position.y < TOP) particle.position.y = TOP; - else if (position.y > BOTTOM) particle.position.y = BOTTOM; + if (position.x < left) particle.position.x = left; + else if (position.x > right) particle.position.x = right; + if (position.y < top) particle.position.y = top; + else if (position.y > bottom) particle.position.y = bottom; } } } } -double ParticleSystem::generate_random_angle(double min_angle, double max_angle) const { +float ParticleSystem::generate_random_angle(float min_angle, float max_angle) const { if (min_angle == max_angle) { return min_angle; } else if (min_angle < max_angle) { return min_angle - + static_cast<double>(std::rand() % static_cast<int>(max_angle - min_angle)); + + static_cast<float>(std::rand() % static_cast<int>(max_angle - min_angle)); } else { - double angle_offset = (360 - min_angle) + max_angle; - double random_angle - = min_angle + static_cast<double>(std::rand() % static_cast<int>(angle_offset)); + float angle_offset = (360 - min_angle) + max_angle; + float random_angle + = min_angle + static_cast<float>(std::rand() % static_cast<int>(angle_offset)); return (random_angle >= 360) ? random_angle - 360 : random_angle; } } -double ParticleSystem::generate_random_speed(double min_speed, double max_speed) const { +float ParticleSystem::generate_random_speed(float min_speed, float max_speed) const { if (min_speed == max_speed) { return min_speed; } else { return min_speed - + static_cast<double>(std::rand() % static_cast<int>(max_speed - min_speed)); + + static_cast<float>(std::rand() % static_cast<int>(max_speed - min_speed)); } } diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h index 068f01c..9eacc70 100644 --- a/src/crepe/system/ParticleSystem.h +++ b/src/crepe/system/ParticleSystem.h @@ -20,7 +20,7 @@ public: * \brief Updates all particle emitters by emitting particles, updating particle states, and * checking bounds. */ - void update() override; + void frame_update() override; private: /** @@ -32,16 +32,6 @@ private: void emit_particle(ParticleEmitter & emitter, const Transform & transform); /** - * \brief Calculates the number of times particles should be emitted based on emission rate - * and update count. - * - * \param count Current update count. - * \param emission Emission rate. - * \return The number of particles to emit. - */ - int calculate_update(int count, double emission) const; - - /** * \brief Checks whether particles are within the emitter’s boundary, resets or stops * particles if they exit. * @@ -57,7 +47,7 @@ private: * \param max_angle Maximum emission angle in degrees. * \return Random angle in degrees. */ - double generate_random_angle(double min_angle, double max_angle) const; + float generate_random_angle(float min_angle, float max_angle) const; /** * \brief Generates a random speed for particle emission within the specified range. @@ -66,15 +56,7 @@ private: * \param max_speed Maximum emission speed. * \return Random speed. */ - double generate_random_speed(double min_speed, double max_speed) const; - -private: - //! Counter to count updates to determine how many times emit_particle is - // called. - unsigned int update_count = 0; - //! Determines the lowest amount of emission rate (1000 = 0.001 = 1 particle per 1000 - // updates). - static constexpr unsigned int MAX_UPDATE_COUNT = 100; + float generate_random_speed(float min_speed, float max_speed) const; }; } // namespace crepe diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp index 3b3b8ab..62f8132 100644 --- a/src/crepe/system/PhysicsSystem.cpp +++ b/src/crepe/system/PhysicsSystem.cpp @@ -12,8 +12,7 @@ using namespace crepe; -void PhysicsSystem::update() { - +void PhysicsSystem::fixed_update() { const Mediator & mediator = this->mediator; ComponentManager & mgr = mediator.component_manager; LoopTimerManager & loop_timer = mediator.loop_timer; diff --git a/src/crepe/system/PhysicsSystem.h b/src/crepe/system/PhysicsSystem.h index 26152a5..5ed624f 100644 --- a/src/crepe/system/PhysicsSystem.h +++ b/src/crepe/system/PhysicsSystem.h @@ -18,7 +18,7 @@ public: * * It calculates new velocties and changes the postion in the transform. */ - void update() override; + void fixed_update() override; }; } // namespace crepe diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index afd9548..c403f6c 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -64,7 +64,7 @@ RefVector<Sprite> RenderSystem::sort(RefVector<Sprite> & objs) const { return sorted_objs; } -void RenderSystem::update() { +void RenderSystem::frame_update() { this->clear_screen(); this->render(); this->present_screen(); @@ -83,11 +83,11 @@ bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) bool rendering_particles = false; for (const ParticleEmitter & em : emitters) { - if (&em.data.sprite != &sprite) continue; + if (&em.sprite != &sprite) continue; rendering_particles = true; if (!em.active) continue; - for (const Particle & p : em.data.particles) { + for (const Particle & p : em.particles) { if (!p.active) continue; ctx.draw(SDLContext::RenderContext{ diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h index fc7b46e..1a61f99 100644 --- a/src/crepe/system/RenderSystem.h +++ b/src/crepe/system/RenderSystem.h @@ -24,7 +24,7 @@ public: * \brief Updates the RenderSystem for the current frame. * This method is called to perform all rendering operations for the current game frame. */ - void update() override; + void frame_update() override; private: //! Clears the screen in preparation for rendering. diff --git a/src/crepe/system/ReplaySystem.cpp b/src/crepe/system/ReplaySystem.cpp new file mode 100644 index 0000000..efc3be4 --- /dev/null +++ b/src/crepe/system/ReplaySystem.cpp @@ -0,0 +1,54 @@ +#include "../manager/ReplayManager.h" +#include "../manager/SystemManager.h" + +#include "EventSystem.h" +#include "RenderSystem.h" +#include "ReplaySystem.h" + +using namespace crepe; +using namespace std; + +void ReplaySystem::fixed_update() { + ReplayManager & replay = this->mediator.replay_manager; + ReplayManager::State state = replay.get_state(); + ReplayManager::State last_state = this->last_state; + this->last_state = state; + + switch (state) { + case ReplayManager::IDLE: + break; + case ReplayManager::RECORDING: { + replay.frame_record(); + break; + } + case ReplayManager::PLAYING: { + if (last_state != ReplayManager::PLAYING) this->playback_begin(); + bool last = replay.frame_step(); + if (last) this->playback_end(); + break; + } + } +} + +void ReplaySystem::playback_begin() { + SystemManager & systems = this->mediator.system_manager; + ComponentManager & components = this->mediator.component_manager; + + this->playback = { + .components = components.save(), + .systems = systems.save(), + }; + + systems.disable_all(); + systems.get_system<RenderSystem>().active = true; + systems.get_system<ReplaySystem>().active = true; + systems.get_system<EventSystem>().active = true; +} + +void ReplaySystem::playback_end() { + SystemManager & systems = this->mediator.system_manager; + ComponentManager & components = this->mediator.component_manager; + + components.restore(this->playback.components); + systems.restore(this->playback.systems); +} diff --git a/src/crepe/system/ReplaySystem.h b/src/crepe/system/ReplaySystem.h new file mode 100644 index 0000000..bbc8d76 --- /dev/null +++ b/src/crepe/system/ReplaySystem.h @@ -0,0 +1,44 @@ +#pragma once + +#include "../manager/ReplayManager.h" +#include "../manager/SystemManager.h" + +#include "System.h" + +namespace crepe { + +/** + * \brief ReplayManager helper system + * + * This system records and replays recordings using ReplayManager. + */ +class ReplaySystem : public System { +public: + using System::System; + + void fixed_update() override; + +private: + //! Last ReplayManager state + ReplayManager::State last_state = ReplayManager::IDLE; + + /** + * \brief Playback snapshot + * + * When starting playback, the component state is saved and most systems are disabled. This + * struct stores the engine state before ReplayManager::play is called. + */ + struct Snapshot { + ComponentManager::Snapshot components; + SystemManager::Snapshot systems; + }; + //! Before playback snapshot + Snapshot playback; + + //! Snapshot state and disable systems during playback + void playback_begin(); + //! Restore state from before \c playback_begin() + void playback_end(); +}; + +} // namespace crepe diff --git a/src/crepe/system/ScriptSystem.cpp b/src/crepe/system/ScriptSystem.cpp index d6b2ca1..58055d6 100644 --- a/src/crepe/system/ScriptSystem.cpp +++ b/src/crepe/system/ScriptSystem.cpp @@ -1,16 +1,18 @@ #include "../api/BehaviorScript.h" #include "../api/Script.h" #include "../manager/ComponentManager.h" +#include "../util/dbg.h" #include "ScriptSystem.h" using namespace std; using namespace crepe; -void ScriptSystem::update() { +void ScriptSystem::fixed_update() { dbg_trace(); ComponentManager & mgr = this->mediator.component_manager; + LoopTimerManager & timer = this->mediator.loop_timer; RefVector<BehaviorScript> behavior_scripts = mgr.get_components_by_type<BehaviorScript>(); for (BehaviorScript & behavior_script : behavior_scripts) { @@ -23,6 +25,8 @@ void ScriptSystem::update() { script->init(); script->initialized = true; } - script->update(); + + duration_t delta_time = timer.get_delta_time(); + script->update(delta_time); } } diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h index 3db1b1e..612c2ae 100644 --- a/src/crepe/system/ScriptSystem.h +++ b/src/crepe/system/ScriptSystem.h @@ -22,7 +22,7 @@ public: * method. It also calls Script::init() if this has not been done before on * the \c BehaviorScript instance. */ - void update() override; + void fixed_update() override; }; } // namespace crepe diff --git a/src/crepe/system/System.h b/src/crepe/system/System.h index 063dfbf..e2ce7eb 100644 --- a/src/crepe/system/System.h +++ b/src/crepe/system/System.h @@ -14,10 +14,12 @@ class ComponentManager; */ class System { public: - /** - * \brief Process all components this system is responsible for. - */ - virtual void update() = 0; + //! Code that runs in the fixed loop + virtual void fixed_update() {}; + //! Code that runs in the frame loop + virtual void frame_update() {}; + //! Indicates that the update functions of this system should be run + bool active = true; public: System(const Mediator & m); diff --git a/src/crepe/util/Log.cpp b/src/crepe/util/Log.cpp index 84d80a8..ce25a1d 100644 --- a/src/crepe/util/Log.cpp +++ b/src/crepe/util/Log.cpp @@ -4,6 +4,7 @@ #include "../api/Config.h" #include "Log.h" +#include "LogColor.h" using namespace crepe; using namespace std; diff --git a/src/crepe/util/Log.h b/src/crepe/util/Log.h index fc0bb3a..b43fe30 100644 --- a/src/crepe/util/Log.h +++ b/src/crepe/util/Log.h @@ -2,27 +2,6 @@ #include <format> -// allow user to disable debug macros -#ifndef CREPE_DISABLE_MACROS - -#include "LogColor.h" - -// utility macros -#define _crepe_logf_here(level, fmt, ...) \ - crepe::Log::logf(level, "{}" fmt, \ - crepe::LogColor().fg_white(false).str(std::format( \ - "{} ({}:{})", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__)), \ - __VA_ARGS__) - -// very illegal global function-style macros -// NOLINTBEGIN -#define dbg_logf(fmt, ...) _crepe_logf_here(crepe::Log::Level::DEBUG, ": " fmt, __VA_ARGS__) -#define dbg_log(str) _crepe_logf_here(crepe::Log::Level::DEBUG, ": {}", str) -#define dbg_trace() _crepe_logf_here(crepe::Log::Level::TRACE, "", "") -// NOLINTEND - -#endif - namespace crepe { /** diff --git a/src/crepe/util/dbg.h b/src/crepe/util/dbg.h new file mode 100644 index 0000000..c7283ee --- /dev/null +++ b/src/crepe/util/dbg.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Log.h" +#include "LogColor.h" + +// utility macros +#define _crepe_logf_here(level, fmt, ...) \ + crepe::Log::logf(level, "{}" fmt, \ + crepe::LogColor().fg_white(false).str(std::format( \ + "{} ({}:{})", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__)), \ + __VA_ARGS__) + +// very illegal global function-style macros +// NOLINTBEGIN +#define dbg_logf(fmt, ...) _crepe_logf_here(crepe::Log::Level::DEBUG, ": " fmt, __VA_ARGS__) +#define dbg_log(str) _crepe_logf_here(crepe::Log::Level::DEBUG, ": {}", str) +#define dbg_trace() _crepe_logf_here(crepe::Log::Level::TRACE, "", "") +// NOLINTEND |