diff options
author | WBoerenkamps <wrj.boerenkamps@student.avans.nl> | 2024-12-12 07:59:47 +0100 |
---|---|---|
committer | WBoerenkamps <wrj.boerenkamps@student.avans.nl> | 2024-12-12 07:59:47 +0100 |
commit | 0f68177a0384d41a7feff36cc0e1869b6d2ac9aa (patch) | |
tree | 4904b60820858efd0ee6ece0af1f2c1fc5846ec5 /src/crepe/api | |
parent | 73f8d5c558ebc0820ede241e64a876ff1c5ccefb (diff) | |
parent | 194ee3f192c3343c3ccc28dfa97fed180503ffd4 (diff) |
Merge branch 'master' of https://github.com/lonkaars/crepe into wouter/inputSystem
Diffstat (limited to 'src/crepe/api')
28 files changed, 547 insertions, 749 deletions
diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp new file mode 100644 index 0000000..2195249 --- /dev/null +++ b/src/crepe/api/AI.cpp @@ -0,0 +1,89 @@ +#include <stdexcept> +#include <type_traits> + +#include "AI.h" +#include "types.h" + +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) { + if (radius <= 0) { + throw std::runtime_error("Radius must be greater than 0"); + } + + // The step size is determined by the radius (step size is in radians) + float step = RADIUS_TO_STEP / radius; + // Force at least MIN_STEP steps (in case of a small radius) + if (step > 2 * M_PI / MIN_STEP) { + step = 2 * M_PI / MIN_STEP; + } + // The path node distance is determined by the step size and the radius + this->path_node_distance = radius * step * PATH_NODE_DISTANCE_FACTOR; + + 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))}); + } + } 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))}); + } + } +} + +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"); + } + + float max_radius = std::max(radius_x, radius_y); + // The step size is determined by the radius (step size is in radians) + float step = RADIUS_TO_STEP / max_radius; + // Force at least MIN_STEP steps (in case of a small radius) + if (step > 2 * M_PI / MIN_STEP) { + step = 2 * M_PI / MIN_STEP; + } + // The path node distance is determined by the step size and the radius + this->path_node_distance = max_radius * step * PATH_NODE_DISTANCE_FACTOR; + + std::function<vec2(vec2, vec2)> rotate_point = [rotation](vec2 point, vec2 center) { + float s = sin(rotation); + float c = cos(rotation); + + // Translate point back to origin + point.x -= center.x; + point.y -= center.y; + + // Rotate point + float xnew = point.x * c - point.y * s; + float ynew = point.x * s + point.y * c; + + // Translate point back + point.x = xnew + center.x; + point.y = ynew + center.y; + + return point; + }; + + 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))}; + 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))}; + path.push_back(rotate_point(point, center)); + } + } +} + +} // namespace crepe diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h new file mode 100644 index 0000000..c780a91 --- /dev/null +++ b/src/crepe/api/AI.h @@ -0,0 +1,128 @@ +#pragma once + +#include "Component.h" +#include "types.h" + +namespace crepe { + +/** + * \brief The AI component is used to control the movement of an entity using AI. + * + * The AI component can be used to control the movement of an entity. The AI component can be used + * to implement different behaviors such as seeking, fleeing, arriving, and path following. + */ +class AI : public Component { +public: + //! The different types of behaviors that can be used + enum BehaviorTypeMask { + SEEK = 0x00002, + FLEE = 0x00004, + ARRIVE = 0x00008, + PATH_FOLLOW = 0x00010, + }; + +public: + /** + * \param id The id of the game object + * \param max_force The maximum force that can be applied to the entity + */ + AI(game_object_id_t id, float max_force); + + /** + * \brief Check if a behavior is on (aka activated) + * + * \param behavior The behavior to check + * \return true if the behavior is on, false otherwise + */ + bool on(BehaviorTypeMask behavior) const { return (flags & behavior); } + //! Turn on the seek behavior + void seek_on() { flags |= SEEK; } + //! Turn off the seek behavior + void seek_off() { flags &= ~SEEK; } + //! Turn on the flee behavior + void flee_on() { flags |= FLEE; } + //! Turn off the flee behavior + void flee_off() { flags &= ~FLEE; } + //! Turn on the arrive behavior + void arrive_on() { flags |= ARRIVE; } + //! Turn off the arrive behavior + void arrive_off() { flags &= ~ARRIVE; } + //! Turn on the path follow behavior + void path_follow_on() { flags |= PATH_FOLLOW; } + //! Turn off the path follow behavior + void path_follow_off() { flags &= ~PATH_FOLLOW; } + + /** + * \brief Add a path node (for the path following behavior) + * + * \note The path is not relative to the entity's position (it is an absolute path) + * + * \param node The path node to add + */ + void add_path_node(const vec2 & node) { path.push_back(node); } + /** + * \brief Make a circle path (for the path following behavior) + * + * \note The path is not relative to the entity's position (it is an absolute path) + * + * \param radius The radius of the circle (in game units) + * \param center The center of the circle (in game units) + * \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); + /** + * \brief Make an oval path (for the path following behavior) + * + * \note The path is not relative to the entity's position (it is an absolute path) + * + * \param radius_x The x radius of the oval (in game units) + * \param radius_y The y radius of the oval (in game units) + * \param center The center of the oval (in game units) + * \param start_angle The start angle of the oval (in radians) + * \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); + +public: + //! The maximum force that can be applied to the entity (higher values will make the entity adjust faster) + float max_force; + + //! The target to seek at + vec2 seek_target; + //! The target to arrive at + vec2 arrive_target; + //! The target to flee from + vec2 flee_target; + //! The distance at which the entity will start to flee from the target + float square_flee_panic_distance = 200.0f * 200.0f; + //! The deceleration rate for the arrive behavior (higher values will make the entity decelerate faster (less overshoot)) + float arrive_deceleration = 40.0f; + //! The path to follow (for the path following behavior) + std::vector<vec2> path; + //! The distance from the path node at which the entity will move to the next node (automatically set by make_circle_path()) + float path_node_distance = 400.0f; + //! Looping behavior for the path + bool path_loop = true; + +private: + //! The flags for the behaviors + int flags = 0; + //! The current path index + size_t path_index = 0; + + //! The AISystem is the only class that should access the flags and path_index variables + friend class AISystem; + + //! The minimum amount of steps for the path following behavior + static constexpr int MIN_STEP = 16; + //! The radius to step size ratio for the path following behavior + static constexpr float RADIUS_TO_STEP = 400.0f; + //! The path node distance factor for the path following behavior + static constexpr float PATH_NODE_DISTANCE_FACTOR = 0.75f; +}; + +} // namespace crepe diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp index b8a91dc..4ce4bf0 100644 --- a/src/crepe/api/Animator.cpp +++ b/src/crepe/api/Animator.cpp @@ -7,23 +7,21 @@ using namespace crepe; -Animator::Animator(game_object_id_t id, Sprite & spritesheet, unsigned int max_row, - unsigned int max_col, 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), - max_rows(max_row), - max_columns(max_col), + grid_size(grid_size), data(data) { dbg_trace(); - this->spritesheet.mask.h /= this->max_columns; - this->spritesheet.mask.w /= this->max_rows; - this->spritesheet.mask.x = this->data.row * this->spritesheet.mask.w; - this->spritesheet.mask.y = this->data.col * this->spritesheet.mask.h; + this->spritesheet.mask.w = single_frame_size.x; + this->spritesheet.mask.h = single_frame_size.y; + this->spritesheet.mask.x = 0; + this->spritesheet.mask.y = 0; - // need to do this for to get the aspect ratio for a single clipping in the spritesheet this->spritesheet.aspect_ratio - = static_cast<double>(this->spritesheet.mask.w) / this->spritesheet.mask.h; + = static_cast<float>(single_frame_size.x) / single_frame_size.y; } Animator::~Animator() { dbg_trace(); } @@ -54,6 +52,6 @@ void Animator::set_anim(int col) { void Animator::next_anim() { Animator::Data & ctx = this->data; - ctx.row = ctx.row++ % this->max_rows; + 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 7c850b8..5918800 100644 --- a/src/crepe/api/Animator.h +++ b/src/crepe/api/Animator.h @@ -75,27 +75,28 @@ public: * * \param id The unique identifier for the component, typically assigned automatically. * \param spritesheet the reference to the spritesheet - * \param max_row maximum of rows inside the given spritesheet - * \param max_col maximum of columns inside the given spritesheet + * \param single_frame_size the width and height in pixels of a single frame inside the + * spritesheet + * \param grid_size the max rows and columns inside the given spritesheet * \param data extra animation data for more control * * This constructor sets up the Animator with the given parameters, and initializes the * animation system. */ - Animator(game_object_id_t id, Sprite & spritesheet, unsigned int max_row, - unsigned int max_col, 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: - //! The maximum number of columns in the sprite sheet. - const unsigned int max_columns; - //! The maximum number of rows in the sprite sheet. - const unsigned int max_rows; Animator::Data data; private: //! A reference to the Sprite sheet containing. Sprite & spritesheet; + + //! The maximum number of rows and columns inside the spritesheet + const uvec2 grid_size; + //! Uses the spritesheet friend AnimatorSystem; }; diff --git a/src/crepe/api/AssetManager.cpp b/src/crepe/api/AssetManager.cpp deleted file mode 100644 index 3925758..0000000 --- a/src/crepe/api/AssetManager.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "util/Log.h" - -#include "AssetManager.h" - -using namespace crepe; - -AssetManager & AssetManager::get_instance() { - static AssetManager instance; - return instance; -} - -AssetManager::~AssetManager() { - dbg_trace(); - this->asset_cache.clear(); -} - -AssetManager::AssetManager() { dbg_trace(); } diff --git a/src/crepe/api/AssetManager.h b/src/crepe/api/AssetManager.h deleted file mode 100644 index 3b1cc4b..0000000 --- a/src/crepe/api/AssetManager.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include <any> -#include <memory> -#include <string> -#include <unordered_map> - -namespace crepe { - -/** - * \brief The AssetManager is responsible for storing and managing assets over multiple scenes. - * - * The AssetManager ensures that assets are loaded once and can be accessed across different - * scenes. It caches assets to avoid reloading them every time a scene is loaded. Assets are - * retained in memory until the AssetManager is destroyed, at which point the cached assets are - * cleared. - */ -class AssetManager { - -private: - //! A cache that holds all the assets, accessible by their file path, over multiple scenes. - std::unordered_map<std::string, std::any> asset_cache; - -private: - AssetManager(); - virtual ~AssetManager(); - -public: - AssetManager(const AssetManager &) = delete; - AssetManager(AssetManager &&) = delete; - AssetManager & operator=(const AssetManager &) = delete; - AssetManager & operator=(AssetManager &&) = delete; - - /** - * \brief Retrieves the singleton instance of the AssetManager. - * - * \return A reference to the single instance of the AssetManager. - */ - static AssetManager & get_instance(); - -public: - /** - * \brief Caches an asset by loading it from the given file path. - * - * \param file_path The path to the asset file to load. - * \param reload If true, the asset will be reloaded from the file, even if it is already - * cached. - * \tparam T The type of asset to cache (e.g., texture, sound, etc.). - * - * \return A shared pointer to the cached asset. - * - * This template function caches the asset at the given file path. If the asset is already - * cached and `reload` is false, the existing cached version will be returned. Otherwise, the - * asset will be reloaded and added to the cache. - */ - template <typename T> - std::shared_ptr<T> cache(const std::string & file_path, bool reload = false); -}; - -} // namespace crepe - -#include "AssetManager.hpp" diff --git a/src/crepe/api/AssetManager.hpp b/src/crepe/api/AssetManager.hpp deleted file mode 100644 index 1c0e978..0000000 --- a/src/crepe/api/AssetManager.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "AssetManager.h" - -namespace crepe { - -template <typename asset> -std::shared_ptr<asset> AssetManager::cache(const std::string & file_path, bool reload) { - auto it = asset_cache.find(file_path); - - if (!reload && it != asset_cache.end()) { - return std::any_cast<std::shared_ptr<asset>>(it->second); - } - - std::shared_ptr<asset> new_asset = std::make_shared<asset>(file_path.c_str()); - - asset_cache[file_path] = new_asset; - - return new_asset; -} - -} // namespace crepe diff --git a/src/crepe/api/AudioSource.cpp b/src/crepe/api/AudioSource.cpp new file mode 100644 index 0000000..7b05cb1 --- /dev/null +++ b/src/crepe/api/AudioSource.cpp @@ -0,0 +1,15 @@ +#include "AudioSource.h" + +using namespace crepe; +using namespace std; + +AudioSource::AudioSource(game_object_id_t id, const Asset & src) + : Component(id), + source(src) {} + +void AudioSource::play(bool looping) { + this->loop = looping; + this->oneshot_play = true; +} + +void AudioSource::stop() { this->oneshot_stop = true; } diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h new file mode 100644 index 0000000..b20e490 --- /dev/null +++ b/src/crepe/api/AudioSource.h @@ -0,0 +1,74 @@ +#pragma once + +#include "../Component.h" +#include "../facade/SoundHandle.h" +#include "../types.h" + +#include "Asset.h" +#include "GameObject.h" + +namespace crepe { + +class AudioSystem; + +//! Audio source component +class AudioSource : public Component { + //! AudioSource components are handled by AudioSystem + friend class AudioSystem; + +protected: + /** + * \param source Sound sample to load + */ + AudioSource(game_object_id_t id, const Asset & source); + //! Only ComponentManager creates components + friend class ComponentManager; + +public: + // std::unique_ptr needs to be able to destoy this component + virtual ~AudioSource() = default; + +public: + //! Start this audio source + void play(bool looping = false); + //! Stop this audio source + void stop(); + +public: + //! Play when this component becomes active + bool play_on_awake = false; + //! Repeat the current audio clip during playback + bool loop = false; + //! Normalized volume (0.0 - 1.0) + float volume = 1.0; + +private: + //! This audio source's clip + const Asset source; + + /** + * \name One-shot state variables + * + * These variables trigger function calls when set to true, and are unconditionally reset on + * every system update. + * + * \{ + */ + //! Play this sample + bool oneshot_play = false; + //! Stop this sample + bool oneshot_stop = false; + //! \} + /** + * \name State diffing variables + * \{ + */ + typeof(active) last_active = false; + typeof(volume) last_volume = volume; + typeof(loop) last_loop = loop; + //! \} + //! This source's voice handle + SoundHandle voice{}; +}; + +} // namespace crepe diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 593c4e6..fb11c8d 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -1,13 +1,11 @@ target_sources(crepe PUBLIC - # AudioSource.cpp + AudioSource.cpp BehaviorScript.cpp GameObject.cpp Rigidbody.cpp ParticleEmitter.cpp Transform.cpp Color.cpp - Texture.cpp - AssetManager.cpp Sprite.cpp Config.cpp Metadata.cpp @@ -15,19 +13,17 @@ target_sources(crepe PUBLIC Animator.cpp BoxCollider.cpp CircleCollider.cpp - IKeyListener.cpp - IMouseListener.cpp LoopManager.cpp - LoopTimer.cpp Asset.cpp EventHandler.cpp Script.cpp Button.cpp UIObject.cpp + AI.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES - # AudioSource.h + AudioSource.h BehaviorScript.h Config.h Script.h @@ -39,9 +35,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Vector2.h Vector2.hpp Color.h - Texture.h - AssetManager.h - AssetManager.hpp Scene.h Metadata.h Camera.h @@ -51,11 +44,9 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES EventHandler.h EventHandler.hpp Event.h - IKeyListener.h - IMouseListener.h LoopManager.h - LoopTimer.h Asset.h Button.h UIObject.h + AI.h ) diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 324e639..925ded8 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -15,20 +15,10 @@ namespace crepe { * modified *before* execution is handed over from the game programmer to the engine (i.e. the * main loop is started). */ -class Config final { -public: +struct Config final { //! Retrieve handle to global Config instance static Config & get_instance(); -private: - Config() = default; - ~Config() = default; - Config(const Config &) = default; - Config(Config &&) = default; - Config & operator=(const Config &) = default; - Config & operator=(Config &&) = default; - -public: //! Logging-related settings struct { /** @@ -91,6 +81,12 @@ public: //! The maximum number of pixels the mouse can move between MouseDown and MouseUp events to be considered a click. int click_tolerance = 5; } input; + + //! Audio system settings + struct { + //! Max amount of simultanious voices + unsigned int voices = 32; + } audio; }; } // namespace crepe diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp deleted file mode 100644 index 8642655..0000000 --- a/src/crepe/api/IKeyListener.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "IKeyListener.h" - -using namespace crepe; - -// Constructor with specified channel -IKeyListener::IKeyListener(event_channel_t channel) - : event_manager(EventManager::get_instance()) { - this->press_id = event_manager.subscribe<KeyPressEvent>( - [this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel); - this->release_id = event_manager.subscribe<KeyReleaseEvent>( - [this](const KeyReleaseEvent & event) { return this->on_key_released(event); }, - channel); -} - -// Destructor, unsubscribe events -IKeyListener::~IKeyListener() { - event_manager.unsubscribe(this->press_id); - event_manager.unsubscribe(this->release_id); -} diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h deleted file mode 100644 index 180a0a6..0000000 --- a/src/crepe/api/IKeyListener.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "../manager/EventManager.h" - -#include "Event.h" -#include "EventHandler.h" - -namespace crepe { - -/** - * \class IKeyListener - * \brief Interface for keyboard event handling in the application. - */ -class IKeyListener { -public: - /** - * \brief Constructs an IKeyListener with a specified channel. - * \param channel The channel ID for event handling. - */ - IKeyListener(event_channel_t channel = EventManager::CHANNEL_ALL); - virtual ~IKeyListener(); - IKeyListener(const IKeyListener &) = delete; - IKeyListener & operator=(const IKeyListener &) = delete; - IKeyListener & operator=(IKeyListener &&) = delete; - IKeyListener(IKeyListener &&) = delete; - - /** - * \brief Pure virtual function to handle key press events. - * \param event The key press event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_key_pressed(const KeyPressEvent & event) = 0; - - /** - * \brief Pure virtual function to handle key release events. - * \param event The key release event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_key_released(const KeyReleaseEvent & event) = 0; - -private: - //! Key press event id - subscription_t press_id = -1; - //! Key release event id - subscription_t release_id = -1; - //! EventManager reference - EventManager & event_manager; -}; - -} // namespace crepe diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp deleted file mode 100644 index 989aeb3..0000000 --- a/src/crepe/api/IMouseListener.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "IMouseListener.h" - -using namespace crepe; - -IMouseListener::IMouseListener(event_channel_t channel) - : event_manager(EventManager::get_instance()) { - this->click_id = event_manager.subscribe<MouseClickEvent>( - [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); }, - channel); - - this->press_id = event_manager.subscribe<MousePressEvent>( - [this](const MousePressEvent & event) { return this->on_mouse_pressed(event); }, - channel); - - this->release_id = event_manager.subscribe<MouseReleaseEvent>( - [this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); }, - channel); - - this->move_id = event_manager.subscribe<MouseMoveEvent>( - [this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }, channel); -} - -IMouseListener::~IMouseListener() { - // Unsubscribe event handlers - event_manager.unsubscribe(this->click_id); - event_manager.unsubscribe(this->press_id); - event_manager.unsubscribe(this->release_id); - event_manager.unsubscribe(this->move_id); -} diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h deleted file mode 100644 index e19897d..0000000 --- a/src/crepe/api/IMouseListener.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "../manager/EventManager.h" - -#include "Event.h" -#include "EventHandler.h" - -namespace crepe { - -/** - * \class IMouseListener - * \brief Interface for mouse event handling in the application. - */ -class IMouseListener { -public: - /** - * \brief Constructs an IMouseListener with a specified channel. - * \param channel The channel ID for event handling. - */ - IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL); - virtual ~IMouseListener(); - IMouseListener & operator=(const IMouseListener &) = delete; - IMouseListener(const IMouseListener &) = delete; - IMouseListener & operator=(const IMouseListener &&) = delete; - IMouseListener(IMouseListener &&) = delete; - - /** - * \brief Move assignment operator (deleted). - */ - IMouseListener & operator=(IMouseListener &&) = delete; - - /** - * \brief Handles a mouse click event. - * \param event The mouse click event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_clicked(const MouseClickEvent & event) = 0; - - /** - * \brief Handles a mouse press event. - * \param event The mouse press event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_pressed(const MousePressEvent & event) = 0; - - /** - * \brief Handles a mouse release event. - * \param event The mouse release event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_released(const MouseReleaseEvent & event) = 0; - - /** - * \brief Handles a mouse move event. - * \param event The mouse move event to handle. - * \return True if the event was handled, false otherwise. - */ - virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0; - -private: - //! Mouse click event id - subscription_t click_id = -1; - //! Mouse press event id - subscription_t press_id = -1; - //! Mouse release event id - subscription_t release_id = -1; - //! Mouse move event id - subscription_t move_id = -1; - //! EventManager reference - EventManager & event_manager; -}; - -} //namespace crepe diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index 88243c4..b5e5ff7 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,11 +1,16 @@ +#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 "manager/EventManager.h" +#include "../util/Log.h" #include "LoopManager.h" @@ -20,58 +25,62 @@ LoopManager::LoopManager() { 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::process_input() { this->get_system<InputSystem>().update(); } - void LoopManager::start() { this->setup(); this->loop(); } -void LoopManager::set_running(bool running) { this->game_running = running; } -void LoopManager::fixed_update() { - // TODO: retrieve EventManager from direct member after singleton refactor - EventManager & ev = this->mediator.event_manager; - ev.dispatch_events(); - this->get_system<ScriptSystem>().update(); - this->get_system<PhysicsSystem>().update(); - this->get_system<CollisionSystem>().update(); +void LoopManager::setup() { + this->game_running = true; + this->loop_timer.start(); + this->scene_manager.load_next_scene(); } void LoopManager::loop() { - LoopTimer & timer = this->loop_timer; - timer.start(); + try { + while (game_running) { + this->loop_timer.update(); - while (game_running) { - 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(); + } - while (timer.get_lag() >= timer.get_fixed_delta_time()) { - this->process_input(); - this->fixed_update(); - timer.advance_fixed_update(); + this->frame_update(); + this->loop_timer.enforce_frame_rate(); } - - this->update(); - this->render(); - - 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{}); } } -void LoopManager::setup() { - LoopTimer & timer = this->loop_timer; - this->game_running = true; - this->scene_manager.load_next_scene(); - timer.start(); - timer.set_fps(200); +// 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<AISystem>().update(); + this->get_system<PhysicsSystem>().update(); + this->get_system<CollisionSystem>().update(); + this->get_system<AudioSystem>().update(); } -void LoopManager::render() { - if (!this->game_running) return; - +// 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(); } -void LoopManager::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 index d8910a0..2915315 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -4,13 +4,16 @@ #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" -#include "LoopTimer.h" namespace crepe { - /** * \brief Main game loop manager * @@ -18,8 +21,14 @@ namespace crepe { */ class LoopManager { public: - void start(); 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 @@ -44,47 +53,20 @@ private: void loop(); /** - * \brief Function for handling input-related system calls. - * - * Processes user inputs from keyboard and mouse. - */ - void process_input(); - - /** * \brief Per-frame update. * * Updates the game state based on the elapsed time since the last frame. */ - void update(); - - /** - * \brief Late update which is called after update(). - * - * This function can be used for final adjustments before rendering. - */ - void late_update(); + 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. */ - void fixed_update(); - - /** - * \brief Set game running variable - * - * \param running running (false = game shutdown, true = game running) - */ - void set_running(bool running); - - /** - * \brief Function for executing render-related systems. - * - * Renders the current state of the game to the screen. - */ - void render(); + virtual void fixed_update(); + //! Indicates whether the game is running. bool game_running = false; private: @@ -95,18 +77,29 @@ private: ComponentManager component_manager{mediator}; //! Scene manager instance SceneManager scene_manager{mediator}; - - //! SDL context \todo no more singletons! - SDLContext & sdl_context = SDLContext::get_instance(); - //! Loop timer \todo no more singletons! - LoopTimer & loop_timer = LoopTimer::get_instance(); + //! LoopTimerManager instance + LoopTimerManager loop_timer{mediator}; + //! EventManager instance + EventManager event_manager{mediator}; + //! Resource manager instance + ResourceManager resource_manager{mediator}; + //! Save manager instance + SaveManager save_manager{mediator}; + //! SDLContext instance + SDLContext sdl_context{mediator}; private: /** + * \brief Callback function for ShutDownEvent + * + * This function sets the game_running variable to false, stopping the gameloop and therefor quitting the game. + */ + bool on_shutdown(const ShutDownEvent & e); + /** * \brief Collection of System instances * * This map holds System instances indexed by the system's class typeid. It is filled in the - * constructor of \c LoopManager using LoopManager::load_system. + * constructor of LoopManager using LoopManager::load_system. */ std::unordered_map<std::type_index, std::unique_ptr<System>> systems; /** diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp deleted file mode 100644 index 15a0e3a..0000000 --- a/src/crepe/api/LoopTimer.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include <chrono> - -#include "../facade/SDLContext.h" -#include "../util/Log.h" - -#include "LoopTimer.h" - -using namespace crepe; - -LoopTimer::LoopTimer() { dbg_trace(); } - -LoopTimer & LoopTimer::get_instance() { - static LoopTimer instance; - return instance; -} - -void LoopTimer::start() { - this->last_frame_time = std::chrono::steady_clock::now(); - this->elapsed_time = std::chrono::milliseconds(0); - this->elapsed_fixed_time = std::chrono::milliseconds(0); - this->delta_time = std::chrono::milliseconds(0); -} - -void LoopTimer::update() { - auto current_frame_time = std::chrono::steady_clock::now(); - // Convert to duration in seconds for delta time - this->delta_time = std::chrono::duration_cast<std::chrono::duration<double>>( - current_frame_time - last_frame_time); - - if (this->delta_time > this->maximum_delta_time) { - this->delta_time = this->maximum_delta_time; - } - - this->delta_time *= this->game_scale; - this->elapsed_time += this->delta_time; - this->last_frame_time = current_frame_time; -} - -double LoopTimer::get_delta_time() const { return this->delta_time.count(); } - -double LoopTimer::get_current_time() const { return this->elapsed_time.count(); } - -void LoopTimer::advance_fixed_update() { this->elapsed_fixed_time += this->fixed_delta_time; } - -double LoopTimer::get_fixed_delta_time() const { return this->fixed_delta_time.count(); } - -void LoopTimer::set_fps(int fps) { - this->fps = fps; - // target time per frame in seconds - this->frame_target_time = std::chrono::duration<double>(1.0) / fps; -} - -int LoopTimer::get_fps() const { return this->fps; } - -void LoopTimer::set_game_scale(double value) { this->game_scale = value; } - -double LoopTimer::get_game_scale() const { return this->game_scale; } -void LoopTimer::enforce_frame_rate() { - std::chrono::steady_clock::time_point current_frame_time - = std::chrono::steady_clock::now(); - std::chrono::milliseconds frame_duration - = std::chrono::duration_cast<std::chrono::milliseconds>(current_frame_time - - this->last_frame_time); - - if (frame_duration < this->frame_target_time) { - std::chrono::milliseconds delay_time - = std::chrono::duration_cast<std::chrono::milliseconds>(this->frame_target_time - - frame_duration); - if (delay_time.count() > 0) { - SDLContext::get_instance().delay(delay_time.count()); - } - } - - this->last_frame_time = current_frame_time; -} - -double LoopTimer::get_lag() const { - return (this->elapsed_time - this->elapsed_fixed_time).count(); -} diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h deleted file mode 100644 index 9393439..0000000 --- a/src/crepe/api/LoopTimer.h +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -#include <chrono> - -namespace crepe { - -class LoopTimer { -public: - /** - * \brief Get the singleton instance of LoopTimer. - * - * \return A reference to the LoopTimer instance. - */ - static LoopTimer & get_instance(); - - /** - * \brief Get the current delta time for the current frame. - * - * \return Delta time in seconds since the last frame. - */ - double get_delta_time() const; - - /** - * \brief Get the current game time. - * - * \note The current game time may vary from real-world elapsed time. It is the cumulative - * sum of each frame's delta time. - * - * \return Elapsed game time in seconds. - */ - double get_current_time() const; - - /** - * \brief Set the target frames per second (FPS). - * - * \param fps The desired frames rendered per second. - */ - void set_fps(int fps); - - /** - * \brief Get the current frames per second (FPS). - * - * \return Current FPS. - */ - int get_fps() const; - - /** - * \brief Get the current game scale. - * - * \return The current game scale, where 0 = paused, 1 = normal speed, and values > 1 speed - * up the game. - */ - double get_game_scale() const; - - /** - * \brief Set the game scale. - * - * \param game_scale The desired game scale (0 = pause, 1 = normal speed, > 1 = speed up). - */ - void set_game_scale(double game_scale); - -private: - friend class LoopManager; - - /** - * \brief Start the loop timer. - * - * Initializes the timer to begin tracking frame times. - */ - void start(); - - /** - * \brief Enforce the frame rate limit. - * - * Ensures that the game loop does not exceed the target FPS by delaying frame updates as - * necessary. - */ - void enforce_frame_rate(); - - /** - * \brief Get the fixed delta time for consistent updates. - * - * Fixed delta time is used for operations that require uniform time steps, such as physics - * calculations. - * - * \return Fixed delta time in seconds. - */ - double get_fixed_delta_time() const; - - /** - * \brief Get the accumulated lag in the game loop. - * - * Lag represents the difference between the target frame time and the actual frame time, - * useful for managing fixed update intervals. - * - * \return Accumulated lag in seconds. - */ - double get_lag() const; - - /** - * \brief Construct a new LoopTimer object. - * - * Private constructor for singleton pattern to restrict instantiation outside the class. - */ - LoopTimer(); - - /** - * \brief Update the timer to the current frame. - * - * Calculates and updates the delta time for the current frame and adds it to the cumulative - * game time. - */ - void update(); - - /** - * \brief Advance the game loop by a fixed update interval. - * - * This method progresses the game state by a consistent, fixed time step, allowing for - * stable updates independent of frame rate fluctuations. - */ - void advance_fixed_update(); - -private: - //! Current frames per second - int fps = 50; - //! Current game scale - double game_scale = 1; - //! Maximum delta time in seconds to avoid large jumps - std::chrono::duration<double> maximum_delta_time{0.25}; - //! Delta time for the current frame in seconds - std::chrono::duration<double> delta_time{0.0}; - //! Target time per frame in seconds - std::chrono::duration<double> frame_target_time = std::chrono::duration<double>(1.0) / fps; - //! Fixed delta time for fixed updates in seconds - std::chrono::duration<double> fixed_delta_time = std::chrono::duration<double>(1.0) / 50.0; - //! Total elapsed game time in seconds - std::chrono::duration<double> elapsed_time{0.0}; - //! Total elapsed time for fixed updates in seconds - std::chrono::duration<double> elapsed_fixed_time{0.0}; - //! Time of the last frame - std::chrono::steady_clock::time_point last_frame_time; -}; - -} // namespace crepe diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index 4091fd4..753a9e3 100644 --- a/src/crepe/api/Script.cpp +++ b/src/crepe/api/Script.cpp @@ -8,8 +8,7 @@ using namespace crepe; using namespace std; Script::~Script() { - Mediator & mediator = this->mediator; - EventManager & mgr = mediator.event_manager; + EventManager & mgr = this->mediator->event_manager; for (auto id : this->listeners) { mgr.unsubscribe(id); } @@ -21,7 +20,8 @@ void Script::subscribe(const EventHandler<CollisionEvent> & callback) { } void Script::set_next_scene(const string & name) { - Mediator & mediator = this->mediator; - SceneManager & mgr = mediator.scene_manager; + SceneManager & mgr = this->mediator->scene_manager; mgr.set_next_scene(name); } + +SaveManager & Script::get_save_manager() const { return this->mediator->save_manager; } diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index d99ab0e..668e5d1 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -86,6 +86,25 @@ protected: RefVector<T> get_components() const; /** + * \copydoc ComponentManager::get_components_by_id + * \see 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 + */ + 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 + */ + 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 @@ -113,6 +132,9 @@ protected: */ void set_next_scene(const std::string & name); + //! Retrieve SaveManager reference + SaveManager & get_save_manager() const; + //! \} private: diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index 45f1ff1..225a51c 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -20,10 +20,7 @@ T & Script::get_component() const { template <typename T> RefVector<T> Script::get_components() const { - Mediator & mediator = this->mediator; - ComponentManager & mgr = mediator.component_manager; - - return mgr.get_components_by_id<T>(this->game_object_id); + return this->get_components_by_id<T>(this->game_object_id); } template <typename... Args> @@ -34,8 +31,7 @@ void Script::logf(Args &&... args) { template <typename EventType> void Script::subscribe_internal(const EventHandler<EventType> & callback, event_channel_t channel) { - Mediator & mediator = this->mediator; - EventManager & mgr = mediator.event_manager; + EventManager & mgr = this->mediator->event_manager; subscription_t listener = mgr.subscribe<EventType>( [this, callback](const EventType & data) -> bool { bool & active = this->active; @@ -56,4 +52,26 @@ void Script::subscribe(const EventHandler<EventType> & callback) { this->subscribe_internal(callback, EventManager::CHANNEL_ALL); } +template <typename T> +RefVector<T> Script::get_components_by_id(game_object_id_t id) const { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + + return mgr.get_components_by_id<T>(id); +} +template <typename T> +RefVector<T> Script::get_components_by_name(const std::string & name) const { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + + return mgr.get_components_by_name<T>(name); +} +template <typename T> +RefVector<T> Script::get_components_by_tag(const std::string & tag) const { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + + return mgr.get_components_by_tag<T>(tag); +} + } // namespace crepe diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp index cc0e20a..ba684ba 100644 --- a/src/crepe/api/Sprite.cpp +++ b/src/crepe/api/Sprite.cpp @@ -1,26 +1,21 @@ #include <cmath> -#include <utility> #include "../util/Log.h" +#include "api/Asset.h" #include "Component.h" #include "Sprite.h" -#include "Texture.h" #include "types.h" using namespace std; using namespace crepe; -Sprite::Sprite(game_object_id_t id, Texture & texture, const Sprite::Data & data) +Sprite::Sprite(game_object_id_t id, const Asset & texture, const Sprite::Data & data) : Component(id), - texture(std::move(texture)), + source(texture), data(data) { dbg_trace(); - - this->mask.w = this->texture.get_size().x; - this->mask.h = this->texture.get_size().y; - this->aspect_ratio = static_cast<double>(this->mask.w) / this->mask.h; } Sprite::~Sprite() { dbg_trace(); } diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h index dbf41e4..a2409c2 100644 --- a/src/crepe/api/Sprite.h +++ b/src/crepe/api/Sprite.h @@ -1,9 +1,9 @@ #pragma once #include "../Component.h" +#include "api/Asset.h" #include "Color.h" -#include "Texture.h" #include "types.h" namespace crepe { @@ -74,24 +74,15 @@ public: * \param texture asset of the image * \param ctx all the sprite data */ - Sprite(game_object_id_t id, Texture & texture, const Data & data); + Sprite(game_object_id_t id, const Asset & texture, const Data & data); ~Sprite(); //! Texture used for the sprite - const Texture texture; + const Asset source; Data data; private: - /** - * \brief ratio of the img - * - * - This will multiply one of \c size variable if it is 0. - * - Will be adjusted if \c Animator component is added to an GameObject that is why this - * value cannot be const. - */ - float aspect_ratio; - //! Reads the mask of sprite friend class SDLContext; @@ -101,6 +92,14 @@ private: //! Reads the all the variables plus the mask friend class AnimatorSystem; + /** + * \aspect_ratio the ratio of the sprite image + * + * - this value will only be set by the \c Animator component for the ratio of the Animation + * - if \c Animator component is not added it will not use this ratio (because 0) and will use aspect_ratio of the Asset. + */ + float aspect_ratio = 0; + struct Rect { int w = 0; int h = 0; diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp deleted file mode 100644 index 2b56271..0000000 --- a/src/crepe/api/Texture.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../facade/SDLContext.h" -#include "../util/Log.h" - -#include "Asset.h" -#include "Texture.h" -#include "types.h" - -using namespace crepe; -using namespace std; - -Texture::Texture(const Asset & src) { - dbg_trace(); - this->load(src); -} - -Texture::~Texture() { - dbg_trace(); - this->texture.reset(); -} - -Texture::Texture(Texture && other) noexcept : texture(std::move(other.texture)) {} - -Texture & Texture::operator=(Texture && other) noexcept { - if (this != &other) { - texture = std::move(other.texture); - } - return *this; -} - -void Texture::load(const Asset & res) { - SDLContext & ctx = SDLContext::get_instance(); - this->texture = ctx.texture_from_path(res.get_path()); -} - -ivec2 Texture::get_size() const { - if (this->texture == nullptr) return {}; - return SDLContext::get_instance().get_size(*this); -} diff --git a/src/crepe/api/Texture.h b/src/crepe/api/Texture.h deleted file mode 100644 index 1817910..0000000 --- a/src/crepe/api/Texture.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -// FIXME: this header can't be included because this is an API header, and SDL2 development -// headers won't be bundled with crepe. Why is this facade in the API namespace? - -#include <SDL2/SDL_render.h> -#include <functional> -#include <memory> - -#include "Asset.h" -#include "types.h" - -namespace crepe { - -class SDLContext; -class Animator; - -/** - * \class Texture - * \brief Manages texture loading and properties. - * - * The Texture class is responsible for loading an image from a source and providing access to - * its dimensions. Textures can be used for rendering. - */ -class Texture { - -public: - /** - * \brief Constructs a Texture from an Asset resource. - * \param src Asset with texture data to load. - */ - Texture(const Asset & src); - - /** - * \brief Destroys the Texture instance, freeing associated resources. - */ - ~Texture(); - // FIXME: this constructor shouldn't be necessary because this class doesn't manage memory - - Texture(Texture && other) noexcept; - Texture & operator=(Texture && other) noexcept; - Texture(const Texture &) = delete; - Texture & operator=(const Texture &) = delete; - - /** - * \brief Gets the width and height of the texture. - * \return Width and height of the texture in pixels. - */ - ivec2 get_size() const; - -private: - /** - * \brief Loads the texture from an Asset resource. - * \param res Unique pointer to an Asset resource to load the texture from. - */ - void load(const Asset & res); - -private: - //! The texture of the class from the library - std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> texture; - - //! Grants SDLContext access to private members. - friend class SDLContext; - - //! Grants Animator access to private members. - friend class Animator; -}; - -} // namespace crepe diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h index c278c87..bf9d124 100644 --- a/src/crepe/api/Vector2.h +++ b/src/crepe/api/Vector2.h @@ -66,6 +66,30 @@ struct Vector2 { //! Checks if this vector is not equal to another vector. bool operator!=(const Vector2<T> & other) const; + + //! Truncates the vector to a maximum length. + void truncate(T max); + + //! Normalizes the vector (resulting in vector with a length of 1). + void normalize(); + + //! Returns the length of the vector. + T length() const; + + //! Returns the squared length of the vector. + T length_squared() const; + + //! Returns the dot product (inwendig product) of this vector and another vector. + T dot(const Vector2<T> & other) const; + + //! Returns the distance between this vector and another vector. + T distance(const Vector2<T> & other) const; + + //! Returns the squared distance between this vector and another vector. + T distance_squared(const Vector2<T> & other) const; + + //! Returns the perpendicular vector to this vector. + Vector2 perpendicular() const; }; } // namespace crepe diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp index cad15f8..ff53cb0 100644 --- a/src/crepe/api/Vector2.hpp +++ b/src/crepe/api/Vector2.hpp @@ -1,5 +1,7 @@ #pragma once +#include <cmath> + #include "Vector2.h" namespace crepe { @@ -115,4 +117,50 @@ bool Vector2<T>::operator!=(const Vector2<T> & other) const { return !(*this == other); } +template <class T> +void Vector2<T>::truncate(T max) { + if (length() > max) { + normalize(); + *this *= max; + } +} + +template <class T> +void Vector2<T>::normalize() { + T len = length(); + if (len > 0) { + *this /= len; + } +} + +template <class T> +T Vector2<T>::length() const { + return std::sqrt(x * x + y * y); +} + +template <class T> +T Vector2<T>::length_squared() const { + return x * x + y * y; +} + +template <class T> +T Vector2<T>::dot(const Vector2<T> & other) const { + return x * other.x + y * other.y; +} + +template <class T> +T Vector2<T>::distance(const Vector2<T> & other) const { + return (*this - other).length(); +} + +template <class T> +T Vector2<T>::distance_squared(const Vector2<T> & other) const { + return (*this - other).length_squared(); +} + +template <class T> +Vector2<T> Vector2<T>::perpendicular() const { + return {-y, x}; +} + } // namespace crepe |