diff options
Diffstat (limited to 'src')
129 files changed, 2256 insertions, 1501 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 b340826..27fa97f 100644 --- a/src/crepe/Particle.cpp +++ b/src/crepe/Particle.cpp @@ -2,8 +2,9 @@ using namespace crepe; -void Particle::reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity, - float 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; diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h index ee0cd66..c013de5 100644 --- a/src/crepe/Particle.h +++ b/src/crepe/Particle.h @@ -41,8 +41,8 @@ public: * \param velocity The initial velocity of the particle. * \param angle The angle of the particle's trajectory or orientation. */ - void reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity, - float angle); + void + reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity, float angle); /** * \brief Updates the particle's state. * diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp index 2195249..2fedaf4 100644 --- a/src/crepe/api/AI.cpp +++ b/src/crepe/api/AI.cpp @@ -8,8 +8,9 @@ namespace crepe { AI::AI(game_object_id_t id, float max_force) : Component(id), max_force(max_force) {} -void AI::make_circle_path(float radius, const vec2 & center, float start_angle, - bool clockwise) { +void AI::make_circle_path( + float radius, const vec2 & center, float start_angle, bool clockwise +) { if (radius <= 0) { throw std::runtime_error("Radius must be greater than 0"); } @@ -25,19 +26,25 @@ void AI::make_circle_path(float radius, const vec2 & center, float start_angle, if (clockwise) { for (float i = start_angle; i < 2 * M_PI + start_angle; i += step) { - path.push_back(vec2{static_cast<float>(center.x + radius * cos(i)), - static_cast<float>(center.y + radius * sin(i))}); + path.push_back(vec2 { + static_cast<float>(center.x + radius * cos(i)), + static_cast<float>(center.y + radius * sin(i)) + }); } } else { for (float i = start_angle; i > start_angle - 2 * M_PI; i -= step) { - path.push_back(vec2{static_cast<float>(center.x + radius * cos(i)), - static_cast<float>(center.y + radius * sin(i))}); + path.push_back(vec2 { + static_cast<float>(center.x + radius * cos(i)), + static_cast<float>(center.y + radius * sin(i)) + }); } } } -void AI::make_oval_path(float radius_x, float radius_y, const vec2 & center, float start_angle, - bool clockwise, float rotation) { +void AI::make_oval_path( + float radius_x, float radius_y, const vec2 & center, float start_angle, bool clockwise, + float rotation +) { if (radius_x <= 0 && radius_y <= 0) { throw std::runtime_error("Radius must be greater than 0"); } @@ -73,14 +80,16 @@ void AI::make_oval_path(float radius_x, float radius_y, const vec2 & center, flo if (clockwise) { for (float i = start_angle; i < 2 * M_PI + start_angle; i += step) { - vec2 point = {static_cast<float>(center.x + radius_x * cos(i)), - static_cast<float>(center.y + radius_y * sin(i))}; + vec2 point + = {static_cast<float>(center.x + radius_x * cos(i)), + static_cast<float>(center.y + radius_y * sin(i))}; path.push_back(rotate_point(point, center)); } } else { for (float i = start_angle; i > start_angle - 2 * M_PI; i -= step) { - vec2 point = {static_cast<float>(center.x + radius_x * cos(i)), - static_cast<float>(center.y + radius_y * sin(i))}; + vec2 point + = {static_cast<float>(center.x + radius_x * cos(i)), + static_cast<float>(center.y + radius_y * sin(i))}; path.push_back(rotate_point(point, center)); } } diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h index c780a91..bee11b3 100644 --- a/src/crepe/api/AI.h +++ b/src/crepe/api/AI.h @@ -70,8 +70,10 @@ public: * \param start_angle The start angle of the circle (in radians) * \param clockwise The direction of the circle */ - void make_circle_path(float radius, const vec2 & center = {0, 0}, float start_angle = 0, - bool clockwise = true); + void make_circle_path( + float radius, const vec2 & center = {0, 0}, float start_angle = 0, + bool clockwise = true + ); /** * \brief Make an oval path (for the path following behavior) * @@ -84,8 +86,10 @@ public: * \param clockwise The direction of the oval * \param rotation The rotation of the oval (in radians) */ - void make_oval_path(float radius_x, float radius_y, const vec2 & center = {0, 0}, - float start_angle = 0, bool clockwise = true, float rotation = 0); + void make_oval_path( + float radius_x, float radius_y, const vec2 & center = {0, 0}, float start_angle = 0, + bool clockwise = true, float rotation = 0 + ); public: //! The maximum force that can be applied to the entity (higher values will make the entity adjust faster) diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp index 4ce4bf0..c558d86 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" @@ -7,8 +7,10 @@ using namespace crepe; -Animator::Animator(game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size, - const uvec2 & grid_size, const Animator::Data & data) +Animator::Animator( + game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size, + const uvec2 & grid_size, const Animator::Data & data +) : Component(id), spritesheet(spritesheet), grid_size(grid_size), @@ -52,6 +54,6 @@ void Animator::set_anim(int col) { void Animator::next_anim() { Animator::Data & ctx = this->data; - ctx.row = ctx.row++ % this->grid_size.x; + ctx.row = ++ctx.row % this->grid_size.x; this->spritesheet.mask.x = ctx.row * this->spritesheet.mask.w; } diff --git a/src/crepe/api/Animator.h b/src/crepe/api/Animator.h index 5918800..95539d3 100644 --- a/src/crepe/api/Animator.h +++ b/src/crepe/api/Animator.h @@ -1,5 +1,6 @@ #pragma once +#include "../manager/LoopTimerManager.h" #include "../types.h" #include "Component.h" @@ -83,8 +84,10 @@ public: * This constructor sets up the Animator with the given parameters, and initializes the * animation system. */ - Animator(game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size, - const uvec2 & grid_size, const Animator::Data & data); + Animator( + game_object_id_t id, Sprite & spritesheet, const ivec2 & single_frame_size, + const uvec2 & grid_size, const Animator::Data & data + ); ~Animator(); // dbg_trace public: @@ -97,6 +100,12 @@ private: //! The maximum number of rows and columns inside the spritesheet const uvec2 grid_size; + // the time elapsed from a frame duration + duration_t elapsed_time = {}; + + // frame counter + unsigned int frame = 0; + //! Uses the spritesheet friend AnimatorSystem; }; diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp index e148367..bab82e7 100644 --- a/src/crepe/api/Asset.cpp +++ b/src/crepe/api/Asset.cpp @@ -50,5 +50,5 @@ string Asset::whereami() const noexcept { bool Asset::operator==(const Asset & other) const noexcept { return this->src == other.src; } size_t std::hash<const Asset>::operator()(const Asset & asset) const noexcept { - return std::hash<string>{}(asset.get_path()); + return std::hash<string> {}(asset.get_path()); }; diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index b20e490..eaa56e8 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -68,7 +68,7 @@ private: typeof(loop) last_loop = loop; //! \} //! This source's voice handle - SoundHandle voice{}; + SoundHandle voice {}; }; } // namespace crepe 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 a893d41..f6b358d 100644 --- a/src/crepe/api/BoxCollider.cpp +++ b/src/crepe/api/BoxCollider.cpp @@ -4,7 +4,8 @@ using namespace crepe; -BoxCollider::BoxCollider(game_object_id_t game_object_id, const vec2 & dimensions, - const vec2 & offset) +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 d643e7f..229b90f 100644 --- a/src/crepe/api/BoxCollider.h +++ b/src/crepe/api/BoxCollider.h @@ -13,8 +13,9 @@ namespace crepe { */ class BoxCollider : public Collider { public: - BoxCollider(game_object_id_t game_object_id, const vec2 & dimensions, - const vec2 & offset = {0, 0}); + 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/Button.cpp b/src/crepe/api/Button.cpp index 40153c9..8eadd89 100644 --- a/src/crepe/api/Button.cpp +++ b/src/crepe/api/Button.cpp @@ -2,7 +2,10 @@ namespace crepe { -Button::Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset) - : UIObject(id, dimensions, offset) {} +Button::Button( + game_object_id_t id, const vec2 & dimensions, const Data & data, const vec2 & offset +) + : UIObject(id, dimensions, offset), + data(data) {} } // namespace crepe diff --git a/src/crepe/api/Button.h b/src/crepe/api/Button.h index d42527e..e986c04 100644 --- a/src/crepe/api/Button.h +++ b/src/crepe/api/Button.h @@ -1,8 +1,7 @@ #pragma once -#include <functional> +#include "../types.h" -#include "Event.h" #include "UIObject.h" namespace crepe { @@ -21,14 +20,24 @@ namespace crepe { */ class Button : public UIObject { public: + struct Data { + //! variable indicating if transform is relative to camera(false) or world(true) + bool world_space = false; + }; + +public: /** * \brief Constructs a Button with the specified game object ID and dimensions. * * \param id The unique ID of the game object associated with this button. * \param dimensions The width and height of the UIObject * \param offset The offset relative this GameObjects Transform + * \param data additional data the button has */ - Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset); + Button( + game_object_id_t id, const vec2 & dimensions, const Data & data, + const vec2 & offset = {0, 0} + ); /** * \brief Get the maximum number of instances for this component * @@ -38,6 +47,9 @@ public: */ virtual int get_instances_max() const { return 1; } +public: + Data data; + private: //! friend relation hover variable friend class InputSystem; diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index e8d6f92..2bee3fb 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -13,7 +13,7 @@ target_sources(crepe PUBLIC Animator.cpp BoxCollider.cpp CircleCollider.cpp - LoopManager.cpp + Engine.cpp Asset.cpp EventHandler.cpp Script.cpp @@ -47,7 +47,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..b1466b5 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,8 +6,9 @@ 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 ivec2 & screen, const vec2 & viewport_size, const Data & data +) : Component(id), screen(screen), viewport_size(viewport_size), diff --git a/src/crepe/api/Camera.h b/src/crepe/api/Camera.h index 54d9a73..3191b04 100644 --- a/src/crepe/api/Camera.h +++ b/src/crepe/api/Camera.h @@ -44,8 +44,10 @@ public: * \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 ivec2 & screen, const vec2 & viewport_size, + const Camera::Data & data + ); ~Camera(); // dbg_trace only public: diff --git a/src/crepe/api/CircleCollider.cpp b/src/crepe/api/CircleCollider.cpp index 90ab5e7..e72800c 100644 --- a/src/crepe/api/CircleCollider.cpp +++ b/src/crepe/api/CircleCollider.cpp @@ -2,7 +2,8 @@ using namespace crepe; -CircleCollider::CircleCollider(game_object_id_t game_object_id, float radius, - const vec2 & offset) +CircleCollider::CircleCollider( + game_object_id_t game_object_id, float radius, const vec2 & offset +) : Collider(game_object_id, offset), radius(radius) {} diff --git a/src/crepe/api/CircleCollider.h b/src/crepe/api/CircleCollider.h index 22da836..e6ad4fa 100644 --- a/src/crepe/api/CircleCollider.h +++ b/src/crepe/api/CircleCollider.h @@ -13,8 +13,9 @@ namespace crepe { */ class CircleCollider : public Collider { public: - CircleCollider(game_object_id_t game_object_id, float radius, - const vec2 & offset = {0, 0}); + CircleCollider( + game_object_id_t game_object_id, float radius, const vec2 & offset = {0, 0} + ); //! Radius of the circle collider. float radius; diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp index 29bd77a..d0e3b35 100644 --- a/src/crepe/api/Color.cpp +++ b/src/crepe/api/Color.cpp @@ -2,11 +2,13 @@ using namespace crepe; -const Color Color::WHITE{0xff, 0xff, 0xff}; -const Color Color::RED{0xff, 0x00, 0x00}; -const Color Color::GREEN{0x00, 0xff, 0x00}; -const Color Color::BLUE{0x00, 0x00, 0xff}; -const Color Color::BLACK{0x00, 0x00, 0x00}; -const Color Color::CYAN{0x00, 0xff, 0xff}; -const Color Color::YELLOW{0xff, 0xff, 0x00}; -const Color Color::MAGENTA{0xff, 0x00, 0xff}; +const Color Color::WHITE {0xff, 0xff, 0xff}; +const Color Color::RED {0xff, 0x00, 0x00}; +const Color Color::GREEN {0x00, 0xff, 0x00}; +const Color Color::BLUE {0x00, 0x00, 0xff}; +const Color Color::BLACK {0x00, 0x00, 0x00}; +const Color Color::CYAN {0x00, 0xff, 0xff}; +const Color Color::YELLOW {0xff, 0xff, 0x00}; +const Color Color::MAGENTA {0xff, 0x00, 0xff}; +const Color Color::GREY {0x80, 0x80, 0x80}; +const Color Color::GOLD {249, 205, 91}; diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h index 84edb5c..dbfd0ed 100644 --- a/src/crepe/api/Color.h +++ b/src/crepe/api/Color.h @@ -18,6 +18,8 @@ struct Color { static const Color MAGENTA; static const Color YELLOW; static const Color BLACK; + static const Color GREY; + static const Color GOLD; }; } // namespace crepe diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 6b9e3ca..9c226a3 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -85,7 +85,7 @@ struct Config final { * This config option is the font size at which all fonts will be loaded initially. * */ - unsigned int size = 16; + unsigned int size = 500; } font; //! Configuration for click tolerance. struct { diff --git a/src/crepe/api/Engine.cpp b/src/crepe/api/Engine.cpp new file mode 100644 index 0000000..0bbe51f --- /dev/null +++ b/src/crepe/api/Engine.cpp @@ -0,0 +1,68 @@ +#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 (this->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(); + this->scene_manager.load_next_scene(); + } 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..452a856 --- /dev/null +++ b/src/crepe/api/Engine.h @@ -0,0 +1,81 @@ +#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; + + //! SystemManager + SystemManager system_manager {mediator}; + + //! SDLContext instance + SDLContext sdl_context {mediator}; + + //! Resource manager instance + ResourceManager resource_manager {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}; + //! Save manager instance + SaveManager save_manager {mediator}; + //! ReplayManager instance + ReplayManager replay_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/Event.h b/src/crepe/api/Event.h index 8e38280..7d4df21 100644 --- a/src/crepe/api/Event.h +++ b/src/crepe/api/Event.h @@ -10,15 +10,14 @@ namespace crepe { /** - * \brief Base class for all event types in the system. + * \brief Base struct for all event types in the system. */ -class Event {}; +struct Event {}; /** * \brief Event triggered when a key is pressed. */ -class KeyPressEvent : public Event { -public: +struct KeyPressEvent : public Event { //! false if first time press, true if key is repeated bool repeat = false; @@ -29,8 +28,7 @@ public: /** * \brief Event triggered when a key is released. */ -class KeyReleaseEvent : public Event { -public: +struct KeyReleaseEvent : public Event { //! The key that was released. Keycode key = Keycode::NONE; }; @@ -38,8 +36,7 @@ public: /** * \brief Event triggered when a mouse button is pressed. */ -class MousePressEvent : public Event { -public: +struct MousePressEvent : public Event { //! mouse position in world coordinates (game units). vec2 mouse_pos = {0, 0}; @@ -50,8 +47,7 @@ public: /** * \brief Event triggered when a mouse button is clicked (press and release). */ -class MouseClickEvent : public Event { -public: +struct MouseClickEvent : public Event { //! mouse position in world coordinates (game units). vec2 mouse_pos = {0, 0}; @@ -62,8 +58,7 @@ public: /** * \brief Event triggered when a mouse button is released. */ -class MouseReleaseEvent : public Event { -public: +struct MouseReleaseEvent : public Event { //! mouse position in world coordinates (game units). vec2 mouse_pos = {0, 0}; @@ -74,8 +69,7 @@ public: /** * \brief Event triggered when the mouse is moved. */ -class MouseMoveEvent : public Event { -public: +struct MouseMoveEvent : public Event { //! mouse position in world coordinates (game units). vec2 mouse_pos = {0, 0}; //! The change in mouse position relative to the last position (in pixels). @@ -85,8 +79,7 @@ public: /** * \brief Event triggered when the mouse is moved. */ -class MouseScrollEvent : public Event { -public: +struct MouseScrollEvent : public Event { //! mouse position in world coordinates (game units) when the scroll happened. vec2 mouse_pos = {0, 0}; //! scroll direction (-1 = down, 1 = up) @@ -98,20 +91,19 @@ public: /** * \brief Event triggered to indicate the application is shutting down. */ -class ShutDownEvent : public Event {}; +struct ShutDownEvent : public Event {}; /** * \brief Event triggered to indicate the window is overlapped by another window. * * When two windows overlap the bottom window gets distorted and that window has to be redrawn. */ -class WindowExposeEvent : public Event {}; +struct WindowExposeEvent : public Event {}; /** * \brief Event triggered to indicate the window is resized. */ -class WindowResizeEvent : public Event { -public: +struct WindowResizeEvent : public Event { //! new window dimensions ivec2 dimensions = {0, 0}; }; @@ -119,8 +111,7 @@ public: /** * \brief Event triggered to indicate the window is moved. */ -class WindowMoveEvent : public Event { -public: +struct WindowMoveEvent : public Event { //! The change in position relative to the last position (in pixels). ivec2 delta_move = {0, 0}; }; @@ -128,12 +119,12 @@ public: /** * \brief Event triggered to indicate the window is minimized. */ -class WindowMinimizeEvent : public Event {}; +struct WindowMinimizeEvent : public Event {}; /** * \brief Event triggered to indicate the window is maximized */ -class WindowMaximizeEvent : public Event {}; +struct WindowMaximizeEvent : public Event {}; /** * \brief Event triggered to indicate the window gained focus @@ -141,7 +132,7 @@ class WindowMaximizeEvent : public Event {}; * This event is triggered when the window receives focus, meaning it becomes the active window * for user interaction. */ -class WindowFocusGainEvent : public Event {}; +struct WindowFocusGainEvent : public Event {}; /** * \brief Event triggered to indicate the window lost focus @@ -149,6 +140,6 @@ class WindowFocusGainEvent : public Event {}; * This event is triggered when the window loses focus, meaning it is no longer the active window * for user interaction. */ -class WindowFocusLostEvent : public Event {}; +struct WindowFocusLostEvent : public Event {}; } // namespace crepe diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp index 9ef4682..100e210 100644 --- a/src/crepe/api/GameObject.cpp +++ b/src/crepe/api/GameObject.cpp @@ -7,20 +7,19 @@ 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 +31,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..043913a 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,22 @@ 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 +79,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/LoopManager.cpp b/src/crepe/api/LoopManager.cpp deleted file mode 100644 index 7a78019..0000000 --- a/src/crepe/api/LoopManager.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "../facade/SDLContext.h" -#include "../manager/EventManager.h" -#include "../manager/LoopTimerManager.h" -#include "../system/AISystem.h" -#include "../system/AnimatorSystem.h" -#include "../system/AudioSystem.h" -#include "../system/CollisionSystem.h" -#include "../system/InputSystem.h" -#include "../system/ParticleSystem.h" -#include "../system/PhysicsSystem.h" -#include "../system/RenderSystem.h" -#include "../system/ScriptSystem.h" -#include "../util/Log.h" - -#include "LoopManager.h" - -using namespace crepe; -using namespace std; - -LoopManager::LoopManager() { - this->load_system<AnimatorSystem>(); - this->load_system<CollisionSystem>(); - this->load_system<ParticleSystem>(); - this->load_system<PhysicsSystem>(); - this->load_system<RenderSystem>(); - this->load_system<ScriptSystem>(); - this->load_system<InputSystem>(); - this->event_manager.subscribe<ShutDownEvent>( - [this](const ShutDownEvent & event) { return this->on_shutdown(event); }); - this->load_system<AudioSystem>(); - this->load_system<AISystem>(); -} -void LoopManager::start() { - this->setup(); - this->loop(); -} - -void LoopManager::setup() { - this->game_running = true; - this->loop_timer.start(); - this->scene_manager.load_next_scene(); -} - -void LoopManager::loop() { - try { - while (game_running) { - this->loop_timer.update(); - - while (this->loop_timer.get_lag() >= this->loop_timer.get_fixed_delta_time()) { - this->fixed_update(); - this->loop_timer.advance_fixed_elapsed_time(); - } - - this->frame_update(); - this->loop_timer.enforce_frame_rate(); - } - } catch (const exception & e) { - Log::logf(Log::Level::ERROR, "Exception caught in main loop: {}", e.what()); - this->event_manager.trigger_event<ShutDownEvent>(ShutDownEvent{}); - } -} - -// will be called at a fixed interval -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(); - this->get_system<AudioSystem>().update(); -} - -// will be called every frame -void LoopManager::frame_update() { - this->scene_manager.load_next_scene(); - this->get_system<AnimatorSystem>().update(); - //render - this->get_system<RenderSystem>().update(); -} - -bool LoopManager::on_shutdown(const ShutDownEvent & e) { - this->game_running = false; - // propagate to possible user ShutDownEvent listeners - return false; -} diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h deleted file mode 100644 index 124cd3a..0000000 --- a/src/crepe/api/LoopManager.h +++ /dev/null @@ -1,123 +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; - /** - * \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; - - //! SDLContext instance - SDLContext sdl_context{mediator}; - //! Resource manager instance - ResourceManager resource_manager{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}; - //! Save manager instance - SaveManager save_manager{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 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 4f54bbd..341c1e2 100644 --- a/src/crepe/api/ParticleEmitter.cpp +++ b/src/crepe/api/ParticleEmitter.cpp @@ -2,9 +2,11 @@ #include "api/Sprite.h" using namespace crepe; +using namespace std; -ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite, - 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) { @@ -12,3 +14,16 @@ ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite & 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 8ac2e72..1edd2b5 100644 --- a/src/crepe/api/ParticleEmitter.h +++ b/src/crepe/api/ParticleEmitter.h @@ -84,6 +84,12 @@ 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; diff --git a/src/crepe/api/Scene.cpp b/src/crepe/api/Scene.cpp index ad729d2..84da7e8 100644 --- a/src/crepe/api/Scene.cpp +++ b/src/crepe/api/Scene.cpp @@ -4,8 +4,10 @@ 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) { +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); } diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h index dcca9d4..b50a0fc 100644 --- a/src/crepe/api/Scene.h +++ b/src/crepe/api/Scene.h @@ -60,7 +60,7 @@ private: OptionalRef<Mediator> mediator; //! \} -protected: +public: /** * \brief Retrieve the reference to the SaveManager instance * @@ -69,9 +69,10 @@ protected: 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); + 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); diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index 7531388..06b535f 100644 --- a/src/crepe/api/Script.cpp +++ b/src/crepe/api/Script.cpp @@ -2,6 +2,7 @@ #include "../facade/SDLContext.h" #include "../manager/SceneManager.h" + #include "Script.h" using namespace crepe; @@ -19,6 +20,21 @@ void Script::subscribe(const EventHandler<CollisionEvent> & callback) { this->subscribe_internal(callback, this->game_object_id); } +template <> +void Script::subscribe(const EventHandler<ButtonExitEvent> & callback) { + this->subscribe_internal(callback, this->game_object_id); +} + +template <> +void Script::subscribe(const EventHandler<ButtonPressEvent> & callback) { + this->subscribe_internal(callback, this->game_object_id); +} + +template <> +void Script::subscribe(const EventHandler<ButtonEnterEvent> & callback) { + this->subscribe_internal(callback, this->game_object_id); +} + void Script::set_next_scene(const string & name) { SceneManager & mgr = this->mediator->scene_manager; mgr.set_next_scene(name); @@ -28,6 +44,26 @@ SaveManager & Script::get_save_manager() const { return this->mediator->save_man LoopTimerManager & Script::get_loop_timer() const { return this->mediator->loop_timer; } +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); +} + const keyboard_state_t & Script::get_keyboard_state() const { SDLContext & sdl_context = this->mediator->sdl_context; return sdl_context.get_keyboard_state(); diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index a87af4e..b000d9d 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -6,8 +6,11 @@ #include "../manager/EventManager.h" #include "../manager/LoopTimerManager.h" #include "../manager/Mediator.h" +#include "../manager/ReplayManager.h" #include "../system/CollisionSystem.h" +#include "../system/InputSystem.h" #include "../types.h" +#include "../util/Log.h" #include "../util/OptionalRef.h" namespace crepe { @@ -46,14 +49,23 @@ protected: */ virtual void init() {} /** - * \brief Script update function (empty by default) + * \brief Script fixed 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. + * \note 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) {} + virtual void fixed_update(duration_t delta_time) {} + /** + * \brief Script frame update function (empty by default) + * + * \param delta_time Time since last frame update + * + * \note This function is called during the ScriptSystem::update() routine if the \c + * BehaviorScript component holding this script instance is active. + */ + virtual void frame_update(duration_t delta_time) {} //! \} //! ScriptSystem calls \c init() and \c update() @@ -61,93 +73,121 @@ 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; + //! \} + /** + * \name Timing functions + * \see LoopTimerManager + * \{ + */ //! Retrieve LoopTimerManager reference LoopTimerManager & get_loop_timer() 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}; /** * \brief Utility function to retrieve the keyboard state * \see SDLContext::get_keyboard_state * * \return current keyboard state map with Keycode as key and bool as value(true = pressed, false = not pressed) - * */ const keyboard_state_t & get_keyboard_state() const; /** @@ -155,7 +195,6 @@ protected: * \see SDLContext::get_keyboard_state * * \return Keycode state (true if pressed, false if not pressed). - * */ bool get_key_state(Keycode key) const noexcept; @@ -234,7 +273,42 @@ void Script::subscribe(const EventHandler<CollisionEvent> & callback); template <> void Script::subscribe(const EventHandler<CollisionEvent> & callback, event_channel_t) = delete; - +/** + * \brief Subscribe to ButtonPressEvent for the current GameObject + * + * This is a template specialization for Script::subscribe which automatically sets the event + * channel so the callback handler is only called for ButtonPressEvent events that apply to the + * current GameObject the parent BehaviorScript is attached to. + */ +template <> +void Script::subscribe(const EventHandler<ButtonPressEvent> & callback); +template <> +void Script::subscribe(const EventHandler<ButtonPressEvent> & callback, event_channel_t) + = delete; +/** + * \brief Subscribe to ButtonExitEvent for the current GameObject + * + * This is a template specialization for Script::subscribe which automatically sets the event + * channel so the callback handler is only called for ButtonExitEvent events that apply to the + * current GameObject the parent BehaviorScript is attached to. + */ +template <> +void Script::subscribe(const EventHandler<ButtonExitEvent> & callback); +template <> +void Script::subscribe(const EventHandler<ButtonExitEvent> & callback, event_channel_t) + = delete; +/** + * \brief Subscribe to ButtonEnterEvent for the current GameObject + * + * This is a template specialization for Script::subscribe which automatically sets the event + * channel so the callback handler is only called for ButtonEnterEvent events that apply to the + * current GameObject the parent BehaviorScript is attached to. + */ +template <> +void Script::subscribe(const EventHandler<ButtonEnterEvent> & callback); +template <> +void Script::subscribe(const EventHandler<ButtonEnterEvent> & callback, event_channel_t) + = delete; } // namespace crepe #include "Script.hpp" diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index 225a51c..c7fa6ff 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" @@ -13,7 +14,8 @@ T & Script::get_component() const { RefVector<T> all_components = this->get_components<T>(); if (all_components.size() < 1) throw runtime_error( - format("Script: no component found with type = {}", typeid(T).name())); + format("Script: no component found with type = {}", typeid(T).name()) + ); return all_components.back().get(); } @@ -23,22 +25,39 @@ 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> -void Script::subscribe_internal(const EventHandler<EventType> & callback, - event_channel_t channel) { +void Script::subscribe_internal( + const EventHandler<EventType> & callback, event_channel_t channel +) { 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); + channel + ); this->listeners.push_back(listener); } @@ -52,6 +71,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..3c77e2e 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" @@ -19,3 +19,16 @@ Sprite::Sprite(game_object_id_t id, const Asset & texture, const Sprite::Data & } Sprite::~Sprite() { dbg_trace(); } + +unique_ptr<Component> Sprite::save() const { return unique_ptr<Component>(new Sprite(*this)); } + +void Sprite::restore(const Component & snapshot) { + *this = static_cast<const Sprite &>(snapshot); +} + +Sprite & Sprite::operator=(const Sprite & snapshot) { + this->active = snapshot.active; + this->data = snapshot.data; + this->mask = snapshot.mask; + return *this; +} diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h index a3fc319..3565bed 100644 --- a/src/crepe/api/Sprite.h +++ b/src/crepe/api/Sprite.h @@ -42,10 +42,10 @@ public: FlipSettings flip; //! Layer sorting level of the sprite - const int sorting_in_layer = 0; + int sorting_in_layer = 0; //! Order within the sorting layer - const int order_in_layer = 0; + int order_in_layer = 0; /** * \brief width and height of the sprite in game units @@ -119,6 +119,12 @@ private: //! Render area of the sprite this will also be adjusted by the AnimatorSystem if an Animator // object is present in GameObject. this is in sprite pixels Rect mask; + +protected: + virtual std::unique_ptr<Component> save() const; + Sprite(const Sprite &) = default; + virtual void restore(const Component & snapshot); + virtual Sprite & operator=(const Sprite &); }; } // namespace crepe diff --git a/src/crepe/api/Text.cpp b/src/crepe/api/Text.cpp index 4a94180..e5cc39d 100644 --- a/src/crepe/api/Text.cpp +++ b/src/crepe/api/Text.cpp @@ -1,10 +1,27 @@ +#include "../types.h" + #include "Text.h" using namespace crepe; +using namespace std; -Text::Text(game_object_id_t id, const vec2 & dimensions, const vec2 & offset, - const std::string & font_family, const Data & data, const std::string & text) +Text::Text( + game_object_id_t id, const vec2 & dimensions, const std::string & font_family, + const Data & data, const vec2 & offset, const std::string & text +) : UIObject(id, dimensions, offset), text(text), data(data), font_family(font_family) {} + +unique_ptr<Component> Text::save() const { return unique_ptr<Component>(new Text(*this)); } + +void Text::restore(const Component & snapshot) { *this = static_cast<const Text &>(snapshot); } + +Text & Text::operator=(const Text & snapshot) { + this->active = snapshot.active; + this->data = snapshot.data; + this->text = snapshot.text; + this->font_family = snapshot.font_family; + return *this; +} diff --git a/src/crepe/api/Text.h b/src/crepe/api/Text.h index da40141..859490e 100644 --- a/src/crepe/api/Text.h +++ b/src/crepe/api/Text.h @@ -3,6 +3,8 @@ #include <optional> #include <string> +#include "../types.h" + #include "Asset.h" #include "Color.h" #include "UIObject.h" @@ -17,22 +19,8 @@ class Text : public UIObject { public: //! Text data that does not have to be set in the constructor struct Data { - /** - * \brief fontsize for text rendering - * - * \note this is not the actual font size that is loaded in. - * - * Since SDL_TTF requires the font size when loading in the font it is not possible to switch the font size. - * The default font size that is loaded is set in the Config. - * Instead this value is used to upscale the font texture which can cause blurring or distorted text when upscaling or downscaling too much. - */ - unsigned int font_size = 16; - - //! Layer sorting level of the text - const int sorting_in_layer = 0; - - //! Order within the sorting text - const int order_in_layer = 0; + //! variable indicating if transform is relative to camera(false) or world(true) + bool world_space = false; //! Label text color. Color text_color = Color::BLACK; @@ -48,8 +36,10 @@ public: * \param data Data struct containing extra text parameters. * \param font Optional font asset that can be passed or left empty. */ - Text(game_object_id_t id, const vec2 & dimensions, const vec2 & offset, - const std::string & font_family, const Data & data, const std::string & text = ""); + Text( + game_object_id_t id, const vec2 & dimensions, const std::string & font_family, + const Data & data, const vec2 & offset = {0, 0}, const std::string & text = "" + ); //! Label text. std::string text = ""; @@ -59,6 +49,12 @@ public: std::optional<Asset> font; //! Data instance Data data; + +protected: + virtual std::unique_ptr<Component> save() const; + Text(const Text &) = default; + virtual void restore(const Component & snapshot); + virtual Text & operator=(const Text &); }; } // namespace crepe diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp index a85b792..b70174c 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/api/UIObject.h b/src/crepe/api/UIObject.h index f7f4fba..0d9b1f7 100644 --- a/src/crepe/api/UIObject.h +++ b/src/crepe/api/UIObject.h @@ -15,7 +15,7 @@ public: * \param dimensions width and height of the UIObject * \param offset Offset relative to the GameObject Transform */ - UIObject(game_object_id_t id, const vec2 & dimensions, const vec2 & offset); + UIObject(game_object_id_t id, const vec2 & dimensions, const vec2 & offset = {0, 0}); //! Width and height of the UIObject vec2 dimensions; //! Position offset relative to this GameObjects Transform diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp index ae2d4bc..7a3e473 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/FontFacade.cpp b/src/crepe/facade/FontFacade.cpp index 87f95ab..e284f5a 100644 --- a/src/crepe/facade/FontFacade.cpp +++ b/src/crepe/facade/FontFacade.cpp @@ -20,8 +20,9 @@ Asset FontFacade::get_font_asset(const string & font_family) { = FcNameParse(reinterpret_cast<const FcChar8 *>(font_family.c_str())); if (raw_pattern == NULL) throw runtime_error("Failed to create font pattern."); - unique_ptr<FcPattern, function<void(FcPattern *)>> pattern{ - raw_pattern, [](FcPattern * p) { FcPatternDestroy(p); }}; + unique_ptr<FcPattern, function<void(FcPattern *)>> pattern { + raw_pattern, [](FcPattern * p) { FcPatternDestroy(p); } + }; FcConfig * config = FcConfigGetCurrent(); if (config == NULL) throw runtime_error("Failed to get current Fontconfig configuration."); diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 2dae1e7..6c93fb2 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -1,5 +1,6 @@ #include <SDL2/SDL.h> #include <SDL2/SDL_blendmode.h> +#include <SDL2/SDL_hints.h> #include <SDL2/SDL_image.h> #include <SDL2/SDL_keycode.h> #include <SDL2/SDL_pixels.h> @@ -19,7 +20,7 @@ #include "../api/Color.h" #include "../api/Config.h" #include "../api/Sprite.h" -#include "../util/Log.h" +#include "../util/dbg.h" #include "api/Text.h" #include "api/Transform.h" #include "facade/Font.h" @@ -40,9 +41,10 @@ SDLContext::SDLContext(Mediator & mediator) { } auto & cfg = Config::get_instance().window_settings; - 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_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 + ); if (!tmp_window) { throw runtime_error(format("SDLContext: SDL_Window error: {}", SDL_GetError())); } @@ -51,8 +53,8 @@ SDLContext::SDLContext(Mediator & mediator) { SDL_Renderer * tmp_renderer = SDL_CreateRenderer(this->game_window.get(), -1, SDL_RENDERER_ACCELERATED); if (!tmp_renderer) { - throw runtime_error( - format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError())); + throw runtime_error(format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError()) + ); } this->game_renderer @@ -105,7 +107,7 @@ const keyboard_state_t & SDLContext::get_keyboard_state() { MouseButton SDLContext::sdl_to_mousebutton(Uint8 sdl_button) { static const std::array<MouseButton, 5> MOUSE_BUTTON_LOOKUP_TABLE = [] { - std::array<MouseButton, 5> table{}; + std::array<MouseButton, 5> table {}; table.fill(MouseButton::NONE); table[SDL_BUTTON_LEFT] = MouseButton::LEFT_MOUSE; @@ -161,7 +163,7 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const { - size / 2 + cam_aux_data.bar_size; } - return SDL_FRect{ + return SDL_FRect { .x = screen_pos.x, .y = screen_pos.y, .w = size.x, @@ -170,6 +172,7 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const { } void SDLContext::draw(const RenderContext & ctx) { + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2"); const Sprite::Data & data = ctx.sprite.data; SDL_RendererFlip render_flip = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * data.flip.flip_x) @@ -185,7 +188,7 @@ void SDLContext::draw(const RenderContext & ctx) { srcrect_ptr = &srcrect; } - SDL_FRect dstrect = this->get_dst_rect(SDLContext::DestinationRectangleData{ + SDL_FRect dstrect = this->get_dst_rect(SDLContext::DestinationRectangleData { .sprite = ctx.sprite, .texture = ctx.texture, .pos = ctx.pos, @@ -195,11 +198,14 @@ void SDLContext::draw(const RenderContext & ctx) { double angle = ctx.angle + data.angle_offset; this->set_color_texture(ctx.texture, ctx.sprite.data.color); - SDL_RenderCopyExF(this->game_renderer.get(), ctx.texture.get_img(), srcrect_ptr, &dstrect, - angle, NULL, render_flip); + SDL_RenderCopyExF( + this->game_renderer.get(), ctx.texture.get_img(), srcrect_ptr, &dstrect, angle, NULL, + render_flip + ); } void SDLContext::draw_text(const RenderText & data) { + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); const Text & text = data.text; const Font & font = data.font; @@ -207,7 +213,7 @@ void SDLContext::draw_text(const RenderText & data) { std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> font_surface; std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> font_texture; - SDL_Color color{ + SDL_Color color { .r = text.data.text_color.r, .g = text.data.text_color.g, .b = text.data.text_color.b, @@ -229,20 +235,28 @@ void SDLContext::draw_text(const RenderText & data) { = {tmp_font_texture, [](SDL_Texture * texture) { SDL_DestroyTexture(texture); }}; vec2 size = text.dimensions * cam_aux_data.render_scale * data.transform.scale; - vec2 screen_pos - = (absoluut_pos - cam_aux_data.cam_pos + (cam_aux_data.zoomed_viewport) / 2) - * cam_aux_data.render_scale - - size / 2 + cam_aux_data.bar_size; + vec2 screen_pos = absoluut_pos; + if (text.data.world_space) { + screen_pos = (screen_pos - cam_aux_data.cam_pos + (cam_aux_data.zoomed_viewport) / 2) + * cam_aux_data.render_scale + - size / 2 + cam_aux_data.bar_size; + } else { + screen_pos + = (screen_pos + (cam_aux_data.zoomed_viewport) / 2) * cam_aux_data.render_scale + - size / 2 + cam_aux_data.bar_size; + } - SDL_FRect dstrect{ + SDL_FRect dstrect { .x = screen_pos.x, .y = screen_pos.y, .w = size.x, .h = size.y, }; - SDL_RenderCopyExF(this->game_renderer.get(), font_texture.get(), NULL, &dstrect, - data.transform.rotation, NULL, SDL_FLIP_NONE); + SDL_RenderCopyExF( + this->game_renderer.get(), font_texture.get(), NULL, &dstrect, data.transform.rotation, + NULL, SDL_FLIP_NONE + ); } void SDLContext::update_camera_view(const Camera & cam, const vec2 & new_pos) { @@ -288,8 +302,10 @@ void SDLContext::update_camera_view(const Camera & cam, const vec2 & new_pos) { render_scale.x = render_scale.y = scale; } - SDL_SetRenderDrawColor(this->game_renderer.get(), cam_data.bg_color.r, cam_data.bg_color.g, - cam_data.bg_color.b, cam_data.bg_color.a); + SDL_SetRenderDrawColor( + this->game_renderer.get(), cam_data.bg_color.r, cam_data.bg_color.g, + cam_data.bg_color.b, cam_data.bg_color.a + ); SDL_Rect bg = { .x = 0, @@ -424,11 +440,12 @@ std::vector<EventData> SDLContext::get_events() { return event_list; } -void SDLContext::handle_window_event(const SDL_WindowEvent & window_event, - std::vector<EventData> & event_list) { +void SDLContext::handle_window_event( + const SDL_WindowEvent & window_event, std::vector<EventData> & event_list +) { switch (window_event.event) { case SDL_WINDOWEVENT_EXPOSED: - event_list.push_back(EventData{EventType::WINDOW_EXPOSE}); + event_list.push_back(EventData {EventType::WINDOW_EXPOSE}); break; case SDL_WINDOWEVENT_RESIZED: event_list.push_back(EventData{ @@ -452,16 +469,16 @@ void SDLContext::handle_window_event(const SDL_WindowEvent & window_event, break; case SDL_WINDOWEVENT_MINIMIZED: - event_list.push_back(EventData{EventType::WINDOW_MINIMIZE}); + event_list.push_back(EventData {EventType::WINDOW_MINIMIZE}); break; case SDL_WINDOWEVENT_MAXIMIZED: - event_list.push_back(EventData{EventType::WINDOW_MAXIMIZE}); + event_list.push_back(EventData {EventType::WINDOW_MAXIMIZE}); break; case SDL_WINDOWEVENT_FOCUS_GAINED: - event_list.push_back(EventData{EventType::WINDOW_FOCUS_GAIN}); + event_list.push_back(EventData {EventType::WINDOW_FOCUS_GAIN}); break; case SDL_WINDOWEVENT_FOCUS_LOST: - event_list.push_back(EventData{EventType::WINDOW_FOCUS_LOST}); + event_list.push_back(EventData {EventType::WINDOW_FOCUS_LOST}); break; } } diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index e570073..bc118f9 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -116,8 +116,9 @@ public: * This method checks if any window events are triggered and adds them to the event_list. * */ - void handle_window_event(const SDL_WindowEvent & window_event, - std::vector<EventData> & event_list); + void handle_window_event( + const SDL_WindowEvent & window_event, std::vector<EventData> & event_list + ); /** * \brief Converts an SDL scan code to the custom Keycode type. * @@ -254,7 +255,7 @@ private: private: //! instance of the font_facade - FontFacade font_facade{}; + FontFacade font_facade {}; public: /** 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..06caa54 100644 --- a/src/crepe/facade/Texture.cpp +++ b/src/crepe/facade/Texture.cpp @@ -1,10 +1,11 @@ -#include "../util/Log.h" -#include "facade/SDLContext.h" -#include "manager/Mediator.h" +#include "../Resource.h" +#include "../facade/SDLContext.h" +#include "../manager/Mediator.h" +#include "../types.h" +#include "../util/dbg.h" -#include "Resource.h" +#include "SDLContext.h" #include "Texture.h" -#include "types.h" using namespace crepe; using namespace std; @@ -23,6 +24,7 @@ Texture::~Texture() { } const ivec2 & Texture::get_size() const noexcept { return this->size; } + const float & Texture::get_ratio() const noexcept { return this->aspect_ratio; } SDL_Texture * Texture::get_img() const noexcept { return this->texture.get(); } 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..245419d 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" @@ -46,14 +46,16 @@ void ComponentManager::delete_all_components() { this->next_id = 0; } -GameObject ComponentManager::new_object(const string & name, const string & tag, - const vec2 & position, double rotation, double scale) { +GameObject ComponentManager::new_object( + const string & name, const string & tag, const vec2 & position, double rotation, + double scale +) { // Find the first available id (taking persistent objects into account) while (this->persistent[this->next_id]) { 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; @@ -64,11 +66,38 @@ void ComponentManager::set_persistent(game_object_id_t id, bool persistent) { } set<game_object_id_t> ComponentManager::get_objects_by_name(const string & name) const { - return this->get_objects_by_predicate<Metadata>( - [name](const Metadata & data) { return data.name == name; }); + return this->get_objects_by_predicate<Metadata>([name](const Metadata & data) { + return data.name == name; + }); } set<game_object_id_t> ComponentManager::get_objects_by_tag(const string & tag) const { - return this->get_objects_by_predicate<Metadata>( - [tag](const Metadata & data) { return data.tag == tag; }); + 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..2eb1f7e 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 @@ -45,16 +38,12 @@ public: * * \note This method automatically assigns a new entity ID */ - GameObject new_object(const std::string & name, const std::string & tag = "", - const vec2 & position = {0, 0}, double rotation = 0, - double scale = 1); + GameObject new_object( + const std::string & name, const std::string & tag = "", 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 +143,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/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp index 9e70865..6d32edb 100644 --- a/src/crepe/manager/ComponentManager.hpp +++ b/src/crepe/manager/ComponentManager.hpp @@ -11,8 +11,10 @@ template <class T, typename... Args> T & ComponentManager::add_component(game_object_id_t id, Args &&... args) { using namespace std; - static_assert(is_base_of<Component, T>::value, - "add_component must recieve a derivative class of Component"); + static_assert( + is_base_of<Component, T>::value, + "add_component must recieve a derivative class of Component" + ); // Determine the type of T (this is used as the key of the unordered_map<>) type_index type = typeid(T); @@ -40,8 +42,8 @@ T & ComponentManager::add_component(game_object_id_t id, Args &&... args) { // Check if the vector size is not greater than get_instances_max int max_instances = instance->get_instances_max(); if (max_instances != -1 && components[type][id].size() >= max_instances) { - throw std::runtime_error( - "Exceeded maximum number of instances for this component type"); + throw std::runtime_error("Exceeded maximum number of instances for this component type" + ); } // store its unique_ptr in the vector<> @@ -95,8 +97,10 @@ template <typename T> RefVector<T> ComponentManager::get_components_by_id(game_object_id_t id) const { using namespace std; - static_assert(is_base_of<Component, T>::value, - "get_components_by_id must recieve a derivative class of Component"); + static_assert( + is_base_of<Component, T>::value, + "get_components_by_id must recieve a derivative class of Component" + ); type_index type = typeid(T); if (!this->components.contains(type)) return {}; @@ -170,8 +174,8 @@ ComponentManager::get_objects_by_predicate(const std::function<bool(const T &)> } template <typename T> -RefVector<T> -ComponentManager::get_components_by_ids(const std::set<game_object_id_t> & ids) const { +RefVector<T> ComponentManager::get_components_by_ids(const std::set<game_object_id_t> & ids +) const { using namespace std; RefVector<T> out = {}; diff --git a/src/crepe/manager/EventManager.h b/src/crepe/manager/EventManager.h index 639e37f..5766a0c 100644 --- a/src/crepe/manager/EventManager.h +++ b/src/crepe/manager/EventManager.h @@ -49,8 +49,8 @@ public: * \return A unique subscription ID associated with the registered callback. */ template <typename EventType> - subscription_t subscribe(const EventHandler<EventType> & callback, - event_channel_t channel = CHANNEL_ALL); + subscription_t + subscribe(const EventHandler<EventType> & callback, event_channel_t channel = CHANNEL_ALL); /** * \brief Unsubscribe a previously registered callback. @@ -106,7 +106,7 @@ private: * \brief Represents an entry in the event queue. */ struct QueueEntry { - std::unique_ptr<Event> event; ///< The event instance. + std::unique_ptr<Event, std::function<void(Event *)>> event; ///< The event instance. event_channel_t channel = CHANNEL_ALL; ///< The channel associated with the event. std::type_index type; ///< The type of the event. }; diff --git a/src/crepe/manager/EventManager.hpp b/src/crepe/manager/EventManager.hpp index a5f4556..1f44943 100644 --- a/src/crepe/manager/EventManager.hpp +++ b/src/crepe/manager/EventManager.hpp @@ -5,24 +5,31 @@ namespace crepe { template <typename EventType> -subscription_t EventManager::subscribe(const EventHandler<EventType> & callback, - event_channel_t channel) { +subscription_t +EventManager::subscribe(const EventHandler<EventType> & callback, event_channel_t channel) { subscription_counter++; std::type_index event_type = typeid(EventType); std::unique_ptr<EventHandlerWrapper<EventType>> handler = std::make_unique<EventHandlerWrapper<EventType>>(callback); std::vector<CallbackEntry> & handlers = this->subscribers[event_type]; - handlers.emplace_back(CallbackEntry{ - .callback = std::move(handler), .channel = channel, .id = subscription_counter}); + handlers.emplace_back(CallbackEntry { + .callback = std::move(handler), .channel = channel, .id = subscription_counter + }); return subscription_counter; } template <typename EventType> void EventManager::queue_event(const EventType & event, event_channel_t channel) { - static_assert(std::is_base_of<Event, EventType>::value, - "EventType must derive from Event"); + static_assert( + std::is_base_of<Event, EventType>::value, "EventType must derive from Event" + ); this->events_queue.push_back(QueueEntry{ - .event = std::make_unique<EventType>(event), + // unique_ptr w/ custom destructor implementation is used because the base Event interface + // can't be polymorphic (= have default virtual destructor) + .event = { + new EventType(event), + [](Event * ev) { delete static_cast<EventType *>(ev); }, + }, .channel = channel, .type = typeid(EventType), }); diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp index a6e4788..b4cd07f 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" @@ -17,9 +17,9 @@ LoopTimerManager::LoopTimerManager(Mediator & mediator) : Manager(mediator) { void LoopTimerManager::start() { this->last_frame_time = std::chrono::steady_clock::now(); - this->elapsed_time = elapsed_time_t{0}; - this->elapsed_fixed_time = elapsed_time_t{0}; - this->delta_time = duration_t{0}; + this->elapsed_time = elapsed_time_t {0}; + this->elapsed_fixed_time = elapsed_time_t {0}; + this->delta_time = duration_t {0}; } void LoopTimerManager::update() { diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h index 76b02d3..279d6b2 100644 --- a/src/crepe/manager/LoopTimerManager.h +++ b/src/crepe/manager/LoopTimerManager.h @@ -6,6 +6,8 @@ namespace crepe { +class Engine; + typedef std::chrono::duration<float> duration_t; typedef std::chrono::duration<unsigned long long, std::micro> elapsed_time_t; @@ -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. * @@ -155,17 +157,17 @@ private: //! 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. - duration_t maximum_delta_time{0.25}; + duration_t maximum_delta_time {0.25}; //! Delta time for the current frame in seconds. - duration_t delta_time{0.0}; + duration_t delta_time {0.0}; //! Target time per frame in seconds - duration_t frame_target_time{1.0 / target_fps}; + duration_t frame_target_time {1.0 / target_fps}; //! Fixed delta time for fixed updates in seconds. - duration_t fixed_delta_time{1.0 / 50.0}; + duration_t fixed_delta_time {1.0 / 50.0}; //! Total elapsed game time in microseconds. - elapsed_time_t elapsed_time{0}; + elapsed_time_t elapsed_time {0}; //! Total elapsed time for fixed updates in microseconds. - elapsed_time_t elapsed_fixed_time{0}; + elapsed_time_t elapsed_fixed_time {0}; typedef std::chrono::steady_clock::time_point time_point_t; //! Time of the last frame. 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..f06a58b --- /dev/null +++ b/src/crepe/manager/ReplayManager.h @@ -0,0 +1,96 @@ +#pragma once + +#include <unordered_map> + +#include "../util/OptionalRef.h" + +#include "ComponentManager.h" +#include "Manager.h" + +namespace crepe { + +//! Handle to recording held by ReplayManager +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/ResourceManager.hpp b/src/crepe/manager/ResourceManager.hpp index cf5c949..4ca6be0 100644 --- a/src/crepe/manager/ResourceManager.hpp +++ b/src/crepe/manager/ResourceManager.hpp @@ -9,17 +9,20 @@ namespace crepe { template <typename T> T & ResourceManager::get(const Asset & asset) { using namespace std; - static_assert(is_base_of<Resource, T>::value, - "cache must recieve a derivative class of Resource"); + static_assert( + is_base_of<Resource, T>::value, "cache must recieve a derivative class of Resource" + ); CacheEntry & entry = this->get_entry(asset); if (entry.resource == nullptr) entry.resource = make_unique<T>(asset, this->mediator); T * concrete_resource = dynamic_cast<T *>(entry.resource.get()); if (concrete_resource == nullptr) - throw runtime_error(format("ResourceManager: mismatch between requested type and " - "actual type of resource ({})", - asset.get_path())); + throw runtime_error(format( + "ResourceManager: mismatch between requested type and " + "actual type of resource ({})", + asset.get_path() + )); return *concrete_resource; } diff --git a/src/crepe/manager/SceneManager.cpp b/src/crepe/manager/SceneManager.cpp index d4ca90b..e6f92db 100644 --- a/src/crepe/manager/SceneManager.cpp +++ b/src/crepe/manager/SceneManager.cpp @@ -17,10 +17,12 @@ void SceneManager::load_next_scene() { // next scene not set if (this->next_scene.empty()) return; - auto it = find_if(this->scenes.begin(), this->scenes.end(), - [&next_scene = this->next_scene](unique_ptr<Scene> & scene) { - return scene.get()->get_name() == next_scene; - }); + auto it = find_if( + this->scenes.begin(), this->scenes.end(), + [&next_scene = this->next_scene](unique_ptr<Scene> & scene) { + return scene.get()->get_name() == next_scene; + } + ); // next scene not found if (it == this->scenes.end()) return; diff --git a/src/crepe/manager/SystemManager.cpp b/src/crepe/manager/SystemManager.cpp new file mode 100644 index 0000000..eabc022 --- /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<ParticleSystem>(); + this->load_system<AISystem>(); + this->load_system<PhysicsSystem>(); + this->load_system<CollisionSystem>(); + this->load_system<AudioSystem>(); + this->load_system<AnimatorSystem>(); + this->load_system<RenderSystem>(); + this->load_system<ReplaySystem>(); + + this->mediator.system_manager = *this; +} + +void SystemManager::fixed_update() { + for (System & system : this->system_order) { + if (!system.active) continue; + system.fixed_update(); + } +} + +void SystemManager::frame_update() { + for (System & system : this->system_order) { + 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..614d90c --- /dev/null +++ b/src/crepe/manager/SystemManager.h @@ -0,0 +1,93 @@ +#pragma once + +#include <memory> +#include <typeindex> +#include <unordered_map> +#include <vector> + +#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 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::vector<std::reference_wrapper<System>> system_order; + /** + * \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..addd274 100644 --- a/src/crepe/api/LoopManager.hpp +++ b/src/crepe/manager/SystemManager.hpp @@ -4,26 +4,20 @@ #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"); + 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,16 +27,18 @@ 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"); + 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); + this->system_order.push_back(*this->systems[type]); } } // namespace crepe diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp index 680dbb8..94445c7 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; @@ -28,7 +28,8 @@ void AISystem::update() { = mgr.get_components_by_id<Rigidbody>(ai.game_object_id); if (rigidbodies.empty()) { throw std::runtime_error( - "AI component must be attached to a GameObject with a Rigidbody component"); + "AI component must be attached to a GameObject with a Rigidbody component" + ); } Rigidbody & rigidbody = rigidbodies.front().get(); if (!rigidbody.active) { @@ -110,8 +111,8 @@ bool AISystem::accumulate_force(const AI & ai, vec2 & running_total, vec2 & forc return true; } -vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody, - const Transform & transform) const { +vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody, const Transform & transform) + const { // Calculate the desired velocity vec2 desired_velocity = ai.seek_target - transform.position; desired_velocity.normalize(); @@ -120,12 +121,12 @@ vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody, return desired_velocity - rigidbody.data.linear_velocity; } -vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody, - const Transform & transform) const { +vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody, const Transform & transform) + const { // Calculate the desired velocity if the entity is within the panic distance vec2 desired_velocity = transform.position - ai.flee_target; if (desired_velocity.length_squared() > ai.square_flee_panic_distance) { - return vec2{0, 0}; + return vec2 {0, 0}; } desired_velocity.normalize(); desired_velocity *= rigidbody.data.max_linear_velocity; @@ -133,8 +134,8 @@ vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody, return desired_velocity - rigidbody.data.linear_velocity; } -vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody, - const Transform & transform) const { +vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody, const Transform & transform) + const { // Calculate the desired velocity (taking into account the deceleration rate) vec2 to_target = ai.arrive_target - transform.position; float distance = to_target.length(); @@ -150,12 +151,12 @@ vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody, return desired_velocity - rigidbody.data.linear_velocity; } - return vec2{0, 0}; + return vec2 {0, 0}; } vec2 AISystem::path_follow(AI & ai, const Rigidbody & rigidbody, const Transform & transform) { if (ai.path.empty()) { - return vec2{0, 0}; + return vec2 {0, 0}; } // Get the target node 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 107b25d..143d5d6 100644 --- a/src/crepe/system/AnimatorSystem.cpp +++ b/src/crepe/system/AnimatorSystem.cpp @@ -1,42 +1,44 @@ - +#include <chrono> #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>(); - float elapsed_time = duration_cast<duration<float>>(timer.get_elapsed_time()).count(); + duration_t elapsed_time = timer.get_delta_time(); 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; - int last_frame = ctx.row; + a.elapsed_time += elapsed_time; + duration_t frame_duration = 1000ms / ctx.fps; int cycle_end = (ctx.cycle_end == -1) ? a.grid_size.x : ctx.cycle_end; - int total_frames = cycle_end - ctx.cycle_start; - - int curr_frame = static_cast<int>(elapsed_time / frame_duration) % total_frames; + if (a.elapsed_time >= frame_duration) { + a.elapsed_time = 0ms; + a.frame++; + if (a.frame == cycle_end) { + a.frame = ctx.cycle_start; + if (!ctx.looping) { + a.active = false; + continue; + } + } + } - ctx.row = ctx.cycle_start + curr_frame; + ctx.row = ctx.cycle_start + a.frame; a.spritesheet.mask.x = ctx.row * a.spritesheet.mask.w; - a.spritesheet.mask.y = (ctx.col * a.spritesheet.mask.h); - - if (!ctx.looping && curr_frame == ctx.cycle_start && last_frame == total_frames - 1) { - a.active = false; - } } } 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..3c2232f 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 @@ -36,6 +36,8 @@ void AudioSystem::diff_update(AudioSource & component, Sound & resource) { if (component.oneshot_play) { component.voice = context.play(resource); + context.set_loop(component.voice, component.loop); + context.set_volume(component.voice, component.volume); component.oneshot_play = false; } if (component.oneshot_stop) { 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 3653d10..571ac70 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -34,7 +34,7 @@ CollisionSystem::CollisionInfo CollisionSystem::CollisionInfo::operator-() const }; } -void CollisionSystem::update() { +void CollisionSystem::fixed_update() { std::vector<CollisionInternal> all_colliders; game_object_id_t id = 0; ComponentManager & mgr = this->mediator.component_manager; @@ -50,9 +50,11 @@ void CollisionSystem::update() { for (BoxCollider & boxcollider : boxcolliders) { if (boxcollider.game_object_id != id) continue; if (!boxcollider.active) continue; - all_colliders.push_back({.id = id, - .collider = collider_variant{boxcollider}, - .info = {transform, rigidbody, metadata}}); + all_colliders.push_back( + {.id = id, + .collider = collider_variant {boxcollider}, + .info = {transform, rigidbody, metadata}} + ); } // Check if the circlecollider is active and has the same id as the rigidbody. RefVector<CircleCollider> circlecolliders @@ -60,9 +62,11 @@ void CollisionSystem::update() { for (CircleCollider & circlecollider : circlecolliders) { if (circlecollider.game_object_id != id) continue; if (!circlecollider.active) continue; - all_colliders.push_back({.id = id, - .collider = collider_variant{circlecollider}, - .info = {transform, rigidbody, metadata}}); + all_colliders.push_back( + {.id = id, + .collider = collider_variant {circlecollider}, + .info = {transform, rigidbody, metadata}} + ); } } @@ -110,8 +114,9 @@ CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) { return collisions_ret; } -bool CollisionSystem::should_collide(const CollisionInternal & self, - const CollisionInternal & other) const { +bool CollisionSystem::should_collide( + const CollisionInternal & self, const CollisionInternal & other +) const { const Rigidbody::Data & self_rigidbody = self.info.rigidbody.data; const Rigidbody::Data & other_rigidbody = other.info.rigidbody.data; @@ -133,9 +138,9 @@ bool CollisionSystem::should_collide(const CollisionInternal & self, return false; } -CollisionSystem::CollisionInternalType -CollisionSystem::get_collider_type(const collider_variant & collider1, - const collider_variant & collider2) const { +CollisionSystem::CollisionInternalType CollisionSystem::get_collider_type( + const collider_variant & collider1, const collider_variant & collider2 +) const { if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider1)) { if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) { return CollisionInternalType::CIRCLE_CIRCLE; @@ -151,8 +156,9 @@ CollisionSystem::get_collider_type(const collider_variant & collider1, } } -bool CollisionSystem::detect_collision(CollisionInternal & self, CollisionInternal & other, - const CollisionInternalType & type) { +bool CollisionSystem::detect_collision( + CollisionInternal & self, CollisionInternal & other, const CollisionInternalType & type +) { vec2 resolution; switch (type) { case CollisionInternalType::BOX_BOX: { @@ -177,10 +183,11 @@ bool CollisionSystem::detect_collision(CollisionInternal & self, CollisionIntern = {.collider = std::get<std::reference_wrapper<BoxCollider>>(self.collider), .transform = self.info.transform, .rigidbody = self.info.rigidbody}; - const CircleColliderInternal CIRCLE2 = { - .collider = std::get<std::reference_wrapper<CircleCollider>>(other.collider), - .transform = other.info.transform, - .rigidbody = other.info.rigidbody}; + const CircleColliderInternal CIRCLE2 + = {.collider + = std::get<std::reference_wrapper<CircleCollider>>(other.collider), + .transform = other.info.transform, + .rigidbody = other.info.rigidbody}; // Get resolution vector from box-circle collision detection resolution = this->get_box_circle_detection(BOX1, CIRCLE2); // If no collision (NaN values), return false @@ -195,10 +202,11 @@ bool CollisionSystem::detect_collision(CollisionInternal & self, CollisionIntern = {.collider = std::get<std::reference_wrapper<CircleCollider>>(self.collider), .transform = self.info.transform, .rigidbody = self.info.rigidbody}; - const CircleColliderInternal CIRCLE2 = { - .collider = std::get<std::reference_wrapper<CircleCollider>>(other.collider), - .transform = other.info.transform, - .rigidbody = other.info.rigidbody}; + const CircleColliderInternal CIRCLE2 + = {.collider + = std::get<std::reference_wrapper<CircleCollider>>(other.collider), + .transform = other.info.transform, + .rigidbody = other.info.rigidbody}; // Get resolution vector from circle-circle collision detection resolution = this->get_circle_circle_detection(CIRCLE1, CIRCLE2); // If no collision (NaN values), return false @@ -239,9 +247,10 @@ bool CollisionSystem::detect_collision(CollisionInternal & self, CollisionIntern return true; } -vec2 CollisionSystem::get_box_box_detection(const BoxColliderInternal & box1, - const BoxColliderInternal & box2) const { - vec2 resolution{NAN, NAN}; +vec2 CollisionSystem::get_box_box_detection( + const BoxColliderInternal & box1, const BoxColliderInternal & box2 +) const { + vec2 resolution {NAN, NAN}; // Get current positions of colliders vec2 pos1 = AbsolutePosition::get_position(box1.transform, box1.collider.offset); vec2 pos2 = AbsolutePosition::get_position(box2.transform, box2.collider.offset); @@ -282,8 +291,9 @@ vec2 CollisionSystem::get_box_box_detection(const BoxColliderInternal & box1, return resolution; } -vec2 CollisionSystem::get_box_circle_detection(const BoxColliderInternal & box, - const CircleColliderInternal & circle) const { +vec2 CollisionSystem::get_box_circle_detection( + const BoxColliderInternal & box, const CircleColliderInternal & circle +) const { /// Get current positions of colliders vec2 box_pos = AbsolutePosition::get_position(box.transform, box.collider.offset); vec2 circle_pos = AbsolutePosition::get_position(circle.transform, circle.collider.offset); @@ -324,14 +334,15 @@ vec2 CollisionSystem::get_box_circle_detection(const BoxColliderInternal & box, float penetration_depth = scaled_circle_radius - distance; // Compute the resolution vector - return vec2{collision_normal * penetration_depth}; + return vec2 {collision_normal * penetration_depth}; } // No collision - return vec2{NAN, NAN}; + return vec2 {NAN, NAN}; } vec2 CollisionSystem::get_circle_circle_detection( - const CircleColliderInternal & circle1, const CircleColliderInternal & circle2) const { + const CircleColliderInternal & circle1, const CircleColliderInternal & circle2 +) const { // Get current positions of colliders vec2 final_position1 = AbsolutePosition::get_position(circle1.transform, circle1.collider.offset); @@ -371,7 +382,7 @@ vec2 CollisionSystem::get_circle_circle_detection( return resolution; } // No collision - return vec2{NAN, NAN}; + return vec2 {NAN, NAN}; ; } @@ -402,17 +413,17 @@ CollisionSystem::resolution_correction(vec2 & resolution, const Rigidbody::Data return resolution_direction; } -CollisionSystem::CollisionInfo -CollisionSystem::get_collision_info(const CollisionInternal & in_self, - const CollisionInternal & in_other) const { +CollisionSystem::CollisionInfo CollisionSystem::get_collision_info( + const CollisionInternal & in_self, const CollisionInternal & in_other +) const { - crepe::CollisionSystem::ColliderInfo self{ + crepe::CollisionSystem::ColliderInfo self { .transform = in_self.info.transform, .rigidbody = in_self.info.rigidbody, .metadata = in_self.info.metadata, }; - crepe::CollisionSystem::ColliderInfo other{ + crepe::CollisionSystem::ColliderInfo other { .transform = in_other.info.transform, .rigidbody = in_other.info.rigidbody, .metadata = in_other.info.metadata, diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h index 7be280a..ff2d35f 100644 --- a/src/crepe/system/CollisionSystem.h +++ b/src/crepe/system/CollisionSystem.h @@ -60,8 +60,8 @@ public: private: //! A variant type that can hold either a BoxCollider or a CircleCollider. - using collider_variant = std::variant<std::reference_wrapper<BoxCollider>, - std::reference_wrapper<CircleCollider>>; + using collider_variant = std::variant< + std::reference_wrapper<BoxCollider>, std::reference_wrapper<CircleCollider>>; //! Enum representing the types of collider pairs for collision detection. enum class CollisionInternalType { @@ -102,7 +102,7 @@ private: public: //! Updates the collision system by checking for collisions between colliders and handling them. - void update() override; + void fixed_update() override; private: /** @@ -114,8 +114,9 @@ private: * \param collider2 Second collider variant (BoxCollider or CircleCollider). * \return The combined type of the two colliders. */ - CollisionInternalType get_collider_type(const collider_variant & collider1, - const collider_variant & collider2) const; + CollisionInternalType get_collider_type( + const collider_variant & collider1, const collider_variant & collider2 + ) const; private: /** @@ -128,8 +129,8 @@ private: * \param data1 Collision data for the first collider. * \param data2 Collision data for the second collider. */ - CollisionInfo get_collision_info(const CollisionInternal & data1, - const CollisionInternal & data2) const; + CollisionInfo + get_collision_info(const CollisionInternal & data1, const CollisionInternal & data2) const; /** * \brief Corrects the collision resolution vector and determines its direction. @@ -221,8 +222,10 @@ private: * \param other_metadata Rigidbody of second object * \return Returns true if there is at least one comparison found. */ - bool should_collide(const CollisionInternal & self, - const CollisionInternal & other) const; //done + bool should_collide( + const CollisionInternal & self, + const CollisionInternal & other + ) const; //done /** * \brief Checks for collision between two colliders. @@ -236,8 +239,10 @@ private: * \param type The type of collider pair. * \return True if a collision is detected, otherwise false. */ - bool detect_collision(CollisionInternal & first_info, CollisionInternal & second_info, - const CollisionInternalType & type); + bool detect_collision( + CollisionInternal & first_info, CollisionInternal & second_info, + const CollisionInternalType & type + ); /** * \brief Detects collisions between two BoxColliders. @@ -250,8 +255,9 @@ private: * \param box2 Information about the second BoxCollider. * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}. */ - vec2 get_box_box_detection(const BoxColliderInternal & box1, - const BoxColliderInternal & box2) const; + vec2 get_box_box_detection( + const BoxColliderInternal & box1, const BoxColliderInternal & box2 + ) const; /** * \brief Check collision for box on circle collider @@ -264,8 +270,9 @@ private: * \param circle2 Information about the circleCollider. * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}. */ - vec2 get_box_circle_detection(const BoxColliderInternal & box1, - const CircleColliderInternal & circle2) const; + vec2 get_box_circle_detection( + const BoxColliderInternal & box1, const CircleColliderInternal & circle2 + ) const; /** * \brief Check collision for circle on circle collider @@ -278,8 +285,9 @@ private: * \param circle2 Information about the second circleCollider. * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}. */ - vec2 get_circle_circle_detection(const CircleColliderInternal & circle1, - const CircleColliderInternal & circle2) const; + vec2 get_circle_circle_detection( + const CircleColliderInternal & circle1, const CircleColliderInternal & circle2 + ) const; }; /** 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 60daa55..be7eda6 100644 --- a/src/crepe/system/InputSystem.cpp +++ b/src/crepe/system/InputSystem.cpp @@ -1,18 +1,18 @@ #include "../api/Button.h" +#include "../api/Config.h" #include "../facade/SDLContext.h" #include "../manager/ComponentManager.h" #include "../manager/EventManager.h" -#include "util/Log.h" #include "InputSystem.h" using namespace crepe; -void InputSystem::update() { +void InputSystem::fixed_update() { ComponentManager & mgr = this->mediator.component_manager; SDLContext & context = this->mediator.sdl_context; std::vector<EventData> event_list = context.get_events(); - RefVector<Camera> cameras = mgr.get_components_by_type<Camera>(); + const RefVector<Camera> cameras = mgr.get_components_by_type<Camera>(); OptionalRef<Camera> curr_cam_ref; // Find the active camera @@ -24,9 +24,8 @@ void InputSystem::update() { if (!curr_cam_ref) return; Camera & current_cam = curr_cam_ref; - RefVector<Transform> transform_vec - = mgr.get_components_by_id<Transform>(current_cam.game_object_id); - Transform & cam_transform = transform_vec.front().get(); + const Transform & cam_transform + = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front(); vec2 camera_origin = cam_transform.position + current_cam.data.postion_offset - (current_cam.viewport_size / 2); @@ -45,12 +44,13 @@ void InputSystem::update() { } } -void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camera_origin, - const Camera & current_cam) { +void InputSystem::handle_mouse_event( + const EventData & event, const vec2 & camera_origin, const Camera & current_cam +) { EventManager & event_mgr = this->mediator.event_manager; vec2 adjusted_mouse; adjusted_mouse.x = event.data.mouse_data.mouse_position.x + camera_origin.x; - adjusted_mouse.x = event.data.mouse_data.mouse_position.y + camera_origin.y; + adjusted_mouse.y = event.data.mouse_data.mouse_position.y + camera_origin.y; // Check if the mouse is within the viewport if ((adjusted_mouse.x < camera_origin.x || adjusted_mouse.x > camera_origin.x + current_cam.viewport_size.x @@ -83,7 +83,9 @@ void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camer .mouse_pos = adjusted_mouse, .button = event.data.mouse_data.mouse_button, }); - this->handle_click(event.data.mouse_data.mouse_button, adjusted_mouse); + this->handle_click( + event.data.mouse_data.mouse_button, adjusted_mouse, current_cam + ); } break; } @@ -93,7 +95,7 @@ void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camer .mouse_pos = adjusted_mouse, .mouse_delta = event.data.mouse_data.rel_mouse_move, }); - this->handle_move(event, adjusted_mouse); + this->handle_move(event, adjusted_mouse, current_cam); break; case EventType::MOUSE_WHEEL: @@ -115,7 +117,8 @@ void InputSystem::handle_non_mouse_event(const EventData & event) { case EventType::KEY_DOWN: event_mgr.queue_event<KeyPressEvent>( - {.repeat = event.data.key_data.key_repeat, .key = event.data.key_data.key}); + {.repeat = event.data.key_data.key_repeat, .key = event.data.key_data.key} + ); break; case EventType::KEY_UP: event_mgr.queue_event<KeyReleaseEvent>({.key = event.data.key_data.key}); @@ -128,11 +131,13 @@ void InputSystem::handle_non_mouse_event(const EventData & event) { break; case EventType::WINDOW_RESIZE: event_mgr.queue_event<WindowResizeEvent>( - WindowResizeEvent{.dimensions = event.data.window_data.resize_dimension}); + WindowResizeEvent {.dimensions = event.data.window_data.resize_dimension} + ); break; case EventType::WINDOW_MOVE: event_mgr.queue_event<WindowMoveEvent>( - {.delta_move = event.data.window_data.move_delta}); + {.delta_move = event.data.window_data.move_delta} + ); break; case EventType::WINDOW_MINIMIZE: event_mgr.queue_event<WindowMinimizeEvent>({}); @@ -151,59 +156,70 @@ void InputSystem::handle_non_mouse_event(const EventData & event) { } } -void InputSystem::handle_move(const EventData & event_data, const vec2 & mouse_pos) { +void InputSystem::handle_move( + const EventData & event_data, const vec2 & mouse_pos, const Camera & current_cam +) { ComponentManager & mgr = this->mediator.component_manager; EventManager & event_mgr = this->mediator.event_manager; - RefVector<Button> buttons = mgr.get_components_by_type<Button>(); + const RefVector<Button> buttons = mgr.get_components_by_type<Button>(); for (Button & button : buttons) { if (!button.active) continue; - Metadata & metadata - = mgr.get_components_by_id<Metadata>(button.game_object_id).front(); - Transform & transform + + const Transform & transform = mgr.get_components_by_id<Transform>(button.game_object_id).front(); + const Transform & cam_transform + = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front(); + const Metadata & metadata + = mgr.get_components_by_id<Metadata>(button.game_object_id).front(); bool was_hovering = button.hover; - if (this->is_mouse_inside_button(mouse_pos, button, transform)) { + + if (this->is_mouse_inside_button(mouse_pos, button, transform, cam_transform)) { button.hover = true; if (!was_hovering) { - event_mgr.trigger_event<ButtonEnterEvent>(metadata); + event_mgr.trigger_event<ButtonEnterEvent>(metadata, metadata.game_object_id); } } else { button.hover = false; if (was_hovering) { - event_mgr.trigger_event<ButtonExitEvent>(metadata); + event_mgr.trigger_event<ButtonExitEvent>(metadata, metadata.game_object_id); } } } } -void InputSystem::handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos) { +void InputSystem::handle_click( + const MouseButton & mouse_button, const vec2 & mouse_pos, const Camera & current_cam +) { ComponentManager & mgr = this->mediator.component_manager; EventManager & event_mgr = this->mediator.event_manager; - RefVector<Button> buttons = mgr.get_components_by_type<Button>(); - + const RefVector<Button> buttons = mgr.get_components_by_type<Button>(); + const Transform & cam_transform + = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front(); for (Button & button : buttons) { if (!button.active) continue; - Metadata & metadata + const Metadata & metadata = mgr.get_components_by_id<Metadata>(button.game_object_id).front(); - Transform & transform + const Transform & transform = mgr.get_components_by_id<Transform>(button.game_object_id).front(); - - if (this->is_mouse_inside_button(mouse_pos, button, transform)) { - event_mgr.trigger_event<ButtonPressEvent>(metadata); + if (this->is_mouse_inside_button(mouse_pos, button, transform, cam_transform)) { + event_mgr.trigger_event<ButtonPressEvent>(metadata, metadata.game_object_id); } } } -bool InputSystem::is_mouse_inside_button(const vec2 & mouse_pos, const Button & button, - const Transform & transform) { - int actual_x = transform.position.x + button.offset.x; - int actual_y = transform.position.y + button.offset.y; - - int half_width = button.dimensions.x / 2; - int half_height = button.dimensions.y / 2; +bool InputSystem::is_mouse_inside_button( + const vec2 & mouse_pos, const Button & button, const Transform & transform, + const Transform & cam_transform +) { + vec2 actual_pos = transform.position + button.offset; + if (!button.data.world_space) { + actual_pos += cam_transform.position; + } + vec2 half_dimensions = button.dimensions * transform.scale / 2; - // Check if the mouse is within the button's boundaries - return mouse_pos.x >= actual_x - half_width && mouse_pos.x <= actual_x + half_width - && mouse_pos.y >= actual_y - half_height && mouse_pos.y <= actual_y + half_height; + return mouse_pos.x >= actual_pos.x - half_dimensions.x + && mouse_pos.x <= actual_pos.x + half_dimensions.x + && mouse_pos.y >= actual_pos.y - half_dimensions.y + && mouse_pos.y <= actual_pos.y + half_dimensions.y; } diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h index e580d8e..be62367 100644 --- a/src/crepe/system/InputSystem.h +++ b/src/crepe/system/InputSystem.h @@ -1,12 +1,9 @@ #pragma once -#include "../api/Config.h" -#include "../facade/EventData.h" - #include "../api/Event.h" #include "../api/Metadata.h" +#include "../facade/EventData.h" #include "../types.h" -#include "../util/OptionalRef.h" #include "System.h" @@ -23,7 +20,7 @@ public: /** * \param metadata Metadata of the button pressed */ - ButtonPressEvent(const Metadata & metadata) : metadata(metadata){}; + ButtonPressEvent(const Metadata & metadata) : metadata(metadata) {}; }; //! Event triggered when the mouse enters a button class ButtonEnterEvent : public Event { @@ -33,7 +30,7 @@ public: /** * \param metadata Metadata of the button pressed */ - ButtonEnterEvent(const Metadata & metadata) : metadata(metadata){}; + ButtonEnterEvent(const Metadata & metadata) : metadata(metadata) {}; }; //! Event triggered when the mouse leaves a button class ButtonExitEvent : public Event { @@ -43,7 +40,7 @@ public: /** * \param metadata Metadata of the button pressed */ - ButtonExitEvent(const Metadata & metadata) : metadata(metadata){}; + ButtonExitEvent(const Metadata & metadata) : metadata(metadata) {}; }; /** @@ -61,7 +58,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. @@ -79,8 +76,9 @@ private: * This method processes mouse events, adjusts the mouse position to world coordinates, * and triggers the appropriate mouse-specific event handling logic. */ - void handle_mouse_event(const EventData & event, const vec2 & camera_origin, - const Camera & current_cam); + void handle_mouse_event( + const EventData & event, const vec2 & camera_origin, const Camera & current_cam + ); /** * \brief Handles non-mouse-related events. * \param event The event data for the non-mouse event. @@ -94,20 +92,26 @@ private: * \param mouse_button The mouse button involved in the click. * \param world_mouse_x The X coordinate of the mouse in world space. * \param world_mouse_y The Y coordinate of the mouse in world space. + * \param current_cam The current active camera. * * This method processes the mouse click event and triggers the corresponding button action. */ - void handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos); + void handle_click( + const MouseButton & mouse_button, const vec2 & mouse_pos, const Camera & current_cam + ); /** * \brief Handles the mouse movement event. * \param event_data The event data containing information about the mouse movement. * \param world_mouse_x The X coordinate of the mouse in world space. * \param world_mouse_y The Y coordinate of the mouse in world space. + * \param current_cam The current active camera. * * This method processes the mouse movement event and updates the button hover state. */ - void handle_move(const EventData & event_data, const vec2 & mouse_pos); + void handle_move( + const EventData & event_data, const vec2 & mouse_pos, const Camera & current_cam + ); /** * \brief Checks if the mouse position is inside the bounds of the button. @@ -115,10 +119,13 @@ private: * \param world_mouse_y The Y coordinate of the mouse in world space. * \param button The button to check. * \param transform The transform component of the button. + * \param cam_transform the transform of the current active camera * \return True if the mouse is inside the button, false otherwise. */ - bool is_mouse_inside_button(const vec2 & mouse_pos, const Button & button, - const Transform & transform); + bool is_mouse_inside_button( + const vec2 & mouse_pos, const Button & button, const Transform & transform, + const Transform & cam_transform + ); /** * \brief Handles the button press event, calling the on_click callback if necessary. diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp index e66c603..f026390 100644 --- a/src/crepe/system/ParticleSystem.cpp +++ b/src/crepe/system/ParticleSystem.cpp @@ -13,7 +13,7 @@ using namespace crepe; -void ParticleSystem::update() { +void ParticleSystem::fixed_update() { // Get all emitters const Mediator & mediator = this->mediator; LoopTimerManager & loop_timer = mediator.loop_timer; @@ -50,9 +50,10 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & constexpr float DEG_TO_RAD = M_PI / 180.0; vec2 initial_position = AbsolutePosition::get_position(transform, emitter.data.offset); - float random_angle - = this->generate_random_angle(emitter.data.min_angle + transform.rotation, - emitter.data.max_angle + transform.rotation); + float random_angle = this->generate_random_angle( + emitter.data.min_angle + transform.rotation, + emitter.data.max_angle + transform.rotation + ); float random_speed = this->generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); @@ -63,8 +64,9 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & for (Particle & particle : emitter.particles) { if (!particle.active) { - particle.reset(emitter.data.end_lifespan, initial_position, velocity, - random_angle); + particle.reset( + emitter.data.end_lifespan, initial_position, velocity, random_angle + ); break; } } @@ -82,8 +84,9 @@ void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform & t 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) { diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h index 154521d..4296ff3 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 fixed_update() override; private: /** 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 8c31743..30bb422 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -70,7 +70,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->render_text(); @@ -92,7 +92,7 @@ void RenderSystem::render_text() { const Font & font = resource_manager.get<Font>(text.font.value()); const auto & transform = mgr.get_components_by_id<Transform>(text.game_object_id).front().get(); - ctx.draw_text(SDLContext::RenderText{ + ctx.draw_text(SDLContext::RenderText { .text = text, .font = font, .transform = transform, @@ -120,7 +120,7 @@ bool RenderSystem::render_particle(const Sprite & sprite, const Transform & tran if (!p.active) continue; if (p.time_in_life < em.data.begin_lifespan) continue; - ctx.draw(SDLContext::RenderContext{ + ctx.draw(SDLContext::RenderContext { .sprite = sprite, .texture = res, .pos = p.position, @@ -136,7 +136,7 @@ void RenderSystem::render_normal(const Sprite & sprite, const Transform & transf ResourceManager & resource_manager = this->mediator.resource_manager; const Texture & res = resource_manager.get<Texture>(sprite.source); vec2 pos = AbsolutePosition::get_position(transform, sprite.data.position_offset); - ctx.draw(SDLContext::RenderContext{ + ctx.draw(SDLContext::RenderContext { .sprite = sprite, .texture = res, .pos = pos, diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h index 14e5c2d..627a743 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 0605c7a..ed0c7cc 100644 --- a/src/crepe/system/ScriptSystem.cpp +++ b/src/crepe/system/ScriptSystem.cpp @@ -7,11 +7,22 @@ using namespace std; using namespace crepe; -void ScriptSystem::update() { - dbg_trace(); +void ScriptSystem::fixed_update() { + LoopTimerManager & timer = this->mediator.loop_timer; + duration_t delta_time = timer.get_scaled_fixed_delta_time(); + this->update(&Script::fixed_update, delta_time); +} - ComponentManager & mgr = this->mediator.component_manager; +void ScriptSystem::frame_update() { LoopTimerManager & timer = this->mediator.loop_timer; + duration_t delta_time = timer.get_delta_time(); + this->update(&Script::frame_update, delta_time); +} + +void ScriptSystem::update( + void (Script::*update_function)(duration_t), const duration_t & delta_time +) { + ComponentManager & mgr = this->mediator.component_manager; RefVector<BehaviorScript> behavior_scripts = mgr.get_components_by_type<BehaviorScript>(); for (BehaviorScript & behavior_script : behavior_scripts) { @@ -25,7 +36,6 @@ void ScriptSystem::update() { script->initialized = true; } - duration_t delta_time = timer.get_scaled_fixed_delta_time(); - script->update(delta_time); + (*script.*update_function)(delta_time); } } diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h index 3db1b1e..257b615 100644 --- a/src/crepe/system/ScriptSystem.h +++ b/src/crepe/system/ScriptSystem.h @@ -1,5 +1,7 @@ #pragma once +#include "../manager/LoopTimerManager.h" + #include "System.h" namespace crepe { @@ -9,20 +11,27 @@ class Script; /** * \brief Script system * - * The script system is responsible for all \c BehaviorScript components, and - * calls the methods on classes derived from \c Script. + * The script system is responsible for all \c BehaviorScript components, and calls the methods + * on classes derived from \c Script. */ class ScriptSystem : public System { public: using System::System; + +public: + //! Call Script::fixed_update() on all active \c BehaviorScript instances + void fixed_update() override; + //! Call Script::frame_update() on all active \c BehaviorScript instances + void frame_update() override; + +private: /** - * \brief Call Script::update() on all active \c BehaviorScript instances + * \brief Call Script `*_update` member function on all active \c BehaviorScript instances * - * This routine updates all scripts sequentially using the Script::update() - * method. It also calls Script::init() if this has not been done before on - * the \c BehaviorScript instance. + * \note This routine also calls Script::init() if this has not been done before on the \c + * BehaviorScript instance. */ - void update() override; + void update(void (Script::*update_function)(duration_t), const duration_t & delta_time); }; } // 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..e448070 --- /dev/null +++ b/src/crepe/util/dbg.h @@ -0,0 +1,21 @@ +#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 diff --git a/src/example/AITest.cpp b/src/example/AITest.cpp index 93ba500..4c4e25e 100644 --- a/src/example/AITest.cpp +++ b/src/example/AITest.cpp @@ -30,10 +30,12 @@ class Script1 : public Script { } void init() { - subscribe<ShutDownEvent>( - [this](const ShutDownEvent & ev) -> bool { return this->shutdown(ev); }); - subscribe<MouseMoveEvent>( - [this](const MouseMoveEvent & ev) -> bool { return this->mousemove(ev); }); + subscribe<ShutDownEvent>([this](const ShutDownEvent & ev) -> bool { + return this->shutdown(ev); + }); + subscribe<MouseMoveEvent>([this](const MouseMoveEvent & ev) -> bool { + return this->mousemove(ev); + }); } }; @@ -43,21 +45,23 @@ public: Mediator & mediator = this->mediator; ComponentManager & mgr = mediator.component_manager; - GameObject game_object1 = mgr.new_object("", "", vec2{0, 0}, 0, 1); - GameObject game_object2 = mgr.new_object("", "", vec2{0, 0}, 0, 1); + GameObject game_object1 = mgr.new_object("", "", vec2 {0, 0}, 0, 1); + GameObject game_object2 = mgr.new_object("", "", vec2 {0, 0}, 0, 1); - Asset img{"asset/texture/test_ap43.png"}; + Asset img {"asset/texture/test_ap43.png"}; Sprite & test_sprite = game_object1.add_component<Sprite>( - img, Sprite::Data{ - .color = Color::MAGENTA, - .flip = Sprite::FlipSettings{false, false}, - .sorting_in_layer = 2, - .order_in_layer = 2, - .size = {0, 100}, - .angle_offset = 0, - .position_offset = {0, 0}, - }); + img, + Sprite::Data { + .color = Color::MAGENTA, + .flip = Sprite::FlipSettings {false, false}, + .sorting_in_layer = 2, + .order_in_layer = 2, + .size = {0, 100}, + .angle_offset = 0, + .position_offset = {0, 0}, + } + ); AI & ai = game_object1.add_component<AI>(3000); // ai.arrive_on(); @@ -65,17 +69,19 @@ public: ai.path_follow_on(); ai.make_oval_path(500, 1000, {0, -1000}, 1.5708, true); ai.make_oval_path(1000, 500, {0, 500}, 4.7124, false); - game_object1.add_component<Rigidbody>(Rigidbody::Data{ + game_object1.add_component<Rigidbody>(Rigidbody::Data { .mass = 0.1f, .max_linear_velocity = 40, }); game_object1.add_component<BehaviorScript>().set_script<Script1>(); - game_object2.add_component<Camera>(ivec2{1080, 720}, vec2{5000, 5000}, - Camera::Data{ - .bg_color = Color::WHITE, - .zoom = 1, - }); + game_object2.add_component<Camera>( + ivec2 {1080, 720}, vec2 {5000, 5000}, + Camera::Data { + .bg_color = Color::WHITE, + .zoom = 1, + } + ); } string get_name() const override { return "Scene1"; } diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index f62414e..afe6cb7 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -17,8 +17,7 @@ function(add_example target_name) endfunction() add_example(rendering_particle) -add_example(game) add_example(button) +add_example(replay) add_example(loadfont) -add_example(FontExample) add_example(AITest) diff --git a/src/example/FontExample.cpp b/src/example/FontExample.cpp deleted file mode 100644 index 6a334b1..0000000 --- a/src/example/FontExample.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include <SDL2/SDL_ttf.h> -#include <chrono> -#include <crepe/api/Camera.h> -#include <crepe/api/Config.h> -#include <crepe/api/GameObject.h> -#include <crepe/api/LoopManager.h> -#include <crepe/api/Scene.h> -#include <crepe/api/Script.h> -#include <crepe/api/Text.h> -#include <crepe/facade/Font.h> -#include <crepe/facade/SDLContext.h> -#include <crepe/manager/EventManager.h> -#include <crepe/manager/Mediator.h> -#include <crepe/manager/ResourceManager.h> -#include <exception> -#include <iostream> -#include <memory> -using namespace crepe; -using namespace std; -using namespace std::chrono; -class TestScript : public Script { -public: - steady_clock::time_point start_time; - virtual void init() override { start_time = steady_clock::now(); } - virtual void update() override { - auto now = steady_clock::now(); - auto elapsed = duration_cast<seconds>(now - start_time).count(); - - if (elapsed >= 5) { - Mediator & med = mediator; - EventManager & event_mgr = med.event_manager; - event_mgr.trigger_event<ShutDownEvent>(); - } - } -}; -class TestScene : public Scene { -public: - void load_scene() override { - GameObject text_object = this->new_object("test", "test", vec2{0, 0}, 0, 1); - text_object.add_component<Text>(vec2(100, 100), vec2(0, 0), "OpenSymbol", - Text::Data{}); - text_object.add_component<BehaviorScript>().set_script<TestScript>(); - text_object.add_component<Camera>(ivec2{300, 300}, vec2{100, 100}, Camera::Data{}); - } - std::string get_name() const override { return "hey"; } -}; -int main() { - // Config& config = Config::get_instance(); - // config.log.level = Log::Level::TRACE; - LoopManager engine; - engine.add_scene<TestScene>(); - engine.start(); - - return 0; -} diff --git a/src/example/button.cpp b/src/example/button.cpp index 4220588..ea7f528 100644 --- a/src/example/button.cpp +++ b/src/example/button.cpp @@ -21,14 +21,16 @@ using namespace std; int main(int argc, char * argv[]) { Mediator mediator; - ComponentManager mgr{mediator}; - RenderSystem sys{mediator}; - EventManager event_mgr{mediator}; - InputSystem input_sys{mediator}; - SDLContext sdl_context{mediator}; - GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); + ComponentManager mgr {mediator}; + RenderSystem sys {mediator}; + EventManager event_mgr {mediator}; + InputSystem input_sys {mediator}; + SDLContext sdl_context {mediator}; + GameObject obj = mgr.new_object("camera", "camera", vec2 {0, 0}, 0, 1); auto & camera = obj.add_component<Camera>( - ivec2{500, 500}, vec2{500, 500}, Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f}); + ivec2 {500, 500}, vec2 {500, 500}, + Camera::Data {.bg_color = Color::WHITE, .zoom = 1.0f} + ); auto start = std::chrono::steady_clock::now(); while (true) { const keyboard_state_t & keyboard_state = sdl_context.get_keyboard_state(); diff --git a/src/example/game.cpp b/src/example/game.cpp deleted file mode 100644 index 7ee784a..0000000 --- a/src/example/game.cpp +++ /dev/null @@ -1,321 +0,0 @@ -#include "api/Asset.h" -#include "api/BehaviorScript.h" -#include "api/CircleCollider.h" -#include "api/ParticleEmitter.h" -#include "api/Scene.h" -#include "manager/ComponentManager.h" -#include "manager/Mediator.h" -#include "types.h" -#include <cmath> -#include <crepe/api/BoxCollider.h> -#include <crepe/api/Camera.h> -#include <crepe/api/Color.h> -#include <crepe/api/Event.h> -#include <crepe/api/GameObject.h> -#include <crepe/api/LoopManager.h> -#include <crepe/api/Rigidbody.h> -#include <crepe/api/Script.h> -#include <crepe/api/Sprite.h> -#include <crepe/api/Transform.h> -#include <crepe/api/Vector2.h> - -using namespace crepe; -using namespace std; - -class ScriptBox : public Script { -public: - bool oncollision(const CollisionEvent & test) { - Log::logf("Box {} on_collision() with {}", test.info.self.metadata.game_object_id, test.info.other.metadata.game_object_id); - return true; - } - - void init() { - subscribe<CollisionEvent>( - [this](const CollisionEvent & ev) -> bool { return this->oncollision(ev); }); - } -}; - -class ScriptCircle : public Script { -public: - bool oncollision(const CollisionEvent & test) { - Log::logf("Circle {} on_collision() with {}", test.info.self.metadata.game_object_id, test.info.other.metadata.game_object_id); - return true; - } - - void init() { - subscribe<CollisionEvent>( - [this](const CollisionEvent & ev) -> bool { return this->oncollision(ev); }); - } -}; - -class ScriptMoveToLeft : public Script { -public: - void update() { - Transform & transform = this->get_component<Transform>(); - transform.position.x -= 0.02; - } -}; - -class ScriptMoveToRight : public Script { -public: - void update() { - Transform & transform = this->get_component<Transform>(); - transform.position.x += 0.02; - } -}; - -class ConcreteScene1 : public Scene { -public: - void load_scene() { - GameObject camera = this->new_object("camera"); - camera.add_component<Camera>(ivec2(1080, 720), vec2(10, 10), Camera::Data{ - .bg_color = Color::WHITE, - .zoom = 1 - }); - - GameObject reference = this->new_object("reference", "tag", vec2(0, 0), 0, 1); - Asset reference_asset = Asset("asset/texture/square.png"); - reference.add_component<Sprite>(reference_asset, Sprite::Data{ - .color = Color::RED, - .sorting_in_layer = 10, - .order_in_layer = 0, - .size = vec2(0.1, 0.1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(0, 0), - }); - - /*GameObject box_1 = this->new_object("box_1", "tag", vec2(0, 0), 0, 1); - Asset box_1_asset = Asset("asset/texture/square.png"); - box_1.add_component<Sprite>(box_1_asset, Sprite::Data{ - .sorting_in_layer = 0, - .order_in_layer = 0, - .size = vec2(1, 1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(0, 0.5), - }); - Asset box_1_asset2 = Asset("asset/texture/test_ap43.png"); - box_1.add_component<Sprite>(box_1_asset2, Sprite::Data{ - .sorting_in_layer = 0, - .order_in_layer = 0, - .size = vec2(1, 1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(0, -0.5), - });*/ - - /*GameObject particles = this->new_object("particles", "tag", vec2(-1, 0), 180, 1); - Asset particles_asset = Asset("asset/texture/test_ap43.png"); - Sprite & particles_sprite = particles.add_component<Sprite>(particles_asset, Sprite::Data{ - .sorting_in_layer = 0, - .order_in_layer = 0, - .size = vec2(1, 1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(0, 0), - }); - ParticleEmitter & particles_emitter = particles.add_component<ParticleEmitter>(particles_sprite, ParticleEmitter::Data{ - .offset = vec2(0, 0), - .emission_rate = 1, - .min_speed = 1, - .max_speed = 1, - .min_angle = 0, - .max_angle = 0, - .end_lifespan = 4, - });*/ - - const bool SCRIPT = false; - - const Rigidbody::BodyType BODYTYPE_LEFT = Rigidbody::BodyType::DYNAMIC; - const Rigidbody::BodyType BODYTYPE_RIGHT = Rigidbody::BodyType::DYNAMIC; - const bool BOUNCE_LEFT = false; - const bool BOUNCE_RIGHT = false; - const bool KINEMATIC_COLLISION = true; - const bool ONTOP = false; - - const float SCALE = 0.5; - const float OFFSET_X_LEFT = 0; - const float OFFSET_X_RIGHT = 0; - const float OFFSET_Y_LEFT = 0; - const float OFFSET_Y_RIGHT = 0; - - GameObject box_1 = this->new_object("box_1", "tag", ONTOP ? vec2(-0.25, -4) : vec2(-2, -4), 0, SCALE); - Asset box_1_asset = Asset("asset/texture/square.png"); - box_1.add_component<Sprite>(box_1_asset, Sprite::Data{ - .sorting_in_layer = 0, - .order_in_layer = 0, - .size = vec2(1, 1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT), - }); - box_1.add_component<Rigidbody>(Rigidbody::Data{ - .gravity_scale = 0, - .body_type = BODYTYPE_LEFT, - .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{1, 0}, - .elasticity_coefficient = BOUNCE_LEFT ? 0.5 : 0, - .kinematic_collision = KINEMATIC_COLLISION, - }); - box_1.add_component<BehaviorScript>().set_script<ScriptMoveToRight>().active = SCRIPT; - box_1.add_component<BoxCollider>(vec2(1, 1), vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT)); - box_1.add_component<BehaviorScript>().set_script<ScriptBox>(); - - GameObject circle_1 = this->new_object("ricle_1", "tag", ONTOP ? vec2(0.25, -4) : vec2(2, -4), 0, SCALE); - Asset circle_1_asset = Asset("asset/texture/circle.png"); - circle_1.add_component<Sprite>(circle_1_asset, Sprite::Data{ - .sorting_in_layer = 0, - .order_in_layer = 0, - .size = vec2(1, 1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT), - }); - circle_1.add_component<Rigidbody>(Rigidbody::Data{ - .gravity_scale = 0, - .body_type = BODYTYPE_RIGHT, - .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{-1, 0}, - .elasticity_coefficient = BOUNCE_RIGHT ? 0.5 : 0, - .kinematic_collision = KINEMATIC_COLLISION, - }); - circle_1.add_component<BehaviorScript>().set_script<ScriptMoveToLeft>().active = SCRIPT; - circle_1.add_component<CircleCollider>(0.5, vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT)); - circle_1.add_component<BehaviorScript>().set_script<ScriptCircle>(); - - GameObject circle_2 = this->new_object("ricle_2", "tag", ONTOP ? vec2(-0.25, -1.5) : vec2(-2.5, -1.5), 0, SCALE); - Asset circle_2_asset = Asset("asset/texture/circle.png"); - circle_2.add_component<Sprite>(circle_2_asset, Sprite::Data{ - .sorting_in_layer = 0, - .order_in_layer = 0, - .size = vec2(1, 1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT), - }); - circle_2.add_component<Rigidbody>(Rigidbody::Data{ - .gravity_scale = 0, - .body_type = BODYTYPE_LEFT, - .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{1, 0}, - .elasticity_coefficient = BOUNCE_LEFT ? 0.5 : 0, - .kinematic_collision = KINEMATIC_COLLISION, - }); - circle_2.add_component<BehaviorScript>().set_script<ScriptMoveToRight>().active = SCRIPT; - circle_2.add_component<CircleCollider>(0.5, vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT)); - circle_2.add_component<BehaviorScript>().set_script<ScriptCircle>(); - - GameObject box_2 = this->new_object("box_2", "tag", ONTOP ? vec2(0.25, -1.5) : vec2(2.5, -1.5), 0, SCALE); - Asset box_2_asset = Asset("asset/texture/square.png"); - box_2.add_component<Sprite>(box_2_asset, Sprite::Data{ - .sorting_in_layer = 0, - .order_in_layer = 0, - .size = vec2(1, 1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT), - }); - box_2.add_component<Rigidbody>(Rigidbody::Data{ - .gravity_scale = 0, - .body_type = BODYTYPE_RIGHT, - .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{-1, 0}, - .elasticity_coefficient = BOUNCE_RIGHT ? 0.5 : 0, - .kinematic_collision = KINEMATIC_COLLISION, - }); - box_2.add_component<BehaviorScript>().set_script<ScriptMoveToLeft>().active = SCRIPT; - box_2.add_component<BoxCollider>(vec2(1, 1), vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT)); - box_2.add_component<BehaviorScript>().set_script<ScriptBox>(); - - GameObject box_3 = this->new_object("box_3", "tag", ONTOP ? vec2(-0.25, 1.5) : vec2(-3, 1.5), 0, SCALE); - Asset box_3_asset = Asset("asset/texture/square.png"); - box_3.add_component<Sprite>(box_3_asset, Sprite::Data{ - .sorting_in_layer = 0, - .order_in_layer = 0, - .size = vec2(1, 1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT), - }); - box_3.add_component<Rigidbody>(Rigidbody::Data{ - .gravity_scale = 0, - .body_type = BODYTYPE_LEFT, - .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{1, 0}, - .elasticity_coefficient = BOUNCE_LEFT ? 0.5 : 0, - .kinematic_collision = KINEMATIC_COLLISION, - }); - box_3.add_component<BehaviorScript>().set_script<ScriptMoveToRight>().active = SCRIPT; - box_3.add_component<BoxCollider>(vec2(1, 1), vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT)); - box_3.add_component<BehaviorScript>().set_script<ScriptBox>(); - - GameObject box_4 = this->new_object("box_4", "tag", ONTOP ? vec2(0.25, 1.5) : vec2(3, 1.5), 0, SCALE); - Asset box_4_asset = Asset("asset/texture/square.png"); - box_4.add_component<Sprite>(box_4_asset, Sprite::Data{ - .sorting_in_layer = 0, - .order_in_layer = 0, - .size = vec2(1, 1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT), - }); - box_4.add_component<Rigidbody>(Rigidbody::Data{ - .gravity_scale = 0, - .body_type = BODYTYPE_RIGHT, - .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{-1, 0}, - .elasticity_coefficient = BOUNCE_RIGHT ? 0.5 : 0, - .kinematic_collision = KINEMATIC_COLLISION, - }); - box_4.add_component<BehaviorScript>().set_script<ScriptMoveToLeft>().active = SCRIPT; - box_4.add_component<BoxCollider>(vec2(1, 1), vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT)); - box_4.add_component<BehaviorScript>().set_script<ScriptBox>(); - - GameObject circle_3 = this->new_object("ricle_3", "tag", ONTOP ? vec2(-0.25, 4) : vec2(-3.5, 4), 0, SCALE); - Asset circle_3_asset = Asset("asset/texture/circle.png"); - circle_3.add_component<Sprite>(circle_3_asset, Sprite::Data{ - .sorting_in_layer = 0, - .order_in_layer = 0, - .size = vec2(1, 1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT), - }); - circle_3.add_component<Rigidbody>(Rigidbody::Data{ - .gravity_scale = 0, - .body_type = BODYTYPE_LEFT, - .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{1, 0}, - .elasticity_coefficient = BOUNCE_LEFT ? 0.5 : 0, - .kinematic_collision = KINEMATIC_COLLISION, - }); - circle_3.add_component<BehaviorScript>().set_script<ScriptMoveToRight>().active = SCRIPT; - circle_3.add_component<CircleCollider>(0.5, vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT)); - circle_3.add_component<BehaviorScript>().set_script<ScriptCircle>(); - - GameObject circle_4 = this->new_object("ricle_4", "tag", ONTOP ? vec2(0.25, 4) : vec2(3.5, 4), 0, SCALE); - Asset circle_4_asset = Asset("asset/texture/circle.png"); - circle_4.add_component<Sprite>(circle_4_asset, Sprite::Data{ - .sorting_in_layer = 0, - .order_in_layer = 0, - .size = vec2(1, 1), - .angle_offset = 0, - .scale_offset = 1, - .position_offset = vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT), - }); - circle_4.add_component<Rigidbody>(Rigidbody::Data{ - .gravity_scale = 0, - .body_type = BODYTYPE_RIGHT, - .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{-1, 0}, - .elasticity_coefficient = BOUNCE_RIGHT ? 0.5 : 0, - .kinematic_collision = KINEMATIC_COLLISION, - }); - circle_4.add_component<BehaviorScript>().set_script<ScriptMoveToLeft>().active = SCRIPT; - circle_4.add_component<CircleCollider>(0.5, vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT)); - circle_4.add_component<BehaviorScript>().set_script<ScriptCircle>(); - } - - string get_name() const { return "scene1"; } -}; - -int main(int argc, char * argv[]) { - - LoopManager gameloop; - gameloop.add_scene<ConcreteScene1>(); - gameloop.start(); - return 0; -} diff --git a/src/example/loadfont.cpp b/src/example/loadfont.cpp index e459332..dd7caff 100644 --- a/src/example/loadfont.cpp +++ b/src/example/loadfont.cpp @@ -15,19 +15,21 @@ int main() { // SDLFontContext font_facade; Mediator mediator; - FontFacade font_facade{}; - SDLContext sdl_context{mediator}; + FontFacade font_facade {}; + SDLContext sdl_context {mediator}; // ComponentManager component_manager{mediator}; - ResourceManager resource_manager{mediator}; + ResourceManager resource_manager {mediator}; try { // Correct way to create a unique pointer for Text std::unique_ptr<Text> label = std::make_unique<Text>( - 1, vec2(100, 100), vec2(0, 0), "OpenSymbol", Text::Data{}, "test text"); + 1, vec2(100, 100), vec2(0, 0), "OpenSymbol", Text::Data {}, "test text" + ); // std::cout << "Path: " << label->font.get_path() << std::endl; Asset asset1 = font_facade.get_font_asset("OpenSymbol"); std::cout << asset1.get_path() << std::endl; std::unique_ptr<Text> label2 = std::make_unique<Text>( - 1, vec2(100, 100), vec2(0, 0), "fsaafdafsdafsdafsdasfdds", Text::Data{}); + 1, vec2(100, 100), vec2(0, 0), "fsaafdafsdafsdafsdasfdds", Text::Data {} + ); Asset asset = Asset("test test"); label->font.emplace(asset); std::cout << label->font.value().get_path() << std::endl; diff --git a/src/example/rendering_particle.cpp b/src/example/rendering_particle.cpp index 5440fdd..e6b31a7 100644 --- a/src/example/rendering_particle.cpp +++ b/src/example/rendering_particle.cpp @@ -7,8 +7,8 @@ #include <crepe/api/Button.h> #include <crepe/api/Camera.h> #include <crepe/api/Color.h> +#include <crepe/api/Engine.h> #include <crepe/api/GameObject.h> -#include <crepe/api/LoopManager.hpp> #include <crepe/api/ParticleEmitter.h> #include <crepe/api/Rigidbody.h> #include <crepe/api/Sprite.h> @@ -23,54 +23,52 @@ using namespace std; class TestScene : public Scene { public: void load_scene() { - GameObject game_object = new_object("", "", vec2{0, 0}, 0, 1); + GameObject game_object = new_object("", "", vec2 {0, 0}, 0, 1); Color color(255, 255, 255, 255); - Asset img{"asset/texture/square.png"}; + Asset img {"asset/spritesheet/pokemon_spritesheet.png"}; Sprite & test_sprite = game_object.add_component<Sprite>( - img, Sprite::Data{ - .color = color, - .flip = Sprite::FlipSettings{false, false}, - .sorting_in_layer = 2, - .order_in_layer = 2, - .size = {1, 1}, - .angle_offset = 0, - .position_offset = {0, 1}, - .world_space = false, - }); - //auto & emitter = game_object.add_component<ParticleEmitter>(test_sprite, ParticleEmitter::Data{}); + img, + Sprite::Data { + .color = color, + .flip = Sprite::FlipSettings {false, false}, + .sorting_in_layer = 2, + .order_in_layer = 2, + .size = {1, 0}, + .angle_offset = 0, + .position_offset = {0, 1}, + .world_space = false, + } + ); - Sprite & test_sprite1 - = game_object.add_component<Sprite>(img, Sprite::Data{ - .color = color, - .size = {1, 1}, - .position_offset = {0, -1}, - .world_space = false, - }); + auto & anim = game_object.add_component<Animator>( + test_sprite, ivec2 {56, 56}, uvec2 {4, 4}, + Animator::Data { + .looping = 0, + } + ); - auto & cam = game_object.add_component<Camera>(ivec2{1280, 720}, vec2{5, 5}, - Camera::Data{ - .bg_color = Color::WHITE, - .postion_offset = {1000, 1000}, - }); + anim.set_anim(1); + anim.pause(); + anim.next_anim(); - /* - game_object.add_component<Text>(vec2{1, 1}, vec2{0, -0.5}, "ComicSansMS", - Text::Data{.text_color = Color::RED}, "test TEST"); - - game_object.add_component<Text>(vec2{1, 1}, vec2{0, 0.5}, "ComicSansMS", - Text::Data{.text_color = Color::BLACK}, "TEST test"); - */ + auto & cam = game_object.add_component<Camera>( + ivec2 {1280, 720}, vec2 {5, 5}, + Camera::Data { + .bg_color = Color::WHITE, + .postion_offset = {1000, 1000}, + } + ); } string get_name() const { return "TestScene"; }; }; int main(int argc, char * argv[]) { - LoopManager engine; + Engine engine; engine.add_scene<TestScene>(); - engine.start(); + engine.main(); return 0; } diff --git a/src/example/replay.cpp b/src/example/replay.cpp new file mode 100644 index 0000000..00a6502 --- /dev/null +++ b/src/example/replay.cpp @@ -0,0 +1,89 @@ +#include <crepe/api/Config.h> +#include <crepe/api/Engine.h> +#include <crepe/api/Script.h> + +using namespace crepe; +using namespace std; + +class AnimationScript : public Script { + Transform * transform; + float t = 0; + + void init() { transform = &get_component<Transform>(); } + + void update() { + t += 0.05; + transform->position = {sin(t), cos(t)}; + } +}; + +class Timeline : public Script { + unsigned i = 0; + recording_t recording; + + void update() { + switch (i++) { + default: + break; + case 10: + logf("record start"); + replay.record_start(); + break; + case 60: + logf("record end, playing recording"); + this->recording = replay.record_end(); + replay.play(this->recording); + break; + case 61: + logf("done, releasing recording"); + replay.release(this->recording); + break; + case 72: + logf("exit"); + queue_event<ShutDownEvent>(); + break; + }; + } +}; + +class TestScene : public Scene { +public: + using Scene::Scene; + + void load_scene() { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + + GameObject cam = mgr.new_object("cam"); + cam.add_component<Camera>( + ivec2 {640, 480}, vec2 {3, 3}, + Camera::Data { + .bg_color = Color::WHITE, + } + ); + + GameObject square = mgr.new_object("square"); + square.add_component<Sprite>( + Asset {"asset/texture/square.png"}, + Sprite::Data { + .size = {0.5, 0.5}, + } + ); + square.add_component<BehaviorScript>().set_script<AnimationScript>(); + + GameObject scapegoat = mgr.new_object(""); + scapegoat.add_component<BehaviorScript>().set_script<Timeline>(); + } + + string get_name() const { return "scene1"; } +}; + +int main(int argc, char * argv[]) { + Config & cfg = Config::get_instance(); + cfg.log.level = Log::Level::DEBUG; + + Engine engine; + + engine.add_scene<TestScene>(); + return engine.main(); +} diff --git a/src/test/AssetTest.cpp b/src/test/AssetTest.cpp index 93fd6a9..f41e9de 100644 --- a/src/test/AssetTest.cpp +++ b/src/test/AssetTest.cpp @@ -7,15 +7,15 @@ using namespace std; using namespace crepe; using namespace testing; -TEST(AssetTest, Existant) { ASSERT_NO_THROW(Asset{"asset/texture/img.png"}); } +TEST(AssetTest, Existant) { ASSERT_NO_THROW(Asset {"asset/texture/img.png"}); } -TEST(AssetTest, Nonexistant) { ASSERT_ANY_THROW(Asset{"asset/nonexistant"}); } +TEST(AssetTest, Nonexistant) { ASSERT_ANY_THROW(Asset {"asset/nonexistant"}); } TEST(AssetTest, Rootless) { Config & cfg = Config::get_instance(); cfg.asset.root_pattern.clear(); string arbitrary = "\\/this is / /../passed through as-is"; - Asset asset{arbitrary}; + Asset asset {arbitrary}; ASSERT_EQ(arbitrary, asset.get_path()); } diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index 48bba1b..e548221 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -31,11 +31,11 @@ private: private: Mediator mediator; - ComponentManager component_manager{mediator}; - ResourceManager resource_manager{mediator}; + ComponentManager component_manager {mediator}; + ResourceManager resource_manager {mediator}; public: - TestAudioSystem system{mediator}; + TestAudioSystem system {mediator}; TestSoundContext & context = system.context; private: @@ -50,16 +50,18 @@ TEST_F(AudioTest, Default) { EXPECT_CALL(context, stop(_)).Times(0); EXPECT_CALL(context, set_volume(_, _)).Times(0); EXPECT_CALL(context, set_loop(_, _)).Times(0); - system.update(); + system.fixed_update(); } TEST_F(AudioTest, Play) { - system.update(); + system.fixed_update(); { InSequence seq; EXPECT_CALL(context, play(_)).Times(0); + EXPECT_CALL(context, set_loop(_, _)).Times(0); + EXPECT_CALL(context, set_volume(_, _)).Times(0); component.play(); } @@ -67,12 +69,14 @@ TEST_F(AudioTest, Play) { InSequence seq; EXPECT_CALL(context, play(_)).Times(1); - system.update(); + EXPECT_CALL(context, set_loop(_, _)).Times(1); + EXPECT_CALL(context, set_volume(_, _)).Times(1); + system.fixed_update(); } } TEST_F(AudioTest, Stop) { - system.update(); + system.fixed_update(); { InSequence seq; @@ -85,12 +89,12 @@ TEST_F(AudioTest, Stop) { InSequence seq; EXPECT_CALL(context, stop(_)).Times(1); - system.update(); + system.fixed_update(); } } TEST_F(AudioTest, Volume) { - system.update(); + system.fixed_update(); { InSequence seq; @@ -103,12 +107,12 @@ TEST_F(AudioTest, Volume) { InSequence seq; EXPECT_CALL(context, set_volume(_, component.volume)).Times(1); - system.update(); + system.fixed_update(); } } TEST_F(AudioTest, Looping) { - system.update(); + system.fixed_update(); { InSequence seq; @@ -121,33 +125,36 @@ TEST_F(AudioTest, Looping) { InSequence seq; EXPECT_CALL(context, set_loop(_, component.loop)).Times(1); - system.update(); + system.fixed_update(); } } TEST_F(AudioTest, StopOnDeactivate) { - system.update(); + system.fixed_update(); { InSequence seq; EXPECT_CALL(context, stop(_)).Times(1); component.active = false; - system.update(); + system.fixed_update(); } } TEST_F(AudioTest, PlayOnActive) { component.active = false; component.play_on_awake = true; - system.update(); + system.fixed_update(); { InSequence seq; EXPECT_CALL(context, play(_)).Times(1); + EXPECT_CALL(context, set_loop(_, _)).Times(1); + EXPECT_CALL(context, set_volume(_, _)).Times(1); + component.active = true; - system.update(); + system.fixed_update(); } } @@ -156,6 +163,8 @@ TEST_F(AudioTest, PlayImmediately) { component.play(); EXPECT_CALL(context, play(_)).Times(1); + EXPECT_CALL(context, set_volume(_, _)).Times(1); + EXPECT_CALL(context, set_loop(_, _)).Times(1); - system.update(); + system.fixed_update(); } diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 11b4ca9..ea92d96 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -15,7 +15,7 @@ target_sources(test_main PUBLIC ValueBrokerTest.cpp DBTest.cpp Vector2Test.cpp - LoopManagerTest.cpp + # LoopManagerTest.cpp LoopTimerTest.cpp InputTest.cpp ScriptEventTest.cpp @@ -24,4 +24,5 @@ target_sources(test_main PUBLIC SaveManagerTest.cpp ScriptSaveManagerTest.cpp ScriptECSTest.cpp + ReplayManagerTest.cpp ) diff --git a/src/test/CollisionTest.cpp b/src/test/CollisionTest.cpp index 11916eb..c571c1a 100644 --- a/src/test/CollisionTest.cpp +++ b/src/test/CollisionTest.cpp @@ -39,8 +39,9 @@ public: } void init() { - subscribe<CollisionEvent>( - [this](const CollisionEvent & ev) -> bool { return this->on_collision(ev); }); + subscribe<CollisionEvent>([this](const CollisionEvent & ev) -> bool { + return this->on_collision(ev); + }); } void update() { // Retrieve component from the same GameObject this script is on @@ -50,11 +51,11 @@ public: class CollisionTest : public Test { public: Mediator m; - EventManager event_mgr{m}; - ComponentManager mgr{m}; - CollisionSystem collision_sys{m}; - ScriptSystem script_sys{m}; - LoopTimerManager loop_timer{m}; + EventManager event_mgr {m}; + ComponentManager mgr {m}; + CollisionSystem collision_sys {m}; + ScriptSystem script_sys {m}; + LoopTimerManager loop_timer {m}; GameObject world = mgr.new_object("world", "", {50, 50}); GameObject game_object1 = mgr.new_object("object1", "", {50, 50}); @@ -64,17 +65,17 @@ public: CollisionHandler * script_object2_ref = nullptr; void SetUp() override { - world.add_component<Rigidbody>(Rigidbody::Data{ + world.add_component<Rigidbody>(Rigidbody::Data { // TODO: remove unrelated properties: .body_type = Rigidbody::BodyType::STATIC, }); // Create a box with an inner size of 10x10 units - world.add_component<BoxCollider>(vec2{100, 100}, vec2{0, -100}); // Top - world.add_component<BoxCollider>(vec2{100, 100}, vec2{0, 100}); // Bottom - world.add_component<BoxCollider>(vec2{100, 100}, vec2{-100, 0}); // Left - world.add_component<BoxCollider>(vec2{100, 100}, vec2{100, 0}); // right + world.add_component<BoxCollider>(vec2 {100, 100}, vec2 {0, -100}); // Top + world.add_component<BoxCollider>(vec2 {100, 100}, vec2 {0, 100}); // Bottom + world.add_component<BoxCollider>(vec2 {100, 100}, vec2 {-100, 0}); // Left + world.add_component<BoxCollider>(vec2 {100, 100}, vec2 {100, 0}); // right - game_object1.add_component<Rigidbody>(Rigidbody::Data{ + game_object1.add_component<Rigidbody>(Rigidbody::Data { .mass = 1, .gravity_scale = 0.01, .body_type = Rigidbody::BodyType::DYNAMIC, @@ -83,13 +84,13 @@ public: .elasticity_coefficient = 1, .collision_layers = {0}, }); - game_object1.add_component<BoxCollider>(vec2{10, 10}, vec2{0, 0}); + game_object1.add_component<BoxCollider>(vec2 {10, 10}, vec2 {0, 0}); BehaviorScript & script_object1 = game_object1.add_component<BehaviorScript>().set_script<CollisionHandler>(1); script_object1_ref = static_cast<CollisionHandler *>(script_object1.script.get()); ASSERT_NE(script_object1_ref, nullptr); - game_object2.add_component<Rigidbody>(Rigidbody::Data{ + game_object2.add_component<Rigidbody>(Rigidbody::Data { .mass = 1, .gravity_scale = 0.01, .body_type = Rigidbody::BodyType::DYNAMIC, @@ -98,14 +99,14 @@ public: .elasticity_coefficient = 1, .collision_layers = {0}, }); - game_object2.add_component<BoxCollider>(vec2{10, 10}, vec2{0, 0}); + game_object2.add_component<BoxCollider>(vec2 {10, 10}, vec2 {0, 0}); BehaviorScript & script_object2 = game_object2.add_component<BehaviorScript>().set_script<CollisionHandler>(2); script_object2_ref = static_cast<CollisionHandler *>(script_object2.script.get()); ASSERT_NE(script_object2_ref, nullptr); // Ensure Script::init() is called on all BehaviorScript instances - script_sys.update(); + script_sys.fixed_update(); } }; @@ -120,7 +121,7 @@ TEST_F(CollisionTest, collision_example) { EXPECT_EQ(ev.info.self.transform.game_object_id, 2); }; EXPECT_FALSE(collision_happend); - collision_sys.update(); + collision_sys.fixed_update(); EXPECT_FALSE(collision_happend); } @@ -143,7 +144,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_both_no_velocity) { EXPECT_FALSE(collision_happend); Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); tf.position = {50, 30}; - collision_sys.update(); + collision_sys.fixed_update(); EXPECT_TRUE(collision_happend); } @@ -154,21 +155,23 @@ TEST_F(CollisionTest, collision_box_box_dynamic_x_direction_no_velocity) { EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, -5); EXPECT_EQ(ev.info.resolution.y, 0); - EXPECT_EQ(ev.info.resolution_direction, - crepe::CollisionSystem::Direction::X_DIRECTION); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::X_DIRECTION + ); }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; EXPECT_EQ(ev.info.self.transform.game_object_id, 2); EXPECT_EQ(ev.info.resolution.x, 5); EXPECT_EQ(ev.info.resolution.y, 0); - EXPECT_EQ(ev.info.resolution_direction, - crepe::CollisionSystem::Direction::X_DIRECTION); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::X_DIRECTION + ); }; EXPECT_FALSE(collision_happend); Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); tf.position = {45, 30}; - collision_sys.update(); + collision_sys.fixed_update(); EXPECT_TRUE(collision_happend); } @@ -179,21 +182,23 @@ TEST_F(CollisionTest, collision_box_box_dynamic_y_direction_no_velocity) { EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, 0); EXPECT_EQ(ev.info.resolution.y, -5); - EXPECT_EQ(ev.info.resolution_direction, - crepe::CollisionSystem::Direction::Y_DIRECTION); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::Y_DIRECTION + ); }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; EXPECT_EQ(ev.info.self.transform.game_object_id, 2); EXPECT_EQ(ev.info.resolution.x, 0); EXPECT_EQ(ev.info.resolution.y, 5); - EXPECT_EQ(ev.info.resolution_direction, - crepe::CollisionSystem::Direction::Y_DIRECTION); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::Y_DIRECTION + ); }; EXPECT_FALSE(collision_happend); Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); tf.position = {50, 25}; - collision_sys.update(); + collision_sys.fixed_update(); EXPECT_TRUE(collision_happend); } @@ -220,7 +225,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_both) { rg1.data.linear_velocity = {10, 10}; Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); rg2.data.linear_velocity = {10, 10}; - collision_sys.update(); + collision_sys.fixed_update(); EXPECT_TRUE(collision_happend); } @@ -231,16 +236,18 @@ TEST_F(CollisionTest, collision_box_box_dynamic_x_direction) { EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, -5); EXPECT_EQ(ev.info.resolution.y, 5); - EXPECT_EQ(ev.info.resolution_direction, - crepe::CollisionSystem::Direction::X_DIRECTION); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::X_DIRECTION + ); }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; EXPECT_EQ(ev.info.self.transform.game_object_id, 2); EXPECT_EQ(ev.info.resolution.x, 5); EXPECT_EQ(ev.info.resolution.y, -5); - EXPECT_EQ(ev.info.resolution_direction, - crepe::CollisionSystem::Direction::X_DIRECTION); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::X_DIRECTION + ); }; EXPECT_FALSE(collision_happend); Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); @@ -249,7 +256,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_x_direction) { rg1.data.linear_velocity = {10, 10}; Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); rg2.data.linear_velocity = {10, 10}; - collision_sys.update(); + collision_sys.fixed_update(); EXPECT_TRUE(collision_happend); } @@ -260,16 +267,18 @@ TEST_F(CollisionTest, collision_box_box_dynamic_y_direction) { EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, 5); EXPECT_EQ(ev.info.resolution.y, -5); - EXPECT_EQ(ev.info.resolution_direction, - crepe::CollisionSystem::Direction::Y_DIRECTION); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::Y_DIRECTION + ); }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; EXPECT_EQ(ev.info.self.transform.game_object_id, 2); EXPECT_EQ(ev.info.resolution.x, -5); EXPECT_EQ(ev.info.resolution.y, 5); - EXPECT_EQ(ev.info.resolution_direction, - crepe::CollisionSystem::Direction::Y_DIRECTION); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::Y_DIRECTION + ); }; EXPECT_FALSE(collision_happend); Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); @@ -278,7 +287,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_y_direction) { rg1.data.linear_velocity = {10, 10}; Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); rg2.data.linear_velocity = {10, 10}; - collision_sys.update(); + collision_sys.fixed_update(); EXPECT_TRUE(collision_happend); } @@ -298,7 +307,7 @@ TEST_F(CollisionTest, collision_box_box_static_both) { tf.position = {50, 30}; Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; - collision_sys.update(); + collision_sys.fixed_update(); EXPECT_TRUE(collision_happend); } @@ -309,8 +318,9 @@ TEST_F(CollisionTest, collision_box_box_static_x_direction) { EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, -5); EXPECT_EQ(ev.info.resolution.y, 5); - EXPECT_EQ(ev.info.resolution_direction, - crepe::CollisionSystem::Direction::X_DIRECTION); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::X_DIRECTION + ); }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { // is static should not be called @@ -323,7 +333,7 @@ TEST_F(CollisionTest, collision_box_box_static_x_direction) { rg1.data.linear_velocity = {10, 10}; Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; - collision_sys.update(); + collision_sys.fixed_update(); EXPECT_TRUE(collision_happend); } @@ -334,8 +344,9 @@ TEST_F(CollisionTest, collision_box_box_static_y_direction) { EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, 5); EXPECT_EQ(ev.info.resolution.y, -5); - EXPECT_EQ(ev.info.resolution_direction, - crepe::CollisionSystem::Direction::Y_DIRECTION); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::Y_DIRECTION + ); }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { // is static should not be called @@ -348,6 +359,6 @@ TEST_F(CollisionTest, collision_box_box_static_y_direction) { rg1.data.linear_velocity = {10, 10}; Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; - collision_sys.update(); + collision_sys.fixed_update(); EXPECT_TRUE(collision_happend); } diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index af2b7b0..92436a9 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -16,7 +16,7 @@ class ECSTest : public ::testing::Test { Mediator m; public: - ComponentManager mgr{m}; + ComponentManager mgr {m}; class TestComponent : public Component { using Component::Component; @@ -24,7 +24,7 @@ public: }; TEST_F(ECSTest, createGameObject) { - GameObject obj = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); + GameObject obj = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>(); vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>(); @@ -44,8 +44,8 @@ TEST_F(ECSTest, createGameObject) { } TEST_F(ECSTest, deleteAllGameObjects) { - GameObject obj0 = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); - GameObject obj1 = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); + GameObject obj0 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); mgr.delete_all_components(); @@ -55,7 +55,7 @@ TEST_F(ECSTest, deleteAllGameObjects) { EXPECT_EQ(metadata.size(), 0); EXPECT_EQ(transform.size(), 0); - GameObject obj2 = mgr.new_object("body2", "person2", vec2{1, 0}, 5, 1); + GameObject obj2 = mgr.new_object("body2", "person2", vec2 {1, 0}, 5, 1); metadata = mgr.get_components_by_type<Metadata>(); transform = mgr.get_components_by_type<Transform>(); @@ -77,8 +77,8 @@ TEST_F(ECSTest, deleteAllGameObjects) { } TEST_F(ECSTest, deleteGameObject) { - GameObject obj0 = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); - GameObject obj1 = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); + GameObject obj0 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); mgr.delete_all_components_of_id(0); @@ -103,7 +103,7 @@ TEST_F(ECSTest, deleteGameObject) { TEST_F(ECSTest, manyGameObjects) { for (int i = 0; i < 5000; i++) { - GameObject obj = mgr.new_object("body", "person", vec2{0, 0}, 0, i); + GameObject obj = mgr.new_object("body", "person", vec2 {0, 0}, 0, i); } vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>(); @@ -135,7 +135,7 @@ TEST_F(ECSTest, manyGameObjects) { for (int i = 0; i < 10000 - 5000; i++) { string tag = "person" + to_string(i); - GameObject obj = mgr.new_object("body", tag, vec2{0, 0}, i, 0); + GameObject obj = mgr.new_object("body", tag, vec2 {0, 0}, i, 0); } metadata = mgr.get_components_by_type<Metadata>(); @@ -168,7 +168,7 @@ TEST_F(ECSTest, manyGameObjects) { for (int i = 0; i < 10000; i++) { string name = "body" + to_string(i); - GameObject obj = mgr.new_object(name, "person", vec2{0, 0}, 0, 0); + GameObject obj = mgr.new_object(name, "person", vec2 {0, 0}, 0, 0); } metadata = mgr.get_components_by_type<Metadata>(); @@ -193,8 +193,8 @@ TEST_F(ECSTest, manyGameObjects) { } TEST_F(ECSTest, getComponentsByID) { - GameObject obj0 = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); - GameObject obj1 = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); + GameObject obj0 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_id<Metadata>(0); vector<reference_wrapper<Transform>> transform = mgr.get_components_by_id<Transform>(1); @@ -217,19 +217,21 @@ TEST_F(ECSTest, getComponentsByID) { TEST_F(ECSTest, tooMuchComponents) { try { - GameObject obj0 = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); - obj0.add_component<Transform>(vec2{10, 10}, 0, 1); + GameObject obj0 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + obj0.add_component<Transform>(vec2 {10, 10}, 0, 1); } catch (const exception & e) { - EXPECT_EQ(e.what(), - string("Exceeded maximum number of instances for this component type")); + EXPECT_EQ( + e.what(), string("Exceeded maximum number of instances for this component type") + ); } try { - GameObject obj1 = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); obj1.add_component<Metadata>("body", "person"); } catch (const exception & e) { - EXPECT_EQ(e.what(), - string("Exceeded maximum number of instances for this component type")); + EXPECT_EQ( + e.what(), string("Exceeded maximum number of instances for this component type") + ); } vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>(); @@ -241,11 +243,11 @@ TEST_F(ECSTest, tooMuchComponents) { TEST_F(ECSTest, partentChild) { { - GameObject body = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); - GameObject right_leg = mgr.new_object("rightLeg", "person", vec2{1, 1}, 0, 1); - GameObject left_leg = mgr.new_object("leftLeg", "person", vec2{1, 1}, 0, 1); - GameObject right_foot = mgr.new_object("rightFoot", "person", vec2{2, 2}, 0, 1); - GameObject left_foot = mgr.new_object("leftFoot", "person", vec2{2, 2}, 0, 1); + GameObject body = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + GameObject right_leg = mgr.new_object("rightLeg", "person", vec2 {1, 1}, 0, 1); + GameObject left_leg = mgr.new_object("leftLeg", "person", vec2 {1, 1}, 0, 1); + GameObject right_foot = mgr.new_object("rightFoot", "person", vec2 {2, 2}, 0, 1); + GameObject left_foot = mgr.new_object("leftFoot", "person", vec2 {2, 2}, 0, 1); // Set the parent of each GameObject right_foot.set_parent(right_leg); @@ -290,10 +292,10 @@ TEST_F(ECSTest, partentChild) { } TEST_F(ECSTest, persistent) { - GameObject obj0 = mgr.new_object("obj0", "obj0", vec2{0, 0}, 0, 1); - GameObject obj1 = mgr.new_object("obj1", "obj1", vec2{0, 0}, 0, 1); + GameObject obj0 = mgr.new_object("obj0", "obj0", vec2 {0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("obj1", "obj1", vec2 {0, 0}, 0, 1); obj1.set_persistent(); - GameObject obj2 = mgr.new_object("obj2", "obj2", vec2{0, 0}, 0, 1); + GameObject obj2 = mgr.new_object("obj2", "obj2", vec2 {0, 0}, 0, 1); vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>(); vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>(); @@ -329,8 +331,8 @@ TEST_F(ECSTest, persistent) { EXPECT_EQ(transform[0].get().position.x, 0); EXPECT_EQ(transform[0].get().position.y, 0); - GameObject obj3 = mgr.new_object("obj3", "obj3", vec2{0, 0}, 0, 5); - GameObject obj4 = mgr.new_object("obj4", "obj4", vec2{0, 0}, 0, 5); + GameObject obj3 = mgr.new_object("obj3", "obj3", vec2 {0, 0}, 0, 5); + GameObject obj4 = mgr.new_object("obj4", "obj4", vec2 {0, 0}, 0, 5); metadata = mgr.get_components_by_type<Metadata>(); transform = mgr.get_components_by_type<Transform>(); @@ -358,10 +360,10 @@ TEST_F(ECSTest, persistent) { } TEST_F(ECSTest, resetPersistent) { - GameObject obj0 = mgr.new_object("obj0", "obj0", vec2{0, 0}, 0, 1); - GameObject obj1 = mgr.new_object("obj1", "obj1", vec2{0, 0}, 0, 1); + GameObject obj0 = mgr.new_object("obj0", "obj0", vec2 {0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("obj1", "obj1", vec2 {0, 0}, 0, 1); obj1.set_persistent(); - GameObject obj2 = mgr.new_object("obj2", "obj2", vec2{0, 0}, 0, 1); + GameObject obj2 = mgr.new_object("obj2", "obj2", vec2 {0, 0}, 0, 1); vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>(); vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>(); @@ -466,3 +468,17 @@ TEST_F(ECSTest, ComponentsByTag) { EXPECT_EQ(objects.size(), 3); } } + +TEST_F(ECSTest, Snapshot) { + GameObject foo = mgr.new_object("foo"); + + foo.transform.position = {1, 1}; + + ComponentManager::Snapshot snapshot = mgr.save(); + + foo.transform.position = {0, 0}; + + mgr.restore(snapshot); + + EXPECT_EQ(foo.transform.position, (vec2 {1, 1})); +} diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index f8be3fe..6105679 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -10,7 +10,7 @@ using namespace crepe; class EventManagerTest : public ::testing::Test { protected: Mediator mediator; - EventManager event_mgr{mediator}; + EventManager event_mgr {mediator}; void SetUp() override { // Clear any existing subscriptions or events before each test event_mgr.clear(); @@ -30,18 +30,20 @@ TEST_F(EventManagerTest, EventSubscription) { // Verify subscription (not directly verifiable; test by triggering event) event_mgr.trigger_event<KeyPressEvent>( - KeyPressEvent{ + KeyPressEvent { .repeat = true, .key = Keycode::A, }, - 1); + 1 + ); event_mgr.trigger_event<KeyPressEvent>( - KeyPressEvent{ + KeyPressEvent { .repeat = true, .key = Keycode::A, }, - EventManager::CHANNEL_ALL); + EventManager::CHANNEL_ALL + ); } TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { bool triggered = false; @@ -55,7 +57,7 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { }; event_mgr.subscribe<MouseClickEvent>(mouse_handler, EventManager::CHANNEL_ALL); - MouseClickEvent click_event{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; + MouseClickEvent click_event {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); EXPECT_TRUE(triggered); @@ -72,7 +74,7 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_one_channel) { }; event_mgr.subscribe<MouseClickEvent>(mouse_handler, test_channel); - MouseClickEvent click_event{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; + MouseClickEvent click_event {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); EXPECT_FALSE(triggered); @@ -103,7 +105,7 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { }; // Test event - MouseClickEvent click_event{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; + MouseClickEvent click_event {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; event_mgr.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL); event_mgr.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL); @@ -151,10 +153,12 @@ TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) { event_mgr.subscribe<MouseClickEvent>(mouse_handler2, test_channel); event_mgr.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}); + MouseClickEvent {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE} + ); event_mgr.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}, - test_channel); + MouseClickEvent {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}, + test_channel + ); event_mgr.dispatch_events(); EXPECT_TRUE(triggered1); EXPECT_TRUE(triggered2); @@ -188,7 +192,8 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { // Queue events event_mgr.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}); + MouseClickEvent {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE} + ); // Dispatch events - both handlers should be triggered event_mgr.dispatch_events(); @@ -204,7 +209,8 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { // Queue the same event again event_mgr.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}); + MouseClickEvent {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE} + ); // Dispatch events - only handler 2 should be triggered, handler 1 should NOT event_mgr.dispatch_events(); @@ -219,7 +225,8 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { // Queue the event again event_mgr.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}); + MouseClickEvent {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE} + ); // Dispatch events - no handler should be triggered event_mgr.dispatch_events(); diff --git a/src/test/InputTest.cpp b/src/test/InputTest.cpp index ce8ea44..9a541a0 100644 --- a/src/test/InputTest.cpp +++ b/src/test/InputTest.cpp @@ -28,22 +28,24 @@ using namespace crepe; class InputTest : public ::testing::Test { public: Mediator mediator; - ComponentManager mgr{mediator}; - SDLContext sdl_context{mediator}; + ComponentManager mgr {mediator}; + SDLContext sdl_context {mediator}; - InputSystem input_system{mediator}; - ResourceManager resman{mediator}; - RenderSystem render{mediator}; - EventManager event_manager{mediator}; + InputSystem input_system {mediator}; + ResourceManager resman {mediator}; + RenderSystem render {mediator}; + EventManager event_manager {mediator}; //GameObject camera; + vec2 offset = {100, 200}; protected: void SetUp() override { - GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); - auto & camera - = obj.add_component<Camera>(ivec2{500, 500}, vec2{500, 500}, - Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f}); - render.update(); + GameObject obj = mgr.new_object("camera", "camera", offset, 0, 1); + auto & camera = obj.add_component<Camera>( + ivec2 {500, 500}, vec2 {500, 500}, + Camera::Data {.bg_color = Color::WHITE, .zoom = 1.0f} + ); + render.frame_update(); //mediator.event_manager = event_manager; //mediator.component_manager = mgr; //event_manager.clear(); @@ -75,8 +77,7 @@ TEST_F(InputTest, MouseDown) { EventHandler<MousePressEvent> on_mouse_down = [&](const MousePressEvent & event) { mouse_triggered = true; //middle of the screen = 0,0 - EXPECT_EQ(event.mouse_pos.x, 0); - EXPECT_EQ(event.mouse_pos.y, 0); + EXPECT_EQ(event.mouse_pos, offset); EXPECT_EQ(event.button, MouseButton::LEFT_MOUSE); return false; }; @@ -91,7 +92,7 @@ TEST_F(InputTest, MouseDown) { event.button.button = SDL_BUTTON_LEFT; SDL_PushEvent(&event); - input_system.update(); + input_system.fixed_update(); event_manager.dispatch_events(); EXPECT_TRUE(mouse_triggered); } @@ -100,8 +101,7 @@ TEST_F(InputTest, MouseUp) { bool function_triggered = false; EventHandler<MouseReleaseEvent> on_mouse_release = [&](const MouseReleaseEvent & e) { function_triggered = true; - EXPECT_EQ(e.mouse_pos.x, 0); - EXPECT_EQ(e.mouse_pos.y, 0); + EXPECT_EQ(e.mouse_pos, offset); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; @@ -115,7 +115,7 @@ TEST_F(InputTest, MouseUp) { event.button.button = SDL_BUTTON_LEFT; SDL_PushEvent(&event); - input_system.update(); + input_system.fixed_update(); event_manager.dispatch_events(); EXPECT_TRUE(function_triggered); } @@ -124,8 +124,7 @@ TEST_F(InputTest, MouseMove) { bool function_triggered = false; EventHandler<MouseMoveEvent> on_mouse_move = [&](const MouseMoveEvent & e) { function_triggered = true; - EXPECT_EQ(e.mouse_pos.x, 0); - EXPECT_EQ(e.mouse_pos.y, 0); + EXPECT_EQ(e.mouse_pos, offset); EXPECT_EQ(e.mouse_delta.x, 10); EXPECT_EQ(e.mouse_delta.y, 10); return false; @@ -141,7 +140,7 @@ TEST_F(InputTest, MouseMove) { event.motion.yrel = 10; SDL_PushEvent(&event); - input_system.update(); + input_system.fixed_update(); event_manager.dispatch_events(); EXPECT_TRUE(function_triggered); } @@ -167,7 +166,7 @@ TEST_F(InputTest, KeyDown) { test_event.key.repeat = 1; // Set repeat flag SDL_PushEvent(&test_event); - input_system.update(); // Process the event + input_system.fixed_update(); // Process the event event_manager.dispatch_events(); // Dispatch events to handlers EXPECT_TRUE(function_triggered); // Check if the handler was triggered @@ -188,7 +187,7 @@ TEST_F(InputTest, KeyUp) { event.key.keysym.scancode = SDL_SCANCODE_B; SDL_PushEvent(&event); - input_system.update(); + input_system.fixed_update(); event_manager.dispatch_events(); EXPECT_TRUE(function_triggered); } @@ -198,43 +197,86 @@ TEST_F(InputTest, MouseClick) { EventHandler<MouseClickEvent> on_mouse_click = [&](const MouseClickEvent & event) { on_click_triggered = true; EXPECT_EQ(event.button, MouseButton::LEFT_MOUSE); - EXPECT_EQ(event.mouse_pos.x, 0); - EXPECT_EQ(event.mouse_pos.y, 0); + EXPECT_EQ(event.mouse_pos, offset); return false; }; event_manager.subscribe<MouseClickEvent>(on_mouse_click); this->simulate_mouse_click(250, 250, SDL_BUTTON_LEFT); - input_system.update(); + input_system.fixed_update(); event_manager.dispatch_events(); EXPECT_TRUE(on_click_triggered); } TEST_F(InputTest, testButtonClick) { - GameObject button_obj = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); + GameObject button_obj = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); bool button_clicked = false; event_manager.subscribe<ButtonPressEvent>([&](const ButtonPressEvent & event) { button_clicked = true; EXPECT_EQ(event.metadata.game_object_id, button_obj.id); return false; }); - auto & button = button_obj.add_component<Button>(vec2{100, 100}, vec2{0, 0}); + auto & button = button_obj.add_component<Button>(vec2 {100, 100}, vec2 {0, 0}); bool hover = false; button.active = true; this->simulate_mouse_click(999, 999, SDL_BUTTON_LEFT); - input_system.update(); + input_system.fixed_update(); event_manager.dispatch_events(); EXPECT_FALSE(button_clicked); this->simulate_mouse_click(250, 250, SDL_BUTTON_LEFT); - input_system.update(); + input_system.fixed_update(); event_manager.dispatch_events(); EXPECT_TRUE(button_clicked); } +TEST_F(InputTest, buttonPositionCamera) { + GameObject button_obj = mgr.new_object("body", "person", vec2 {50, 50}, 0, 1); + bool button_clicked = false; + event_manager.subscribe<ButtonPressEvent>([&](const ButtonPressEvent & event) { + button_clicked = true; + EXPECT_EQ(event.metadata.game_object_id, button_obj.id); + return false; + }); + auto & button = button_obj.add_component<Button>(vec2 {10, 10}, vec2 {0, 0}); + + button.world_space = false; + bool hover = false; + button.active = true; + this->simulate_mouse_click(999, 999, SDL_BUTTON_LEFT); + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_FALSE(button_clicked); + + this->simulate_mouse_click(300, 300, SDL_BUTTON_LEFT); + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_TRUE(button_clicked); +} +TEST_F(InputTest, buttonPositionWorld) { + GameObject button_obj = mgr.new_object("body", "person", vec2 {50, 50}, 0, 1); + bool button_clicked = false; + event_manager.subscribe<ButtonPressEvent>([&](const ButtonPressEvent & event) { + button_clicked = true; + EXPECT_EQ(event.metadata.game_object_id, button_obj.id); + return false; + }); + auto & button = button_obj.add_component<Button>(vec2 {10, 10}, vec2 {0, 0}); + button.world_space = true; + bool hover = false; + button.active = true; + this->simulate_mouse_click(999, 999, SDL_BUTTON_LEFT); + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_FALSE(button_clicked); + this->simulate_mouse_click(300, 300, SDL_BUTTON_LEFT); + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_FALSE(button_clicked); +} TEST_F(InputTest, testButtonHover) { - GameObject button_obj = mgr.new_object("body", "person", vec2{0, 0}, 0, 1); + GameObject button_obj = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); bool button_hover = false; event_manager.subscribe<ButtonEnterEvent>([&](const ButtonEnterEvent & event) { button_hover = true; @@ -246,7 +288,7 @@ TEST_F(InputTest, testButtonHover) { EXPECT_EQ(event.metadata.game_object_id, button_obj.id); return false; }); - auto & button = button_obj.add_component<Button>(vec2{100, 100}, vec2{0, 0}); + auto & button = button_obj.add_component<Button>(vec2 {100, 100}, vec2 {0, 0}); button.active = true; // Mouse on button SDL_Event hover_event; @@ -258,7 +300,7 @@ TEST_F(InputTest, testButtonHover) { hover_event.motion.yrel = 10; SDL_PushEvent(&hover_event); - input_system.update(); + input_system.fixed_update(); event_manager.dispatch_events(); EXPECT_TRUE(button.hover); EXPECT_TRUE(button_hover); @@ -272,7 +314,7 @@ TEST_F(InputTest, testButtonHover) { event.motion.yrel = 10; SDL_PushEvent(&event); - input_system.update(); + input_system.fixed_update(); event_manager.dispatch_events(); EXPECT_FALSE(button.hover); EXPECT_FALSE(button_hover); diff --git a/src/test/LoopManagerTest.cpp b/src/test/LoopManagerTest.cpp index df132ae..302d96c 100644 --- a/src/test/LoopManagerTest.cpp +++ b/src/test/LoopManagerTest.cpp @@ -4,7 +4,7 @@ #include <thread> #define private public #define protected public -#include <crepe/api/LoopManager.h> +#include <crepe/api/Engine.h> #include <crepe/manager/EventManager.h> #include <crepe/manager/LoopTimerManager.h> using namespace std::chrono; @@ -12,7 +12,7 @@ using namespace crepe; class DISABLED_LoopManagerTest : public ::testing::Test { protected: - class TestGameLoop : public crepe::LoopManager { + class TestGameLoop : public crepe::Engine { public: MOCK_METHOD(void, fixed_update, (), (override)); MOCK_METHOD(void, frame_update, (), (override)); @@ -72,7 +72,7 @@ TEST_F(DISABLED_LoopManagerTest, ShutDown) { // Start the loop in a separate thread std::thread loop_thread([&]() { test_loop.start(); }); std::this_thread::sleep_for(std::chrono::milliseconds(1)); - test_loop.event_manager.trigger_event<ShutDownEvent>(ShutDownEvent{}); + test_loop.event_manager.trigger_event<ShutDownEvent>(ShutDownEvent {}); // Wait for the loop thread to finish loop_thread.join(); } diff --git a/src/test/LoopTimerTest.cpp b/src/test/LoopTimerTest.cpp index 7bd6305..52e412e 100644 --- a/src/test/LoopTimerTest.cpp +++ b/src/test/LoopTimerTest.cpp @@ -14,7 +14,7 @@ using namespace crepe; class LoopTimerTest : public ::testing::Test { protected: Mediator mediator; - LoopTimerManager loop_timer{mediator}; + LoopTimerManager loop_timer {mediator}; void SetUp() override { loop_timer.start(); } }; diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp index 9263e00..eee7a73 100644 --- a/src/test/ParticleTest.cpp +++ b/src/test/ParticleTest.cpp @@ -22,45 +22,49 @@ class ParticlesTest : public ::testing::Test { Mediator m; public: - ComponentManager component_manager{m}; - ParticleSystem particle_system{m}; - LoopTimerManager loop_timer{m}; + ComponentManager component_manager {m}; + ParticleSystem particle_system {m}; + LoopTimerManager loop_timer {m}; void SetUp() override { ComponentManager & mgr = this->component_manager; std::vector<std::reference_wrapper<Transform>> transforms = mgr.get_components_by_id<Transform>(0); if (transforms.empty()) { - GameObject game_object = mgr.new_object("", "", vec2{0, 0}, 0, 0); + GameObject game_object = mgr.new_object("", "", vec2 {0, 0}, 0, 0); Color color(0, 0, 0, 0); auto s1 = Asset("asset/texture/img.png"); Sprite & test_sprite = game_object.add_component<Sprite>( - s1, Sprite::Data{ - .color = color, - .flip = Sprite::FlipSettings{true, true}, - .size = {10, 10}, - }); + s1, + Sprite::Data { + .color = color, + .flip = Sprite::FlipSettings {true, true}, + .size = {10, 10}, + } + ); - game_object.add_component<ParticleEmitter>(test_sprite, - ParticleEmitter::Data{ - .offset = {0, 0}, - .max_particles = 100, - .emission_rate = 0, - .min_speed = 0, - .max_speed = 0, - .min_angle = 0, - .max_angle = 0, - .begin_lifespan = 0, - .end_lifespan = 0, - .force_over_time = vec2{0, 0}, - .boundary{ - .width = 0, - .height = 0, - .offset = vec2{0, 0}, - .reset_on_exit = false, - }, - }); + game_object.add_component<ParticleEmitter>( + test_sprite, + ParticleEmitter::Data { + .offset = {0, 0}, + .max_particles = 100, + .emission_rate = 0, + .min_speed = 0, + .max_speed = 0, + .min_angle = 0, + .max_angle = 0, + .begin_lifespan = 0, + .end_lifespan = 0, + .force_over_time = vec2 {0, 0}, + .boundary { + .width = 0, + .height = 0, + .offset = vec2 {0, 0}, + .reset_on_exit = false, + }, + } + ); } transforms = mgr.get_components_by_id<Transform>(0); Transform & transform = transforms.front().get(); @@ -78,8 +82,8 @@ public: emitter.data.max_angle = 0; emitter.data.begin_lifespan = 0; emitter.data.end_lifespan = 0; - emitter.data.force_over_time = vec2{0, 0}; - emitter.data.boundary = {0, 0, vec2{0, 0}, false}; + emitter.data.force_over_time = vec2 {0, 0}; + emitter.data.boundary = {0, 0, vec2 {0, 0}, false}; for (auto & particle : emitter.particles) { particle.active = false; } @@ -97,18 +101,18 @@ TEST_F(ParticlesTest, spawnParticle) { emitter.data.max_angle = 0.1; emitter.data.max_speed = 10; emitter.data.max_angle = 10; - particle_system.update(); + particle_system.fixed_update(); //check if nothing happend EXPECT_EQ(emitter.particles[0].active, false); emitter.data.emission_rate = 50; //check particle spawnes - particle_system.update(); + particle_system.fixed_update(); EXPECT_EQ(emitter.particles[0].active, true); - particle_system.update(); + particle_system.fixed_update(); EXPECT_EQ(emitter.particles[1].active, true); - particle_system.update(); + particle_system.fixed_update(); EXPECT_EQ(emitter.particles[2].active, true); - particle_system.update(); + particle_system.fixed_update(); EXPECT_EQ(emitter.particles[3].active, true); for (auto & particle : emitter.particles) { @@ -142,7 +146,7 @@ TEST_F(ParticlesTest, moveParticleHorizontal) { emitter.data.max_angle = 0; emitter.data.emission_rate = 50; for (int a = 1; a < emitter.data.boundary.width / 2; a++) { - particle_system.update(); + particle_system.fixed_update(); EXPECT_EQ(emitter.particles[0].position.x, a); } } @@ -160,7 +164,7 @@ TEST_F(ParticlesTest, moveParticleVertical) { emitter.data.max_angle = 90; emitter.data.emission_rate = 50; for (int a = 1; a < emitter.data.boundary.width / 2; a++) { - particle_system.update(); + particle_system.fixed_update(); EXPECT_EQ(emitter.particles[0].position.y, a); } } @@ -179,7 +183,7 @@ TEST_F(ParticlesTest, boundaryParticleReset) { emitter.data.max_angle = 90; emitter.data.emission_rate = 1; for (int a = 0; a < emitter.data.boundary.width / 2 + 1; a++) { - particle_system.update(); + particle_system.fixed_update(); } EXPECT_EQ(emitter.particles[0].active, false); } @@ -198,15 +202,19 @@ TEST_F(ParticlesTest, boundaryParticleStop) { emitter.data.max_angle = 90; emitter.data.emission_rate = 1; for (int a = 0; a < emitter.data.boundary.width / 2 + 1; a++) { - particle_system.update(); + particle_system.fixed_update(); } const double TOLERANCE = 0.01; EXPECT_NEAR(emitter.particles[0].velocity.x, 0, TOLERANCE); EXPECT_NEAR(emitter.particles[0].velocity.y, 0, TOLERANCE); if (emitter.particles[0].velocity.x != 0) - EXPECT_NEAR(std::abs(emitter.particles[0].position.x), - emitter.data.boundary.height / 2, TOLERANCE); + EXPECT_NEAR( + std::abs(emitter.particles[0].position.x), emitter.data.boundary.height / 2, + TOLERANCE + ); if (emitter.particles[0].velocity.y != 0) - EXPECT_NEAR(std::abs(emitter.particles[0].position.y), emitter.data.boundary.width / 2, - TOLERANCE); + EXPECT_NEAR( + std::abs(emitter.particles[0].position.y), emitter.data.boundary.width / 2, + TOLERANCE + ); } diff --git a/src/test/PhysicsTest.cpp b/src/test/PhysicsTest.cpp index 3afb3c7..85eb6d5 100644 --- a/src/test/PhysicsTest.cpp +++ b/src/test/PhysicsTest.cpp @@ -16,17 +16,17 @@ class PhysicsTest : public ::testing::Test { Mediator m; public: - ComponentManager component_manager{m}; - PhysicsSystem system{m}; - LoopTimerManager loop_timer{m}; + ComponentManager component_manager {m}; + PhysicsSystem system {m}; + LoopTimerManager loop_timer {m}; void SetUp() override { ComponentManager & mgr = this->component_manager; vector<reference_wrapper<Transform>> transforms = mgr.get_components_by_id<Transform>(0); if (transforms.empty()) { - auto entity = mgr.new_object("", "", vec2{0, 0}, 0, 0); - entity.add_component<Rigidbody>(Rigidbody::Data{ + auto entity = mgr.new_object("", "", vec2 {0, 0}, 0, 0); + entity.add_component<Rigidbody>(Rigidbody::Data { .mass = 1, .gravity_scale = 1, .body_type = Rigidbody::BodyType::DYNAMIC, @@ -57,10 +57,10 @@ TEST_F(PhysicsTest, gravity) { ASSERT_FALSE(transforms.empty()); EXPECT_EQ(transform.position.y, 0); - system.update(); + system.fixed_update(); EXPECT_NEAR(transform.position.y, 0.0004, 0.0001); - system.update(); + system.fixed_update(); EXPECT_NEAR(transform.position.y, 0.002, 0.001); } @@ -74,14 +74,14 @@ TEST_F(PhysicsTest, max_velocity) { rigidbody.add_force_linear({100, 100}); rigidbody.add_force_angular(100); - system.update(); + system.fixed_update(); EXPECT_NEAR(rigidbody.data.linear_velocity.y, 7.07, 0.01); EXPECT_NEAR(rigidbody.data.linear_velocity.x, 7.07, 0.01); EXPECT_EQ(rigidbody.data.angular_velocity, 10); rigidbody.add_force_linear({-100, -100}); rigidbody.add_force_angular(-100); - system.update(); + system.fixed_update(); EXPECT_NEAR(rigidbody.data.linear_velocity.y, -7.07, 0.01); EXPECT_NEAR(rigidbody.data.linear_velocity.x, -7.07, 0.01); EXPECT_EQ(rigidbody.data.angular_velocity, -10); @@ -99,7 +99,7 @@ TEST_F(PhysicsTest, movement) { rigidbody.add_force_linear({1, 1}); rigidbody.add_force_angular(1); - system.update(); + system.fixed_update(); EXPECT_NEAR(transform.position.x, 0.02, 0.001); EXPECT_NEAR(transform.position.y, 0.02, 0.001); EXPECT_NEAR(transform.rotation, 0.02, 0.001); @@ -112,7 +112,7 @@ TEST_F(PhysicsTest, movement) { rigidbody.data.linear_velocity_coefficient.x = 0.5; rigidbody.data.linear_velocity_coefficient.y = 0.5; rigidbody.data.angular_velocity_coefficient = 0.5; - system.update(); + system.fixed_update(); EXPECT_NEAR(rigidbody.data.linear_velocity.x, 0.98, 0.01); EXPECT_NEAR(rigidbody.data.linear_velocity.y, 0.98, 0.01); EXPECT_NEAR(rigidbody.data.angular_velocity, 0.98, 0.01); @@ -121,12 +121,12 @@ TEST_F(PhysicsTest, movement) { rigidbody.data.angular_velocity_coefficient = 0; rigidbody.data.max_angular_velocity = 1000; rigidbody.data.angular_velocity = 360; - system.update(); + system.fixed_update(); EXPECT_NEAR(transform.rotation, 7.24, 0.01); rigidbody.data.angular_velocity = -360; - system.update(); + system.fixed_update(); EXPECT_NEAR(transform.rotation, 0.04, 0.001); - system.update(); + system.fixed_update(); EXPECT_NEAR(transform.rotation, 352.84, 0.01); } diff --git a/src/test/Profiling.cpp b/src/test/Profiling.cpp index cd8a40e..d8bd09d 100644 --- a/src/test/Profiling.cpp +++ b/src/test/Profiling.cpp @@ -36,10 +36,11 @@ class TestScript : public Script { return true; } void init() { - subscribe<CollisionEvent>( - [this](const CollisionEvent & ev) -> bool { return this->oncollision(ev); }); + subscribe<CollisionEvent>([this](const CollisionEvent & ev) -> bool { + return this->oncollision(ev); + }); } - void update() { + void fixed_update() { // Retrieve component from the same GameObject this script is on } }; @@ -57,17 +58,17 @@ public: const std::chrono::microseconds duration = 16000us; Mediator m; - SDLContext sdl_context{m}; - ResourceManager resman{m}; - ComponentManager mgr{m}; + SDLContext sdl_context {m}; + ResourceManager resman {m}; + ComponentManager mgr {m}; // Add system used for profling tests - EventManager evmgr{m}; - LoopTimerManager loopmgr{m}; - CollisionSystem collision_sys{m}; - PhysicsSystem physics_sys{m}; - ParticleSystem particle_sys{m}; - RenderSystem render_sys{m}; - ScriptSystem script_sys{m}; + EventManager evmgr {m}; + LoopTimerManager loopmgr {m}; + CollisionSystem collision_sys {m}; + PhysicsSystem physics_sys {m}; + ParticleSystem particle_sys {m}; + RenderSystem render_sys {m}; + ScriptSystem script_sys {m}; // Test data std::map<std::string, std::chrono::microseconds> timings; @@ -77,16 +78,18 @@ public: void SetUp() override { GameObject do_not_use = mgr.new_object("DO_NOT_USE", "", {0, 0}); - do_not_use.add_component<Camera>(ivec2{1080, 720}, vec2{2000, 2000}, - Camera::Data{ - .bg_color = Color::WHITE, - .zoom = 1.0f, - }); + do_not_use.add_component<Camera>( + ivec2 {1080, 720}, vec2 {2000, 2000}, + Camera::Data { + .bg_color = Color::WHITE, + .zoom = 1.0f, + } + ); // initialize systems here: //calls init - script_sys.update(); + script_sys.fixed_update(); //creates window - render_sys.update(); + render_sys.frame_update(); } // Helper function to time an update call and store its duration @@ -104,12 +107,14 @@ public: // Run and profile all systems, return the total time in milliseconds std::chrono::microseconds run_all_systems() { std::chrono::microseconds total_microseconds = 0us; - total_microseconds += time_function("PhysicsSystem", [&]() { physics_sys.update(); }); total_microseconds - += time_function("CollisionSystem", [&]() { collision_sys.update(); }); + += time_function("PhysicsSystem", [&]() { physics_sys.fixed_update(); }); total_microseconds - += time_function("ParticleSystem", [&]() { particle_sys.update(); }); - total_microseconds += time_function("RenderSystem", [&]() { render_sys.update(); }); + += time_function("CollisionSystem", [&]() { collision_sys.fixed_update(); }); + total_microseconds + += time_function("ParticleSystem", [&]() { particle_sys.fixed_update(); }); + total_microseconds + += time_function("RenderSystem", [&]() { render_sys.frame_update(); }); return total_microseconds; } @@ -166,23 +171,25 @@ TEST_F(DISABLED_ProfilingTest, Profiling_2) { { //define gameobject used for testing GameObject gameobject = mgr.new_object( - "gameobject", "", {static_cast<float>(game_object_count * 2), 0}); - gameobject.add_component<Rigidbody>(Rigidbody::Data{ + "gameobject", "", {static_cast<float>(game_object_count * 2), 0} + ); + gameobject.add_component<Rigidbody>(Rigidbody::Data { .gravity_scale = 0.0, .body_type = Rigidbody::BodyType::STATIC, }); - gameobject.add_component<BoxCollider>(vec2{0, 0}, vec2{1, 1}); + gameobject.add_component<BoxCollider>(vec2 {0, 0}, vec2 {1, 1}); gameobject.add_component<BehaviorScript>().set_script<TestScript>(); Sprite & test_sprite = gameobject.add_component<Sprite>( - Asset{"asset/texture/square.png"}, - Sprite::Data{ + Asset {"asset/texture/square.png"}, + Sprite::Data { .color = {0, 0, 0, 0}, .flip = {.flip_x = false, .flip_y = false}, .sorting_in_layer = 1, .order_in_layer = 1, .size = {.y = 500}, - }); + } + ); } this->game_object_count++; @@ -205,37 +212,41 @@ TEST_F(DISABLED_ProfilingTest, Profiling_3) { { //define gameobject used for testing GameObject gameobject = mgr.new_object( - "gameobject", "", {static_cast<float>(game_object_count * 2), 0}); - gameobject.add_component<Rigidbody>(Rigidbody::Data{ + "gameobject", "", {static_cast<float>(game_object_count * 2), 0} + ); + gameobject.add_component<Rigidbody>(Rigidbody::Data { .gravity_scale = 0, .body_type = Rigidbody::BodyType::STATIC, }); - gameobject.add_component<BoxCollider>(vec2{0, 0}, vec2{1, 1}); + gameobject.add_component<BoxCollider>(vec2 {0, 0}, vec2 {1, 1}); gameobject.add_component<BehaviorScript>().set_script<TestScript>(); Sprite & test_sprite = gameobject.add_component<Sprite>( - Asset{"asset/texture/square.png"}, - Sprite::Data{ + Asset {"asset/texture/square.png"}, + Sprite::Data { .color = {0, 0, 0, 0}, .flip = {.flip_x = false, .flip_y = false}, .sorting_in_layer = 1, .order_in_layer = 1, .size = {.y = 500}, - }); + } + ); auto & test = gameobject.add_component<ParticleEmitter>( - test_sprite, ParticleEmitter::Data{ - .max_particles = 10, - .emission_rate = 100, - .end_lifespan = 100000, - .boundary{ - .width = 1000, - .height = 1000, - .offset = vec2{0, 0}, - .reset_on_exit = false, - }, - - }); + test_sprite, + ParticleEmitter::Data { + .max_particles = 10, + .emission_rate = 100, + .end_lifespan = 100000, + .boundary { + .width = 1000, + .height = 1000, + .offset = vec2 {0, 0}, + .reset_on_exit = false, + }, + + } + ); } - render_sys.update(); + render_sys.frame_update(); this->game_object_count++; this->total_time = 0us; diff --git a/src/test/RenderSystemTest.cpp b/src/test/RenderSystemTest.cpp index b4519cb..bdd87ee 100644 --- a/src/test/RenderSystemTest.cpp +++ b/src/test/RenderSystemTest.cpp @@ -26,10 +26,10 @@ class RenderSystemTest : public Test { Mediator m; public: - ComponentManager mgr{m}; - SDLContext ctx{m}; - ResourceManager resource_manager{m}; - RenderSystem sys{m}; + ComponentManager mgr {m}; + SDLContext ctx {m}; + ResourceManager resource_manager {m}; + RenderSystem sys {m}; GameObject entity1 = this->mgr.new_object("name"); GameObject entity2 = this->mgr.new_object("name"); GameObject entity3 = this->mgr.new_object("name"); @@ -40,44 +40,52 @@ public: auto s2 = Asset("asset/texture/img.png"); auto s3 = Asset("asset/texture/img.png"); auto s4 = Asset("asset/texture/img.png"); - auto & sprite1 - = entity1.add_component<Sprite>(s1, Sprite::Data{ - .color = Color(0, 0, 0, 0), - .flip = Sprite::FlipSettings{false, false}, - .sorting_in_layer = 5, - .order_in_layer = 5, - .size = {10, 10}, - }); + auto & sprite1 = entity1.add_component<Sprite>( + s1, + Sprite::Data { + .color = Color(0, 0, 0, 0), + .flip = Sprite::FlipSettings {false, false}, + .sorting_in_layer = 5, + .order_in_layer = 5, + .size = {10, 10}, + } + ); EXPECT_EQ(sprite1.data.order_in_layer, 5); EXPECT_EQ(sprite1.data.sorting_in_layer, 5); - auto & sprite2 - = entity2.add_component<Sprite>(s2, Sprite::Data{ - .color = Color(0, 0, 0, 0), - .flip = Sprite::FlipSettings{false, false}, - .sorting_in_layer = 2, - .order_in_layer = 1, - }); + auto & sprite2 = entity2.add_component<Sprite>( + s2, + Sprite::Data { + .color = Color(0, 0, 0, 0), + .flip = Sprite::FlipSettings {false, false}, + .sorting_in_layer = 2, + .order_in_layer = 1, + } + ); EXPECT_EQ(sprite2.data.sorting_in_layer, 2); EXPECT_EQ(sprite2.data.order_in_layer, 1); - auto & sprite3 - = entity3.add_component<Sprite>(s3, Sprite::Data{ - .color = Color(0, 0, 0, 0), - .flip = Sprite::FlipSettings{false, false}, - .sorting_in_layer = 1, - .order_in_layer = 2, - }); + auto & sprite3 = entity3.add_component<Sprite>( + s3, + Sprite::Data { + .color = Color(0, 0, 0, 0), + .flip = Sprite::FlipSettings {false, false}, + .sorting_in_layer = 1, + .order_in_layer = 2, + } + ); EXPECT_EQ(sprite3.data.sorting_in_layer, 1); EXPECT_EQ(sprite3.data.order_in_layer, 2); - auto & sprite4 - = entity4.add_component<Sprite>(s4, Sprite::Data{ - .color = Color(0, 0, 0, 0), - .flip = Sprite::FlipSettings{false, false}, - .sorting_in_layer = 1, - .order_in_layer = 1, - }); + auto & sprite4 = entity4.add_component<Sprite>( + s4, + Sprite::Data { + .color = Color(0, 0, 0, 0), + .flip = Sprite::FlipSettings {false, false}, + .sorting_in_layer = 1, + .order_in_layer = 1, + } + ); EXPECT_EQ(sprite4.data.sorting_in_layer, 1); EXPECT_EQ(sprite4.data.order_in_layer, 1); } @@ -85,7 +93,7 @@ public: TEST_F(RenderSystemTest, NoCamera) { // No camera - EXPECT_ANY_THROW({ this->sys.update(); }); + EXPECT_ANY_THROW({ this->sys.frame_update(); }); } TEST_F(RenderSystemTest, make_sprites) {} @@ -128,8 +136,10 @@ TEST_F(RenderSystemTest, sorting_sprites) { } TEST_F(RenderSystemTest, Update) { - entity1.add_component<Camera>(ivec2{100, 100}, vec2{100, 100}, - Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f}); + entity1.add_component<Camera>( + ivec2 {100, 100}, vec2 {100, 100}, + Camera::Data {.bg_color = Color::WHITE, .zoom = 1.0f} + ); { vector<reference_wrapper<Sprite>> sprites = this->mgr.get_components_by_type<Sprite>(); ASSERT_EQ(sprites.size(), 4); @@ -139,7 +149,7 @@ TEST_F(RenderSystemTest, Update) { EXPECT_EQ(sprites[2].get().game_object_id, 2); EXPECT_EQ(sprites[3].get().game_object_id, 3); } - this->sys.update(); + this->sys.frame_update(); { vector<reference_wrapper<Sprite>> sprites = this->mgr.get_components_by_type<Sprite>(); ASSERT_EQ(sprites.size(), 4); @@ -157,8 +167,10 @@ TEST_F(RenderSystemTest, Camera) { EXPECT_NE(cameras.size(), 1); } { - entity1.add_component<Camera>(ivec2{100, 100}, vec2{100, 100}, - Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f}); + entity1.add_component<Camera>( + ivec2 {100, 100}, vec2 {100, 100}, + Camera::Data {.bg_color = Color::WHITE, .zoom = 1.0f} + ); auto cameras = this->mgr.get_components_by_type<Camera>(); EXPECT_EQ(cameras.size(), 1); @@ -167,8 +179,10 @@ TEST_F(RenderSystemTest, Camera) { //TODO improve with newer version } TEST_F(RenderSystemTest, Color) { - entity1.add_component<Camera>(ivec2{100, 100}, vec2{100, 100}, - Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f}); + entity1.add_component<Camera>( + ivec2 {100, 100}, vec2 {100, 100}, + Camera::Data {.bg_color = Color::WHITE, .zoom = 1.0f} + ); auto & sprite = this->mgr.get_components_by_id<Sprite>(entity1.id).front().get(); //ASSERT_NE(sprite.texture.texture.get(), nullptr); @@ -178,7 +192,7 @@ TEST_F(RenderSystemTest, Color) { EXPECT_EQ(sprite.data.color.g, Color::GREEN.g); EXPECT_EQ(sprite.data.color.b, Color::GREEN.b); EXPECT_EQ(sprite.data.color.a, Color::GREEN.a); - this->sys.update(); + this->sys.frame_update(); EXPECT_EQ(sprite.data.color.r, Color::GREEN.r); EXPECT_EQ(sprite.data.color.g, Color::GREEN.g); EXPECT_EQ(sprite.data.color.b, Color::GREEN.b); diff --git a/src/test/ReplayManagerTest.cpp b/src/test/ReplayManagerTest.cpp new file mode 100644 index 0000000..b2619eb --- /dev/null +++ b/src/test/ReplayManagerTest.cpp @@ -0,0 +1,38 @@ +#include <gtest/gtest.h> + +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/Scene.h> +#include <crepe/api/Script.h> +#include <crepe/manager/ReplayManager.h> +#include <crepe/system/ReplaySystem.h> + +using namespace std; +using namespace crepe; +using namespace testing; + +class ReplayManagerTest : public Test { + Mediator mediator; + +public: + ComponentManager component_manager {mediator}; + ReplayManager replay_manager {mediator}; + ReplaySystem replay_system {mediator}; + + GameObject entity = component_manager.new_object("foo"); + Transform & entity_transform + = component_manager.get_components_by_id<Transform>(entity.id).back(); + Metadata & entity_metadata + = component_manager.get_components_by_id<Metadata>(entity.id).back(); +}; + +TEST_F(ReplayManagerTest, Default) { + // replay_manager.record_start(); + + // replay_system.fixed_update(); + // entity_transform.position += {1, 1}; + // replay_system.fixed_update(); + // entity_transform.position += {1, 1}; + // replay_system.fixed_update(); + + // recording_t recording = replay_manager.record_end(); +} diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp index 965eeab..e5a7fad 100644 --- a/src/test/ResourceManagerTest.cpp +++ b/src/test/ResourceManagerTest.cpp @@ -16,14 +16,14 @@ class ResourceManagerTest : public Test { Mediator mediator; public: - ResourceManager resource_manager{mediator}; + ResourceManager resource_manager {mediator}; class Unrelated : public Resource { using Resource::Resource; }; - Asset asset_a{"asset/texture/img.png"}; - Asset asset_b{"asset/texture/ERROR.png"}; + Asset asset_a {"asset/texture/img.png"}; + Asset asset_b {"asset/texture/ERROR.png"}; class TestResource : public Resource { public: diff --git a/src/test/SaveManagerTest.cpp b/src/test/SaveManagerTest.cpp index 7609e69..fd53200 100644 --- a/src/test/SaveManagerTest.cpp +++ b/src/test/SaveManagerTest.cpp @@ -14,12 +14,12 @@ class SaveManagerTest : public Test { using SaveManager::SaveManager; // in-memory database for testing - DB db{}; + DB db {}; virtual DB & get_db() override { return this->db; } }; public: - TestSaveManager mgr{m}; + TestSaveManager mgr {m}; }; TEST_F(SaveManagerTest, ReadWrite) { diff --git a/src/test/SceneManagerTest.cpp b/src/test/SceneManagerTest.cpp index 480e07a..e58ce36 100644 --- a/src/test/SceneManagerTest.cpp +++ b/src/test/SceneManagerTest.cpp @@ -15,9 +15,9 @@ using namespace crepe; class ConcreteScene1 : public Scene { public: void load_scene() { - GameObject object1 = new_object("scene_1", "tag_scene_1", vec2{0, 0}, 0, 1); - GameObject object2 = new_object("scene_1", "tag_scene_1", vec2{1, 0}, 0, 1); - GameObject object3 = new_object("scene_1", "tag_scene_1", vec2{2, 0}, 0, 1); + GameObject object1 = new_object("scene_1", "tag_scene_1", vec2 {0, 0}, 0, 1); + GameObject object2 = new_object("scene_1", "tag_scene_1", vec2 {1, 0}, 0, 1); + GameObject object3 = new_object("scene_1", "tag_scene_1", vec2 {2, 0}, 0, 1); } string get_name() const { return "scene1"; } @@ -26,10 +26,10 @@ public: class ConcreteScene2 : public Scene { public: void load_scene() { - GameObject object1 = new_object("scene_2", "tag_scene_2", vec2{0, 0}, 0, 1); - GameObject object2 = new_object("scene_2", "tag_scene_2", vec2{0, 1}, 0, 1); - GameObject object3 = new_object("scene_2", "tag_scene_2", vec2{0, 2}, 0, 1); - GameObject object4 = new_object("scene_2", "tag_scene_2", vec2{0, 3}, 0, 1); + GameObject object1 = new_object("scene_2", "tag_scene_2", vec2 {0, 0}, 0, 1); + GameObject object2 = new_object("scene_2", "tag_scene_2", vec2 {0, 1}, 0, 1); + GameObject object3 = new_object("scene_2", "tag_scene_2", vec2 {0, 2}, 0, 1); + GameObject object4 = new_object("scene_2", "tag_scene_2", vec2 {0, 3}, 0, 1); } string get_name() const { return "scene2"; } @@ -40,7 +40,7 @@ public: ConcreteScene3(const string & name) : name(name) {} void load_scene() { - GameObject object1 = new_object("scene_3", "tag_scene_3", vec2{0, 0}, 0, 1); + GameObject object1 = new_object("scene_3", "tag_scene_3", vec2 {0, 0}, 0, 1); } string get_name() const { return name; } @@ -53,8 +53,8 @@ class SceneManagerTest : public ::testing::Test { Mediator m; public: - ComponentManager component_mgr{m}; - SceneManager scene_mgr{m}; + ComponentManager component_mgr {m}; + SceneManager scene_mgr {m}; }; TEST_F(SceneManagerTest, loadScene) { diff --git a/src/test/ScriptEventTest.cpp b/src/test/ScriptEventTest.cpp index c1b4028..8b4a72d 100644 --- a/src/test/ScriptEventTest.cpp +++ b/src/test/ScriptEventTest.cpp @@ -23,7 +23,7 @@ class ScriptEventTest : public ScriptTest { public: EventManager & event_manager = mediator.event_manager; - class MyEvent : public Event {}; + struct MyEvent : public Event {}; }; TEST_F(ScriptEventTest, Default) { @@ -37,7 +37,7 @@ TEST_F(ScriptEventTest, Default) { return true; }); - system.update(); + system.fixed_update(); behaviorscript.active = false; EXPECT_EQ(0, event_count); diff --git a/src/test/ScriptSaveManagerTest.cpp b/src/test/ScriptSaveManagerTest.cpp index 64403c4..e2debae 100644 --- a/src/test/ScriptSaveManagerTest.cpp +++ b/src/test/ScriptSaveManagerTest.cpp @@ -19,11 +19,11 @@ public: using SaveManager::SaveManager; // in-memory database for testing - DB db{}; + DB db {}; virtual DB & get_db() override { return this->db; } }; - TestSaveManager save_mgr{mediator}; + TestSaveManager save_mgr {mediator}; }; TEST_F(ScriptSaveManagerTest, GetSaveManager) { diff --git a/src/test/ScriptSceneTest.cpp b/src/test/ScriptSceneTest.cpp index 2568049..7d01f14 100644 --- a/src/test/ScriptSceneTest.cpp +++ b/src/test/ScriptSceneTest.cpp @@ -13,7 +13,7 @@ using namespace testing; class ScriptSceneTest : public ScriptTest { public: - SceneManager scene_manager{mediator}; + SceneManager scene_manager {mediator}; class MyScene : public Scene {}; }; diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp index 846e398..40aa25c 100644 --- a/src/test/ScriptTest.cpp +++ b/src/test/ScriptTest.cpp @@ -28,7 +28,8 @@ void ScriptTest::SetUp() { TEST_F(ScriptTest, Default) { MyScript & script = this->script; EXPECT_CALL(script, init()).Times(0); - EXPECT_CALL(script, update(_)).Times(0); + EXPECT_CALL(script, fixed_update(_)).Times(0); + EXPECT_CALL(script, frame_update(_)).Times(0); } TEST_F(ScriptTest, UpdateOnce) { @@ -38,16 +39,23 @@ TEST_F(ScriptTest, UpdateOnce) { InSequence seq; EXPECT_CALL(script, init()).Times(1); - EXPECT_CALL(script, update(_)).Times(1); - system.update(); + EXPECT_CALL(script, fixed_update(_)).Times(1); + system.fixed_update(); } { InSequence seq; EXPECT_CALL(script, init()).Times(0); - EXPECT_CALL(script, update(_)).Times(1); - system.update(); + EXPECT_CALL(script, fixed_update(_)).Times(1); + system.fixed_update(); + } + + { + InSequence seq; + + EXPECT_CALL(script, frame_update(_)).Times(1); + system.frame_update(); } } @@ -59,18 +67,18 @@ TEST_F(ScriptTest, UpdateInactive) { InSequence seq; EXPECT_CALL(script, init()).Times(0); - EXPECT_CALL(script, update(_)).Times(0); + EXPECT_CALL(script, fixed_update(_)).Times(0); behaviorscript.active = false; - system.update(); + system.fixed_update(); } { InSequence seq; EXPECT_CALL(script, init()).Times(1); - EXPECT_CALL(script, update(_)).Times(1); + EXPECT_CALL(script, fixed_update(_)).Times(1); behaviorscript.active = true; - system.update(); + system.fixed_update(); } } diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h index f3dbda4..f953aab 100644 --- a/src/test/ScriptTest.h +++ b/src/test/ScriptTest.h @@ -17,11 +17,11 @@ protected: static constexpr const char * OBJ_NAME = "foo"; public: - crepe::ComponentManager component_manager{mediator}; - crepe::ScriptSystem system{mediator}; - crepe::EventManager event_mgr{mediator}; - crepe::LoopTimerManager loop_timer{mediator}; - crepe::SaveManager save_manager{mediator}; + crepe::ComponentManager component_manager {mediator}; + crepe::ScriptSystem system {mediator}; + crepe::EventManager event_mgr {mediator}; + crepe::LoopTimerManager loop_timer {mediator}; + crepe::SaveManager save_manager {mediator}; crepe::GameObject entity = component_manager.new_object(OBJ_NAME); class MyScript : public crepe::Script { @@ -29,7 +29,8 @@ public: public: MOCK_METHOD(void, init, (), (override)); - MOCK_METHOD(void, update, (crepe::duration_t), (override)); + MOCK_METHOD(void, fixed_update, (crepe::duration_t), (override)); + MOCK_METHOD(void, frame_update, (crepe::duration_t), (override)); }; crepe::OptionalRef<crepe::BehaviorScript> behaviorscript; diff --git a/src/test/ValueBrokerTest.cpp b/src/test/ValueBrokerTest.cpp index e6bb058..5928c37 100644 --- a/src/test/ValueBrokerTest.cpp +++ b/src/test/ValueBrokerTest.cpp @@ -13,7 +13,7 @@ public: int write_count = 0; int value = 0; - ValueBroker<int> broker{ + ValueBroker<int> broker { [this](const int & target) -> void { this->write_count++; this->value = target; @@ -23,7 +23,7 @@ public: return this->value; }, }; - Proxy<int> proxy{broker}; + Proxy<int> proxy {broker}; void SetUp() override { ASSERT_EQ(read_count, 0); |