diff options
Diffstat (limited to 'src/crepe/api')
65 files changed, 3065 insertions, 486 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 new file mode 100644 index 0000000..4ce4bf0 --- /dev/null +++ b/src/crepe/api/Animator.cpp @@ -0,0 +1,57 @@ + +#include "util/Log.h" + +#include "Animator.h" +#include "Component.h" +#include "Sprite.h" + +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) + : Component(id), + spritesheet(spritesheet), + grid_size(grid_size), + data(data) { + dbg_trace(); + + 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; + + this->spritesheet.aspect_ratio + = static_cast<float>(single_frame_size.x) / single_frame_size.y; +} + +Animator::~Animator() { dbg_trace(); } + +void Animator::loop() { this->data.looping = true; } + +void Animator::play() { this->active = true; } + +void Animator::pause() { this->active = false; } + +void Animator::stop() { + this->active = false; + this->data.col = 0; + this->data.row = 0; +} +void Animator::set_fps(int fps) { this->data.fps = fps; } + +void Animator::set_cycle_range(int start, int end) { + this->data.cycle_start = start, this->data.cycle_end = end; +} + +void Animator::set_anim(int col) { + Animator::Data & ctx = this->data; + this->spritesheet.mask.x = ctx.row = 0; + ctx.col = col; + this->spritesheet.mask.y = ctx.col * this->spritesheet.mask.h; +} + +void Animator::next_anim() { + Animator::Data & ctx = this->data; + 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 new file mode 100644 index 0000000..5918800 --- /dev/null +++ b/src/crepe/api/Animator.h @@ -0,0 +1,104 @@ +#pragma once + +#include "../types.h" + +#include "Component.h" +#include "Sprite.h" + +namespace crepe { + +class AnimatorSystem; +class SDLContext; + +/** + * \brief The Animator component is used to animate sprites by managing the movement and frame + * changes within a sprite sheet. + * + * This component allows for controlling sprite animation through rows and columns of a sprite + * sheet. It can be used to play animations, loop them, or stop them. + */ +class Animator : public Component { +public: + struct Data { + //! frames per second for animation + unsigned int fps = 1; + //! The current col being animated. + unsigned int col = 0; + //! The current row being animated. + unsigned int row = 0; + //! should the animation loop + bool looping = false; + //! starting frame for cycling + unsigned int cycle_start = 0; + //! end frame for cycling (-1 = use last frame) + int cycle_end = -1; + }; + +public: + //! Animator will repeat the animation + void loop(); + //! starts the animation + void play(); + //! pauses the animation + void pause(); + /** + * \brief stops the animation + * + * sets the active on false and resets all the current rows and columns + */ + void stop(); + /** + * \brief set frames per second + * + * \param fps frames per second + */ + void set_fps(int fps); + /** + * \brief set the range in the row + * + * \param start of row animation + * \param end of row animation + */ + void set_cycle_range(int start, int end); + /** + * \brief select which column to animate from + * + * \param col animation column + */ + void set_anim(int col); + //! will go to the next animaiton of current row + void next_anim(); + +public: + /** + * \brief Constructs an Animator object that will control animations for a sprite sheet. + * + * \param id The unique identifier for the component, typically assigned automatically. + * \param spritesheet the reference to the 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, const ivec2 & single_frame_size, + const uvec2 & grid_size, const Animator::Data & data); + ~Animator(); // dbg_trace + +public: + 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; +}; + +} // namespace crepe diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp new file mode 100644 index 0000000..e148367 --- /dev/null +++ b/src/crepe/api/Asset.cpp @@ -0,0 +1,54 @@ +#include <filesystem> +#include <stdexcept> +#include <whereami.h> + +#include "api/Config.h" + +#include "Asset.h" + +using namespace crepe; +using namespace std; + +Asset::Asset(const string & src) : src(find_asset(src)) {} +Asset::Asset(const char * src) : src(find_asset(src)) {} + +const string & Asset::get_path() const noexcept { return this->src; } + +string Asset::find_asset(const string & src) const { + auto & cfg = Config::get_instance(); + string & root_pattern = cfg.asset.root_pattern; + + // if root_pattern is empty, find_asset must return all paths as-is + if (root_pattern.empty()) return src; + + // absolute paths do not need to be resolved, only canonicalized + filesystem::path path = src; + if (path.is_absolute()) return filesystem::canonical(path); + + // find directory matching root_pattern + filesystem::path root = this->whereami(); + while (1) { + if (filesystem::exists(root / root_pattern)) break; + if (!root.has_parent_path()) + throw runtime_error(format("Asset: Cannot find root pattern ({})", root_pattern)); + root = root.parent_path(); + } + + // join path to root (base directory) and canonicalize + return filesystem::canonical(root / path); +} + +string Asset::whereami() const noexcept { + string path; + size_t path_length = wai_getExecutablePath(NULL, 0, NULL); + path.resize(path_length + 1); // wai writes null byte + wai_getExecutablePath(path.data(), path_length, NULL); + path.resize(path_length); + return path; +} + +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()); +}; diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h new file mode 100644 index 0000000..bfd0ac7 --- /dev/null +++ b/src/crepe/api/Asset.h @@ -0,0 +1,84 @@ +#pragma once + +#include <string> + +namespace crepe { + +/** + * \brief Asset location helper + * + * This class is used to locate game asset files, and should *always* be used + * instead of reading file paths directly. + */ +class Asset { +public: + /** + * \param src Unique identifier to asset + */ + Asset(const std::string & src); + /** + * \param src Unique identifier to asset + */ + Asset(const char * src); + +public: + /** + * \brief Get the path to this asset + * \return path to this asset + */ + const std::string & get_path() const noexcept; + + /** + * \brief Comparison operator + * \param other Possibly different instance of \c Asset to test equality against + * \return True if \c this and \c other are equal + */ + bool operator==(const Asset & other) const noexcept; + +private: + //! path to asset + const std::string src; + +private: + /** + * \brief Locate asset path, or throw exception if it cannot be found + * + * This function resolves asset locations relative to crepe::Config::root_pattern if it is + * set and \p src is a relative path. If \p src is an absolute path, it is canonicalized. + * This function only returns if the file can be found. + * + * \param src Arbitrary path to resource file + * + * \returns \p src if crepe::Config::root_pattern is empty + * \returns Canonical path to \p src + * + * \throws std::runtime_error if root_pattern cannot be found + * \throws std::filesystem::filesystem_error if the resolved path does not exist + * \throws std::filesystem::filesystem_error if the path cannot be canonicalized + */ + std::string find_asset(const std::string & src) const; + /** + * \returns The path to the current executable + */ + std::string whereami() const noexcept; +}; + +} // namespace crepe + +namespace std { + +//! Hash helper struct +template <> +struct hash<const crepe::Asset> { + /** + * \brief Hash operator for crepe::Asset + * + * This function hashes a crepe::Asset instance, allowing it to be used as a key in an \c + * std::unordered_map. + * + * \returns Hash value + */ + size_t operator()(const crepe::Asset & asset) const noexcept; +}; + +} // namespace std diff --git a/src/crepe/api/AssetManager.cpp b/src/crepe/api/AssetManager.cpp deleted file mode 100644 index b891760..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 fefbed9..0000000 --- a/src/crepe/api/AssetManager.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include <any> -#include <memory> -#include <string> -#include <unordered_map> - -namespace crepe { - -class AssetManager { - -private: - 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; - - static AssetManager & get_instance(); - -public: - template <typename asset> - std::shared_ptr<asset> 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 977b4e1..0000000 --- a/src/crepe/api/AssetManager.hpp +++ /dev/null @@ -1,24 +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 index 63fd0d7..7b05cb1 100644 --- a/src/crepe/api/AudioSource.cpp +++ b/src/crepe/api/AudioSource.cpp @@ -1,23 +1,15 @@ -#include <memory> - -#include "../facade/Sound.h" - #include "AudioSource.h" using namespace crepe; +using namespace std; -AudioSource::AudioSource(std::unique_ptr<Asset> audio_clip) { - this->sound = std::make_unique<crepe::Sound>(std::move(audio_clip)); -} - -void AudioSource::play() { return this->play(false); } +AudioSource::AudioSource(game_object_id_t id, const Asset & src) + : Component(id), + source(src) {} void AudioSource::play(bool looping) { - this->sound->set_looping(looping); - this->sound->play(); + this->loop = looping; + this->oneshot_play = true; } -void AudioSource::stop() { - this->sound->pause(); - this->sound->rewind(); -} +void AudioSource::stop() { this->oneshot_stop = true; } diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 42add50..b20e490 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -1,39 +1,74 @@ #pragma once -#include <memory> - -#include "../Asset.h" #include "../Component.h" +#include "../facade/SoundHandle.h" +#include "../types.h" + +#include "Asset.h" +#include "GameObject.h" namespace crepe { -class Sound; +class AudioSystem; //! Audio source component -class AudioSource : 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: - AudioSource(std::unique_ptr<Asset> audio_clip); + // std::unique_ptr needs to be able to destoy this component virtual ~AudioSource() = default; public: - //! Start or resume this audio source - void play(); - void play(bool looping); + //! Start this audio source + void play(bool looping = false); //! Stop this audio source void stop(); public: - //! Sample file location - std::unique_ptr<Asset> audio_clip; - //! TODO: ????? - bool play_on_awake; + //! Play when this component becomes active + bool play_on_awake = false; //! Repeat the current audio clip during playback - bool loop; + bool loop = false; //! Normalized volume (0.0 - 1.0) - float volume; + float volume = 1.0; private: - std::unique_ptr<Sound> sound; + //! 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/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp index e69de29..d22afdf 100644 --- a/src/crepe/api/BehaviorScript.cpp +++ b/src/crepe/api/BehaviorScript.cpp @@ -0,0 +1,15 @@ +#include "BehaviorScript.h" +#include "Component.h" +#include "GameObject.h" + +using namespace crepe; + +BehaviorScript::BehaviorScript(game_object_id_t id, Mediator & mediator) + : Component(id), + mediator(mediator) {} + +template <> +BehaviorScript & GameObject::add_component<BehaviorScript>() { + ComponentManager & mgr = this->component_manager; + return mgr.add_component<BehaviorScript>(this->id, mgr.mediator); +} diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h index 21638f4..3909b96 100644 --- a/src/crepe/api/BehaviorScript.h +++ b/src/crepe/api/BehaviorScript.h @@ -4,32 +4,71 @@ #include "../Component.h" -namespace crepe { -class ScriptSystem; -class ComponentManager; -} // namespace crepe +#include "GameObject.h" namespace crepe { +class ScriptSystem; +class ComponentManager; class Script; +/** + * \brief Script component + * + * This class acts as a (component) wrapper around an instance of (a class derivatived from) \c + * Script. \c BehaviorScript is the only ECS component that stores member function + * implementations as data. + */ class BehaviorScript : public Component { protected: - friend class crepe::ComponentManager; - using Component::Component; + /** + * \param id Parent \c GameObject id + * \param mediator Mediator reference + * + * \note Calls to this constructor (should) always pass through \c GameObject::add_component, + * which has an exception for this specific component type. This was done so the user does + * not have to pass references used within \c Script to each \c BehaviorScript instance. + */ + BehaviorScript(game_object_id_t id, Mediator & mediator); + //! Only ComponentManager is allowed to instantiate BehaviorScript + friend class ComponentManager; public: - virtual ~BehaviorScript() = default; - -public: - template <class T> - BehaviorScript & set_script(); + /** + * \brief Set the concrete script of this component + * + * \tparam T Concrete script type (derived from \c crepe::Script) + * \tparam Args Arguments for concrete script constructor + * + * \param args Arguments for concrete script constructor (forwarded using perfect forwarding) + * + * \returns Reference to BehaviorScript component (`*this`) + */ + template <class T, typename... Args> + BehaviorScript & set_script(Args &&... args); protected: - friend class crepe::ScriptSystem; + //! Script instance std::unique_ptr<Script> script = nullptr; + //! ScriptSystem needs direct access to the script instance + friend class ScriptSystem; + +protected: + //! Reference mediator + Mediator & mediator; }; +/** + * \brief Add a BehaviorScript component to this game object + * + * The \c BehaviorScript class is the only exception to the ECS harmony, and requires a + * reference to the component manager passed to its constructor in order to function normally. + * This is because the \c BehaviorScript (and \c Script) classes are the only component-related + * classes that store implemented member functions as data. + */ +template <> +BehaviorScript & GameObject::add_component<BehaviorScript>(); + } // namespace crepe #include "BehaviorScript.hpp" diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp index 4751607..b9bb1e2 100644 --- a/src/crepe/api/BehaviorScript.hpp +++ b/src/crepe/api/BehaviorScript.hpp @@ -2,20 +2,23 @@ #include <type_traits> -#include "../util/log.h" +#include "../util/Log.h" #include "BehaviorScript.h" #include "Script.h" namespace crepe { -template <class T> -BehaviorScript & BehaviorScript::set_script() { - static_assert(std::is_base_of<Script, T>::value); +template <class T, typename... Args> +BehaviorScript & BehaviorScript::set_script(Args &&... args) { dbg_trace(); - Script * s = new T(); - s->parent = this; - this->script = std::unique_ptr<Script>(s); + static_assert(std::is_base_of<Script, T>::value); + this->script = std::unique_ptr<Script>(new T(std::forward<Args>(args)...)); + + this->script->game_object_id = this->game_object_id; + this->script->active = this->active; + this->script->mediator = this->mediator; + return *this; } diff --git a/src/crepe/api/BoxCollider.cpp b/src/crepe/api/BoxCollider.cpp new file mode 100644 index 0000000..a893d41 --- /dev/null +++ b/src/crepe/api/BoxCollider.cpp @@ -0,0 +1,10 @@ +#include "BoxCollider.h" + +#include "../Collider.h" + +using namespace crepe; + +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 new file mode 100644 index 0000000..d643e7f --- /dev/null +++ b/src/crepe/api/BoxCollider.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../Collider.h" +#include "Vector2.h" +#include "types.h" + +namespace crepe { + +/** + * \brief A class representing a box-shaped collider. + * + * This class is used for collision detection with other colliders (e.g., CircleCollider). + */ +class BoxCollider : public Collider { +public: + 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; +}; + +} // namespace crepe diff --git a/src/crepe/api/Button.cpp b/src/crepe/api/Button.cpp new file mode 100644 index 0000000..305922c --- /dev/null +++ b/src/crepe/api/Button.cpp @@ -0,0 +1,10 @@ +#include "Button.h" + +namespace crepe { + +Button::Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset, + const std::function<void()> & on_click) + : UIObject(id, dimensions, offset), + on_click(on_click) {} + +} // namespace crepe diff --git a/src/crepe/api/Button.h b/src/crepe/api/Button.h new file mode 100644 index 0000000..08f5dec --- /dev/null +++ b/src/crepe/api/Button.h @@ -0,0 +1,57 @@ +#pragma once + +#include <functional> + +#include "UIObject.h" + +namespace crepe { + +//! Represents a clickable UI button, derived from the UiObject class. +class Button : public UIObject { +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 on_click callback function that will be invoked when the button is clicked. + */ + Button(game_object_id_t id, const vec2 & dimensions, const vec2 & offset, + const std::function<void()> & on_click); + + // TODO: create separate toggle button class + /** + * \brief The callback function to be executed when the button is clicked. + * + * This function is invoked whenever the button is clicked. It can be set to any + * function that matches the signature `void()`. + */ + std::function<void()> on_click = nullptr; + + /** + * \brief Callback function to be executed when the mouse enters the button's boundaries. + * + * This function is triggered when the mouse cursor moves over the button, allowing + * custom actions like visual effects, highlighting, or sound effects. + */ + std::function<void()> on_mouse_enter = nullptr; + + /** + * \brief Callback function to be executed when the mouse exits the button's boundaries. + * + * This function is triggered when the mouse cursor moves out of the button's area, + * allowing custom actions like resetting visual effects or playing exit-related effects. + */ + std::function<void()> on_mouse_exit = nullptr; + +private: + //! friend relation for is_pressed and hover variables + friend class InputSystem; + //! Indicates whether the mouse is currently hovering over the button + bool hover = false; + +public: +}; + +} // namespace crepe diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 0bb1263..e8d6f92 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -1,20 +1,31 @@ target_sources(crepe PUBLIC - # AudioSource.cpp + AudioSource.cpp BehaviorScript.cpp - Script.cpp GameObject.cpp Rigidbody.cpp - Force.cpp ParticleEmitter.cpp Transform.cpp Color.cpp - Texture.cpp - AssetManager.cpp Sprite.cpp + Config.cpp + Metadata.cpp + Camera.cpp + Animator.cpp + BoxCollider.cpp + CircleCollider.cpp + LoopManager.cpp + Asset.cpp + EventHandler.cpp + Script.cpp + Button.cpp + UIObject.cpp + AI.cpp + Text.cpp + Scene.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES - # AudioSource.h + AudioSource.h BehaviorScript.h Config.h Script.h @@ -23,9 +34,23 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES GameObject.hpp Rigidbody.h Sprite.h - Point.h + Vector2.h + Vector2.hpp Color.h - Texture.h - AssetManager.h - AssetManager.hpp + Scene.h + Scene.hpp + Metadata.h + Camera.h + Animator.h + BoxCollider.h + CircleCollider.h + EventHandler.h + EventHandler.hpp + Event.h + LoopManager.h + Asset.h + Button.h + UIObject.h + AI.h + Text.h ) diff --git a/src/crepe/api/Camera.cpp b/src/crepe/api/Camera.cpp new file mode 100644 index 0000000..179dc18 --- /dev/null +++ b/src/crepe/api/Camera.cpp @@ -0,0 +1,18 @@ +#include "util/Log.h" + +#include "Camera.h" +#include "Component.h" +#include "types.h" + +using namespace crepe; + +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), + data(data) { + dbg_trace(); +} + +Camera::~Camera() { dbg_trace(); } diff --git a/src/crepe/api/Camera.h b/src/crepe/api/Camera.h new file mode 100644 index 0000000..54d9a73 --- /dev/null +++ b/src/crepe/api/Camera.h @@ -0,0 +1,67 @@ +#pragma once + +#include "Color.h" +#include "Component.h" +#include "types.h" + +namespace crepe { + +/** + * \class Camera + * \brief Represents a camera component for rendering in the game. + * + * The Camera class defines the view parameters, including background color, aspect ratio, + * position, and zoom level. It controls what part of the game world is visible on the screen. + */ +class Camera : public Component { +public: + struct Data { + /** + * \bg_color background color of the game + * + * This will make the background the same color as the given value. + */ + const Color bg_color = Color::BLACK; + + /** + * \zoom Zooming level of the game + * + * zoom = 1 --> no zoom. + * zoom < 1 --> zoom out + * zoom > 1 --> zoom in + */ + double zoom = 1; + + //! offset postion from the game object transform component + vec2 postion_offset; + }; + +public: + /** + * \brief Constructs a Camera with the specified ID and background color. + * \param id Unique identifier for the camera component. + * \param screen is the actual screen size in pixels + * \param viewport_size is the view of the world in game units + * \param data the camera component data + */ + Camera(game_object_id_t id, const ivec2 & screen, const vec2 & viewport_size, + const Camera::Data & data); + ~Camera(); // dbg_trace only + +public: + Camera::Data data; + + //! screen the display size in pixels ( output resolution ) + const ivec2 screen; + + //! viewport is the area of the world visible through the camera (in world units) + const vec2 viewport_size; + +public: + /** + * \brief Gets the maximum number of camera instances allowed. + * \return Maximum instance count as an integer. + */ + virtual int get_instances_max() const { return 1; } +}; +} // namespace crepe diff --git a/src/crepe/api/CircleCollider.cpp b/src/crepe/api/CircleCollider.cpp new file mode 100644 index 0000000..90ab5e7 --- /dev/null +++ b/src/crepe/api/CircleCollider.cpp @@ -0,0 +1,8 @@ +#include "CircleCollider.h" + +using namespace crepe; + +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 931b012..22da836 100644 --- a/src/crepe/api/CircleCollider.h +++ b/src/crepe/api/CircleCollider.h @@ -1,13 +1,23 @@ #pragma once + +#include "Vector2.h" + #include "../Collider.h" namespace crepe { +/** + * \brief A class representing a circle-shaped collider. + * + * This class is used for collision detection with other colliders (e.g., BoxCollider). + */ class CircleCollider : public Collider { public: - CircleCollider(uint32_t game_object_id, int radius) - : Collider(game_object_id), radius(radius) {} - int radius; + CircleCollider(game_object_id_t game_object_id, float radius, + const vec2 & offset = {0, 0}); + + //! Radius of the circle collider. + float radius; }; } // namespace crepe diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp index fc6313d..29bd77a 100644 --- a/src/crepe/api/Color.cpp +++ b/src/crepe/api/Color.cpp @@ -2,33 +2,11 @@ using namespace crepe; -Color Color::white = Color(255, 255, 255, 0); -Color Color::red = Color(255, 0, 0, 0); -Color Color::green = Color(0, 255, 0, 0); -Color Color::blue = Color(0, 0, 255, 0); -Color Color::black = Color(0, 0, 0, 0); -Color Color::cyan = Color(0, 255, 255, 0); -Color Color::yellow = Color(255, 255, 0, 0); -Color Color::magenta = Color(255, 0, 255, 0); - -// FIXME: do we really need double precision for color values? -Color::Color(double red, double green, double blue, double alpha) { - this->a = alpha; - this->r = red; - this->g = green; - this->b = blue; -}; - -const Color & Color::get_white() { return Color::white; }; - -const Color & Color::get_red() { return Color::red; }; -const Color & Color::get_green() { return Color::green; }; -const Color & Color::get_blue() { return Color::blue; }; - -const Color & Color::get_black() { return Color::black; }; - -const Color & Color::get_cyan() { return Color::cyan; }; - -const Color & Color::get_yellow() { return Color::yellow; }; - -const Color & Color::get_magenta() { return Color::magenta; }; +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}; diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h index 6b54888..84edb5c 100644 --- a/src/crepe/api/Color.h +++ b/src/crepe/api/Color.h @@ -1,37 +1,23 @@ #pragma once -namespace crepe { - -class Color { +#include <cstdint> - // FIXME: can't these colors be defined as a `static constexpr const Color` - // instead? - -public: - Color(double red, double green, double blue, double alpha); - static const Color & get_white(); - static const Color & get_red(); - static const Color & get_green(); - static const Color & get_blue(); - static const Color & get_cyan(); - static const Color & get_magenta(); - static const Color & get_yellow(); - static const Color & get_black(); +namespace crepe { -private: - double r; - double g; - double b; - double a; +struct Color { + uint8_t r = 0x00; + uint8_t g = 0x00; + uint8_t b = 0x00; + uint8_t a = 0xff; - static Color white; - static Color red; - static Color green; - static Color blue; - static Color cyan; - static Color magenta; - static Color yellow; - static Color black; + static const Color WHITE; + static const Color RED; + static const Color GREEN; + static const Color BLUE; + static const Color CYAN; + static const Color MAGENTA; + static const Color YELLOW; + static const Color BLACK; }; } // namespace crepe diff --git a/src/crepe/api/Config.cpp b/src/crepe/api/Config.cpp new file mode 100644 index 0000000..0100bcc --- /dev/null +++ b/src/crepe/api/Config.cpp @@ -0,0 +1,8 @@ +#include "Config.h" + +using namespace crepe; + +Config & Config::get_instance() { + static Config instance; + return instance; +} diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 22104a7..6b9e3ca 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -1,33 +1,32 @@ #pragma once -#include "../util/log.h" +#include <string> -namespace crepe { +#include "../util/Log.h" -class Config { -private: - Config() = default; +#include "types.h" -public: - ~Config() = default; +namespace crepe { -public: +/** + * \brief Global configuration interface + * + * This class stores engine default settings. Properties on this class are only supposed to be + * modified *before* execution is handed over from the game programmer to the engine (i.e. the + * main loop is started). + */ +struct Config final { //! Retrieve handle to global Config instance - static Config & get_instance() { - static Config instance; - return instance; - } + static Config & get_instance(); -public: //! Logging-related settings struct { /** * \brief Log level * - * Only messages with equal or higher priority than this value will be - * logged. + * Only messages with equal or higher priority than this value will be logged. */ - LogLevel level = LogLevel::INFO; + Log::Level level = Log::Level::INFO; /** * \brief Colored log output * @@ -35,6 +34,70 @@ public: */ bool color = true; } log; + + //! Save manager + struct { + /** + * \brief Save file location + * + * This location is used by the constructor of SaveManager, and should be set before save + * manager functionality is attempted to be used. + */ + std::string location = "save.crepe.db"; + } savemgr; + + //! physics-related settings + struct { + /** + * \brief gravity value of physics system + * + * Gravity value of game. + */ + float gravity = 10; + } physics; + + //! default window settings + struct { + //! default screen size in pixels + ivec2 default_size = {1280, 720}; + std::string window_title = "Jetpack joyride clone"; + } window_settings; + + //! Asset loading options + struct { + /** + * \brief Pattern to match for Asset base directory + * + * All non-absolute paths resolved using \c Asset will be made relative to + * the first parent directory relative to the calling executable where + * appending this pattern results in a path that exists. If this string is + * empty, path resolution is disabled, and Asset will return all paths + * as-is. + */ + std::string root_pattern = ".crepe-root"; + } asset; + //! Default font options + struct { + /** + * \brief Default font size + * + * Using the SDL_ttf library the font size needs to be set when loading the font. + * This config option is the font size at which all fonts will be loaded initially. + * + */ + unsigned int size = 16; + } font; + //! Configuration for click tolerance. + struct { + //! 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/Event.h b/src/crepe/api/Event.h new file mode 100644 index 0000000..73bf461 --- /dev/null +++ b/src/crepe/api/Event.h @@ -0,0 +1,162 @@ +#pragma once +// TODO discussing the location of these events + +#include <string> + +#include "api/KeyCodes.h" +#include "types.h" + +namespace crepe { + +/** + * \brief Base class for all event types in the system. + */ +class Event {}; + +/** + * \brief Event triggered when a key is pressed. + */ +class KeyPressEvent : public Event { +public: + //! false if first time press, true if key is repeated + bool repeat = false; + + //! The key that was pressed. + Keycode key = Keycode::NONE; +}; + +/** + * \brief Event triggered when a key is released. + */ +class KeyReleaseEvent : public Event { +public: + //! The key that was released. + Keycode key = Keycode::NONE; +}; + +/** + * \brief Event triggered when a mouse button is pressed. + */ +class MousePressEvent : public Event { +public: + //! mouse position in world coordinates (game units). + vec2 mouse_pos = {0, 0}; + + //! The mouse button that was pressed. + MouseButton button = MouseButton::NONE; +}; + +/** + * \brief Event triggered when a mouse button is clicked (press and release). + */ +class MouseClickEvent : public Event { +public: + //! mouse position in world coordinates (game units). + vec2 mouse_pos = {0, 0}; + + //! The mouse button that was clicked. + MouseButton button = MouseButton::NONE; +}; + +/** + * \brief Event triggered when a mouse button is released. + */ +class MouseReleaseEvent : public Event { +public: + //! mouse position in world coordinates (game units). + vec2 mouse_pos = {0, 0}; + + //! The mouse button that was released. + MouseButton button = MouseButton::NONE; +}; + +/** + * \brief Event triggered when the mouse is moved. + */ +class MouseMoveEvent : public Event { +public: + //! mouse position in world coordinates (game units). + vec2 mouse_pos = {0, 0}; + //! The change in mouse position relative to the last position (in pixels). + ivec2 mouse_delta = {0, 0}; +}; + +/** + * \brief Event triggered when the mouse is moved. + */ +class MouseScrollEvent : public Event { +public: + //! mouse position in world coordinates (game units) when the scroll happened. + vec2 mouse_pos = {0, 0}; + //! scroll direction (-1 = down, 1 = up) + int scroll_direction = 0; + //! scroll amount in y axis (from and away from the person). + float scroll_delta = 0; +}; + +/** + * \brief Event triggered when text is submitted, e.g., from a text input. + */ +class TextSubmitEvent : public Event { +public: + //! The submitted text. + std::string text = ""; +}; + +/** + * \brief Event triggered to indicate the application is shutting down. + */ +class 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 {}; + +/** + * \brief Event triggered to indicate the window is resized. + */ +class WindowResizeEvent : public Event { +public: + //! new window dimensions + ivec2 dimensions = {0, 0}; +}; + +/** + * \brief Event triggered to indicate the window is moved. + */ +class WindowMoveEvent : public Event { +public: + //! The change in position relative to the last position (in pixels). + ivec2 delta_move = {0, 0}; +}; + +/** + * \brief Event triggered to indicate the window is minimized. + */ +class WindowMinimizeEvent : public Event {}; + +/** + * \brief Event triggered to indicate the window is maximized + */ +class WindowMaximizeEvent : public Event {}; + +/** + * \brief Event triggered to indicate the window gained focus + * + * This event is triggered when the window receives focus, meaning it becomes the active window + * for user interaction. + */ +class WindowFocusGainEvent : public Event {}; + +/** + * \brief Event triggered to indicate the window lost focus + * + * This event is triggered when the window loses focus, meaning it is no longer the active window + * for user interaction. + */ +class WindowFocusLostEvent : public Event {}; + +} // namespace crepe diff --git a/src/crepe/api/EventHandler.cpp b/src/crepe/api/EventHandler.cpp new file mode 100644 index 0000000..4dc232f --- /dev/null +++ b/src/crepe/api/EventHandler.cpp @@ -0,0 +1,5 @@ +#include "EventHandler.h" + +using namespace crepe; + +bool IEventHandlerWrapper::exec(const Event & e) { return this->call(e); } diff --git a/src/crepe/api/EventHandler.h b/src/crepe/api/EventHandler.h new file mode 100644 index 0000000..7bb501b --- /dev/null +++ b/src/crepe/api/EventHandler.h @@ -0,0 +1,96 @@ +#pragma once + +#include <functional> +#include <string> + +#include "Event.h" + +namespace crepe { +/** + * \brief A type alias for an event handler function. + * + * The EventHandler is a std::function that takes an EventType reference and returns a boolean value + * indicating whether the event is handled. + * + * \tparam EventType The type of event this handler will handle. + * + * Returning \c false from an event handler results in the event being propogated to other listeners for the same event type, while returning \c true stops propogation altogether. + */ +template <typename EventType> +using EventHandler = std::function<bool(const EventType & e)>; + +/** + * \class IEventHandlerWrapper + * \brief An abstract base class for event handler wrappers. + * + * This class provides the interface for handling events. Derived classes must implement the + * `call()` method to process events + */ +class IEventHandlerWrapper { +public: + /** + * \brief Virtual destructor for IEventHandlerWrapper. + */ + virtual ~IEventHandlerWrapper() = default; + + /** + * \brief Executes the handler with the given event. + * + * This method calls the `call()` method of the derived class, passing the event to the handler. + * + * \param e The event to be processed. + * \return A boolean value indicating whether the event is handled. + */ + bool exec(const Event & e); + +private: + /** + * \brief The method responsible for handling the event. + * + * This method is implemented by derived classes to process the event. + * + * \param e The event to be processed. + * \return A boolean value indicating whether the event is handled. + */ + virtual bool call(const Event & e) = 0; +}; + +/** + * \class EventHandlerWrapper + * \brief A wrapper for event handler functions. + * + * This class wraps an event handler function of a specific event type. It implements the + * `call()` and `get_type()` methods to allow the handler to be executed and its type to be + * queried. + * + * \tparam EventType The type of event this handler will handle. + */ +template <typename EventType> +class EventHandlerWrapper : public IEventHandlerWrapper { +public: + /** + * \brief Constructs an EventHandlerWrapper with a given handler. + * + * The constructor takes an event handler function and stores it in the wrapper. + * + * \param handler The event handler function. + */ + explicit EventHandlerWrapper(const EventHandler<EventType> & handler); + +private: + /** + * \brief Calls the stored event handler with the event. + * + * This method casts the event to the appropriate type and calls the handler. + * + * \param e The event to be handled. + * \return A boolean value indicating whether the event is handled. + */ + bool call(const Event & e) override; + //! The event handler function. + EventHandler<EventType> handler; +}; + +} // namespace crepe + +#include "EventHandler.hpp" diff --git a/src/crepe/api/EventHandler.hpp b/src/crepe/api/EventHandler.hpp new file mode 100644 index 0000000..050e57e --- /dev/null +++ b/src/crepe/api/EventHandler.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <typeindex> + +#include "EventHandler.h" + +namespace crepe { + +// Implementation of EventHandlerWrapper constructor +template <typename EventType> +EventHandlerWrapper<EventType>::EventHandlerWrapper(const EventHandler<EventType> & handler) + : handler(handler) {} + +// Implementation of EventHandlerWrapper::call +template <typename EventType> +bool EventHandlerWrapper<EventType>::call(const Event & e) { + return this->handler(static_cast<const EventType &>(e)); +} + +} //namespace crepe diff --git a/src/crepe/api/Force.cpp b/src/crepe/api/Force.cpp deleted file mode 100644 index 3c33ad3..0000000 --- a/src/crepe/api/Force.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include <cmath> - -#include "Force.h" - -namespace crepe { - -Force::Force(uint32_t game_object_id, uint32_t magnitude, uint32_t direction) - : Component(game_object_id) { - // TODO: A standard angle unit should be established for the entire engine - // and assumed to be the default everywhere. Only conversion functions should - // explicitly contain the unit (i.e. `deg_to_rad()` & `rad_to_deg()`) - - // Convert direction from degrees to radians - float radian_direction = static_cast<float>(direction) * (M_PI / 180.0f); - force_x = static_cast<int32_t>( - std::round(magnitude * std::cos(radian_direction))); - force_y = static_cast<int32_t>( - std::round(magnitude * std::sin(radian_direction))); -} - -} // namespace crepe diff --git a/src/crepe/api/Force.h b/src/crepe/api/Force.h deleted file mode 100644 index c08a8b9..0000000 --- a/src/crepe/api/Force.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include <cstdint> - -#include "../Component.h" - -namespace crepe { - -class Force : public Component { -public: - Force(uint32_t game_object_id, uint32_t magnitude, uint32_t direction); - - int32_t force_x; - int32_t force_y; -}; - -} // namespace crepe diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp index 445a60d..9ef4682 100644 --- a/src/crepe/api/GameObject.cpp +++ b/src/crepe/api/GameObject.cpp @@ -1,7 +1,38 @@ +#include "api/Transform.h" + +#include "BehaviorScript.h" #include "GameObject.h" +#include "Metadata.h" using namespace crepe; using namespace std; -GameObject::GameObject(uint32_t id, string name, string tag, int layer) - : id(id), name(name), tag(tag), active(true), layer(layer) {} +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) + : 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); +} + +void GameObject::set_parent(const GameObject & parent) { + ComponentManager & mgr = this->component_manager; + + // Set parent on own Metadata component + RefVector<Metadata> this_metadata = mgr.get_components_by_id<Metadata>(this->id); + this_metadata.at(0).get().parent = parent.id; + + // Add own id to children list of parent's Metadata component + RefVector<Metadata> parent_metadata = mgr.get_components_by_id<Metadata>(parent.id); + parent_metadata.at(0).get().children.push_back(this->id); +} + +void GameObject::set_persistent(bool persistent) { + ComponentManager & mgr = this->component_manager; + + mgr.set_persistent(this->id, persistent); +} diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h index b5d6399..ff80f49 100644 --- a/src/crepe/api/GameObject.h +++ b/src/crepe/api/GameObject.h @@ -1,22 +1,79 @@ #pragma once -#include <cstdint> #include <string> +#include "types.h" + namespace crepe { +class ComponentManager; + +/** + * \brief Represents a GameObject + * + * This class represents a GameObject. The GameObject class is only used as an interface for + * the game programmer. The actual implementation is done in the ComponentManager. + */ class GameObject { -public: - GameObject(uint32_t id, std::string name, std::string tag, int layer); +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 id The id of the GameObject + * \param name The name of the GameObject + * \param tag The tag of the GameObject + * \param position The position of the GameObject + * \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); + //! ComponentManager instances GameObject + friend class ComponentManager; +public: + /** + * \brief Set the parent of this GameObject + * + * This method sets the parent of this GameObject. It sets the parent in the Metadata + * component of this GameObject and adds this GameObject to the children list of the parent + * GameObject. + * + * \param parent The parent GameObject + */ + void set_parent(const GameObject & parent); + /** + * \brief Add a component to the GameObject + * + * This method adds a component to the GameObject. It forwards the arguments to the + * ComponentManager. + * + * \tparam T The type of the component + * \tparam Args The types of the arguments + * \param args The arguments to create the component + * \return The created component + */ template <typename T, typename... Args> T & add_component(Args &&... args); + /** + * \brief Components will not be deleted if this method is called + * + * This method sets the persistent flag of the GameObject to true. If the persistent + * flag is set to true, the GameObject will not be deleted when the scene is changed. + * + * \param persistent The persistent flag + */ + void set_persistent(bool persistent = true); + +public: + //! The id of the GameObject + const game_object_id_t id; - uint32_t id; - std::string name; - std::string tag; - bool active; - int layer; +protected: + ComponentManager & component_manager; }; } // namespace crepe diff --git a/src/crepe/api/GameObject.hpp b/src/crepe/api/GameObject.hpp index 77cf40e..a6b45b0 100644 --- a/src/crepe/api/GameObject.hpp +++ b/src/crepe/api/GameObject.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../ComponentManager.h" +#include "../manager/ComponentManager.h" #include "GameObject.h" @@ -8,7 +8,7 @@ namespace crepe { template <typename T, typename... Args> T & GameObject::add_component(Args &&... args) { - auto & mgr = ComponentManager::get_instance(); + ComponentManager & mgr = this->component_manager; return mgr.add_component<T>(this->id, std::forward<Args>(args)...); } diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h new file mode 100644 index 0000000..1b9573a --- /dev/null +++ b/src/crepe/api/KeyCodes.h @@ -0,0 +1,160 @@ +#pragma once + +#include <unordered_map> + +namespace crepe { + +//! Enumeration for mouse button inputs, including standard and extended buttons. +enum class MouseButton { + NONE = 0, //!< No mouse button input. + LEFT_MOUSE = 1, //!< Left mouse button. + RIGHT_MOUSE = 2, //!< Right mouse button. + MIDDLE_MOUSE = 3, //!< Middle mouse button (scroll wheel press). + X1_MOUSE = 4, //!< First extended mouse button. + X2_MOUSE = 5, //!< Second extended mouse button. + SCROLL_UP = 6, //!< Scroll wheel upward movement. + SCROLL_DOWN = 7, //!< Scroll wheel downward movement. +}; + +//! Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys. +enum class Keycode { + NONE = 0, //!< No key input. + SPACE = 32, //!< Spacebar. + APOSTROPHE = 39, //!< Apostrophe ('). + COMMA = 44, //!< Comma (,). + MINUS = 45, //!< Minus (-). + PERIOD = 46, //!< Period (.). + SLASH = 47, //!< Slash (/). + D0 = 48, //!< Digit 0. + D1 = 49, //!< Digit 1. + D2 = 50, //!< Digit 2. + D3 = 51, //!< Digit 3. + D4 = 52, //!< Digit 4. + D5 = 53, //!< Digit 5. + D6 = 54, //!< Digit 6. + D7 = 55, //!< Digit 7. + D8 = 56, //!< Digit 8. + D9 = 57, //!< Digit 9. + SEMICOLON = 59, //!< Semicolon (;). + EQUAL = 61, //!< Equal sign (=). + A = 65, //!< Key 'A'. + B = 66, //!< Key 'B'. + C = 67, //!< Key 'C'. + D = 68, //!< Key 'D'. + E = 69, //!< Key 'E'. + F = 70, //!< Key 'F'. + G = 71, //!< Key 'G'. + H = 72, //!< Key 'H'. + I = 73, //!< Key 'I'. + J = 74, //!< Key 'J'. + K = 75, //!< Key 'K'. + L = 76, //!< Key 'L'. + M = 77, //!< Key 'M'. + N = 78, //!< Key 'N'. + O = 79, //!< Key 'O'. + P = 80, //!< Key 'P'. + Q = 81, //!< Key 'Q'. + R = 82, //!< Key 'R'. + S = 83, //!< Key 'S'. + T = 84, //!< Key 'T'. + U = 85, //!< Key 'U'. + V = 86, //!< Key 'V'. + W = 87, //!< Key 'W'. + X = 88, //!< Key 'X'. + Y = 89, //!< Key 'Y'. + Z = 90, //!< Key 'Z'. + LEFT_BRACKET = 91, //!< Left bracket ([). + BACKSLASH = 92, //!< Backslash (\). + RIGHT_BRACKET = 93, //!< Right bracket (]). + GRAVE_ACCENT = 96, //!< Grave accent (`). + WORLD1 = 161, //!< Non-US key #1. + WORLD2 = 162, //!< Non-US key #2. + ESCAPE = 256, //!< Escape key. + ENTER = 257, //!< Enter key. + TAB = 258, //!< Tab key. + BACKSPACE = 259, //!< Backspace key. + INSERT = 260, //!< Insert key. + DELETE = 261, //!< Delete key. + RIGHT = 262, //!< Right arrow key. + LEFT = 263, //!< Left arrow key. + DOWN = 264, //!< Down arrow key. + UP = 265, //!< Up arrow key. + PAGE_UP = 266, //!< Page Up key. + PAGE_DOWN = 267, //!< Page Down key. + HOME = 268, //!< Home key. + END = 269, //!< End key. + CAPS_LOCK = 280, //!< Caps Lock key. + SCROLL_LOCK = 281, //!< Scroll Lock key. + NUM_LOCK = 282, //!< Num Lock key. + PRINT_SCREEN = 283, //!< Print Screen key. + PAUSE = 284, //!< Pause key. + /** + * \name Function keys (F1-F25). + * \{ + */ + F1 = 290, + F2 = 291, + F3 = 292, + F4 = 293, + F5 = 294, + F6 = 295, + F7 = 296, + F8 = 297, + F9 = 298, + F10 = 299, + F11 = 300, + F12 = 301, + F13 = 302, + F14 = 303, + F15 = 304, + F16 = 305, + F17 = 306, + F18 = 307, + F19 = 308, + F20 = 309, + F21 = 310, + F22 = 311, + F23 = 312, + F24 = 313, + F25 = 314, + /// \} + /** + * \name Keypad digits and operators. + * \{ + */ + KP0 = 320, + KP1 = 321, + KP2 = 322, + KP3 = 323, + KP4 = 324, + KP5 = 325, + KP6 = 326, + KP7 = 327, + KP8 = 328, + KP9 = 329, + KP_DECIMAL = 330, + KP_DIVIDE = 331, + KP_MULTIPLY = 332, + KP_SUBTRACT = 333, + KP_ADD = 334, + KP_ENTER = 335, + KP_EQUAL = 336, + /// \} + /** + * \name Modifier keys. + * \{ + */ + LEFT_SHIFT = 340, + LEFT_CONTROL = 341, + LEFT_ALT = 342, + LEFT_SUPER = 343, + RIGHT_SHIFT = 344, + RIGHT_CONTROL = 345, + RIGHT_ALT = 346, + RIGHT_SUPER = 347, + /// \} + MENU = 348, //!< Menu key. +}; +//! Typedef for keyboard state. +typedef std::unordered_map<Keycode, bool> keyboard_state_t; +} // namespace crepe diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp new file mode 100644 index 0000000..7a78019 --- /dev/null +++ b/src/crepe/api/LoopManager.cpp @@ -0,0 +1,87 @@ +#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 new file mode 100644 index 0000000..1d23cbf --- /dev/null +++ b/src/crepe/api/LoopManager.h @@ -0,0 +1,122 @@ +#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; + + //! SDLContext instance + SDLContext sdl_context{mediator}; + //! Component manager instance + ComponentManager component_manager{mediator}; + //! Scene manager instance + SceneManager scene_manager{mediator}; + //! LoopTimerManager instance + LoopTimerManager loop_timer{mediator}; + //! EventManager instance + EventManager event_manager{mediator}; + //! Resource manager instance + ResourceManager resource_manager{mediator}; + //! Save manager instance + SaveManager save_manager{mediator}; + +private: + /** + * \brief Callback function for ShutDownEvent + * + * This function sets the game_running variable to false, stopping the gameloop and therefor quitting the game. + */ + bool on_shutdown(const ShutDownEvent & e); + /** + * \brief Collection of System instances + * + * This map holds System instances indexed by the system's class typeid. It is filled in the + * constructor of LoopManager using LoopManager::load_system. + */ + std::unordered_map<std::type_index, std::unique_ptr<System>> systems; + /** + * \brief Initialize a system + * \tparam T System type (must be derivative of \c System) + */ + template <class T> + void load_system(); + /** + * \brief Retrieve a reference to ECS system + * \tparam T System type + * \returns Reference to system instance + * \throws std::runtime_error if the System is not initialized + */ + template <class T> + T & get_system(); +}; + +} // namespace crepe + +#include "LoopManager.hpp" diff --git a/src/crepe/api/LoopManager.hpp b/src/crepe/api/LoopManager.hpp new file mode 100644 index 0000000..266758a --- /dev/null +++ b/src/crepe/api/LoopManager.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include <cassert> +#include <format> +#include <memory> + +#include "../system/System.h" + +#include "LoopManager.h" + +namespace crepe { + +template <class T> +void LoopManager::add_scene() { + this->scene_manager.add_scene<T>(); +} + +template <class T> +T & LoopManager::get_system() { + using namespace std; + static_assert(is_base_of<System, T>::value, + "get_system must recieve a derivative class of System"); + + const type_info & type = typeid(T); + if (!this->systems.contains(type)) + throw runtime_error(format("LoopManager: {} is not initialized", type.name())); + + System * system = this->systems.at(type).get(); + T * concrete_system = dynamic_cast<T *>(system); + assert(concrete_system != nullptr); + + return *concrete_system; +} + +template <class T> +void LoopManager::load_system() { + using namespace std; + static_assert(is_base_of<System, T>::value, + "load_system must recieve a derivative class of System"); + + const type_info & type = typeid(T); + if (this->systems.contains(type)) + throw runtime_error(format("LoopManager: {} is already initialized", type.name())); + System * system = new T(this->mediator); + this->systems[type] = unique_ptr<System>(system); +} + +} // namespace crepe diff --git a/src/crepe/api/Metadata.cpp b/src/crepe/api/Metadata.cpp new file mode 100644 index 0000000..d421de5 --- /dev/null +++ b/src/crepe/api/Metadata.cpp @@ -0,0 +1,9 @@ +#include "Metadata.h" + +using namespace crepe; +using namespace std; + +Metadata::Metadata(game_object_id_t id, const string & name, const string & tag) + : Component(id), + name(name), + tag(tag) {} diff --git a/src/crepe/api/Metadata.h b/src/crepe/api/Metadata.h new file mode 100644 index 0000000..f404703 --- /dev/null +++ b/src/crepe/api/Metadata.h @@ -0,0 +1,42 @@ +#pragma once + +#include <string> +#include <vector> + +#include "../Component.h" + +namespace crepe { + +/** + * \brief Metadata component + * + * This class represents the Metadata component. It stores the name, tag, parent and children + * of a GameObject. + */ +class Metadata : public Component { +public: + /** + * \param game_object_id The id of the GameObject this component belongs to + * \param name The name of the GameObject + * \param tag The tag of the GameObject + */ + Metadata(game_object_id_t id, const std::string & name, const std::string & tag); + /** + * \brief Get the maximum number of instances for this component + * + * \return The maximum number of instances for this component + */ + virtual int get_instances_max() const { return 1; } + +public: + //! The name of the GameObject + const std::string name; + //! The tag of the GameObject + const std::string tag; + //! The id of the parent GameObject (-1 if no parent) + game_object_id_t parent = -1; + //! The ids of the children GameObjects + std::vector<game_object_id_t> children; +}; + +} // namespace crepe diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp index 0b3a9ee..4f54bbd 100644 --- a/src/crepe/api/ParticleEmitter.cpp +++ b/src/crepe/api/ParticleEmitter.cpp @@ -1,37 +1,14 @@ -#include <ctime> -#include <iostream> - -#include "Particle.h" #include "ParticleEmitter.h" +#include "api/Sprite.h" using namespace crepe; -ParticleEmitter::ParticleEmitter(uint32_t game_object_id, - uint32_t max_particles, uint32_t emission_rate, - uint32_t speed, uint32_t speed_offset, - uint32_t angle, uint32_t angleOffset, - float begin_lifespan, float end_lifespan) - : Component(game_object_id), max_particles(max_particles), - emission_rate(emission_rate), speed(speed), speed_offset(speed_offset), - position{0, 0}, begin_lifespan(begin_lifespan), - end_lifespan(end_lifespan) { - std::srand( - static_cast<uint32_t>(std::time(nullptr))); // initialize random seed - std::cout << "Create emitter" << std::endl; - // FIXME: Why do these expressions start with `360 +`, only to be `% 360`'d - // right after? This does not make any sense to me. - min_angle = (360 + angle - (angleOffset % 360)) % 360; - max_angle = (360 + angle + (angleOffset % 360)) % 360; - position.x = 400; // FIXME: what are these magic values? - position.y = 400; - for (size_t i = 0; i < max_particles; i++) { +ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite, + const Data & data) + : Component(game_object_id), + sprite(sprite), + data(data) { + for (size_t i = 0; i < this->data.max_particles; i++) { this->particles.emplace_back(); } } - -ParticleEmitter::~ParticleEmitter() { - std::vector<Particle>::iterator it = this->particles.begin(); - while (it != this->particles.end()) { - it = this->particles.erase(it); - } -} diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h index 2e2e95b..8ac2e72 100644 --- a/src/crepe/api/ParticleEmitter.h +++ b/src/crepe/api/ParticleEmitter.h @@ -1,40 +1,96 @@ #pragma once -#include <cstdint> +#include <cmath> #include <vector> +#include "system/ParticleSystem.h" +#include "system/RenderSystem.h" + #include "Component.h" #include "Particle.h" +#include "types.h" namespace crepe { +class Sprite; + +/** + * \brief Data holder for particle emission parameters. + * + * The ParticleEmitter class stores configuration data for particle properties, defining the + * characteristics and boundaries of particle emissions. + */ class ParticleEmitter : public Component { public: - ParticleEmitter(uint32_t game_object_id, uint32_t max_particles, - uint32_t emission_rate, uint32_t speed, - uint32_t speed_offset, uint32_t angle, uint32_t angleOffset, - float begin_lifespan, float end_lifespan); - ~ParticleEmitter(); - - //! position of the emitter - Position position; - //! maximum number of particles - uint32_t max_particles; - //! rate of particle emission - uint32_t emission_rate; - //! base speed of the particles - uint32_t speed; - //! offset for random speed variation - uint32_t speed_offset; - //! min angle of particle emission - uint32_t min_angle; - //! max angle of particle emission - uint32_t max_angle; - //! begin Lifespan of particle (only visual) - float begin_lifespan; - //! begin Lifespan of particle - float end_lifespan; + /** + * \brief Defines the boundary within which particles are constrained. + * + * This structure specifies the boundary's size and offset, as well as the behavior of + * particles upon reaching the boundary limits. + */ + struct Boundary { + //! boundary width (midpoint is emitter location) + float width = INFINITY; + //! boundary height (midpoint is emitter location) + float height = INFINITY; + //! boundary offset from particle emitter location + vec2 offset; + //! reset on exit or stop velocity and set max postion + bool reset_on_exit = false; + }; + + //! sprite reference of displayed sprite + const Sprite & sprite; + + /** + * \brief Holds parameters that control particle emission. + * + * Contains settings for the emitter’s position, particle speed, angle, lifespan, boundary, + * and the sprite used for rendering particles. + */ + struct Data { + //! offset of the emitter relative to transform + vec2 offset; + //! maximum number of particles + const unsigned int max_particles = 256; + //! rate of particle emission per second + float emission_rate = 50; + //! min speed of the particles + float min_speed = 100; + //! min speed of the particles + float max_speed = 100; + //! min angle of particle emission + float min_angle = 0; + //! max angle of particle emission + float max_angle = 0; + //! begin Lifespan of particle in seconds (only visual) + float begin_lifespan = 0.0; + //! end Lifespan of particle in seconds + float end_lifespan = 10.0; + //! force over time (physics) + vec2 force_over_time; + //! particle boundary + Boundary boundary; + }; + +public: + /** + * \param game_object_id Identifier for the game object using this emitter. + * \param data Configuration data defining particle properties. + */ + ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite, const Data & data); + +public: + //! Configuration data for particle emission settings. + Data data; +private: + //! Only ParticleSystem can move and read particles + friend ParticleSystem; + //! Only RenderSystem can read particles + friend RenderSystem; + //! Saves time left over from last update event. + float spawn_accumulator = 0; //! collection of particles std::vector<Particle> particles; }; diff --git a/src/crepe/api/Point.h b/src/crepe/api/Point.h deleted file mode 100644 index 575d624..0000000 --- a/src/crepe/api/Point.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -namespace crepe { - -class Point { -public: - double x; - double y; -}; - -} // namespace crepe diff --git a/src/crepe/api/Rigidbody.cpp b/src/crepe/api/Rigidbody.cpp index 0a6262a..8213afb 100644 --- a/src/crepe/api/Rigidbody.cpp +++ b/src/crepe/api/Rigidbody.cpp @@ -2,7 +2,12 @@ using namespace crepe; -Rigidbody::Rigidbody(uint32_t game_object_id, int mass, int gravity_scale, - BodyType bodyType) - : Component(game_object_id), mass(mass), gravity_scale(gravity_scale), - body_type(bodyType) {} +crepe::Rigidbody::Rigidbody(game_object_id_t id, const Data & data) + : Component(id), + data(data) {} + +void crepe::Rigidbody::add_force_linear(const vec2 & force) { + this->data.linear_velocity += force; +} + +void crepe::Rigidbody::add_force_angular(float force) { this->data.angular_velocity += force; } diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h index 518ed94..6900295 100644 --- a/src/crepe/api/Rigidbody.h +++ b/src/crepe/api/Rigidbody.h @@ -1,30 +1,178 @@ #pragma once -#include <cstdint> +#include <cmath> +#include <set> #include "../Component.h" -namespace crepe { +#include "types.h" -// FIXME: can't this enum be defined inside the class declaration of Rigidbody? -enum class BodyType { - //! Does not move (e.g. walls, ground ...) - STATIC, - //! Moves and responds to forces (e.g. player, physics objects ...) - DYNAMIC, - //! Moves but does not respond to forces (e.g. moving platforms ...) - KINEMATIC, -}; +namespace crepe { +/** + * \brief Rigidbody class + * + * This class is used by the physics sytem and collision system. It configures how to system + * interact with the gameobject for movement and collisions. + */ class Rigidbody : public Component { public: - Rigidbody(uint32_t game_object_id, int mass, int gravity_scale, - BodyType body_type); - int32_t velocity_x; - int32_t velocity_y; - int mass; - int gravity_scale; - BodyType body_type; + /** + * \brief BodyType enum + * + * This enum provides three bodytypes the physics sytem and collision system use. + */ + enum class BodyType { + //! Does not move (e.g. walls, ground ...) + STATIC, + //! Moves and responds to forces (e.g. player, physics objects ...) + DYNAMIC, + //! Moves but does not respond to forces (e.g. moving platforms ...) + KINEMATIC, + }; + /** + * \brief PhysicsConstraints to constrain movement + * + * This struct configures the movement constraint for this object. If a constraint is enabled + * the systems will not move the object. + */ + struct PhysicsConstraints { + //! Prevent movement along X axis + bool x = false; + //! Prevent movement along Y axis + bool y = false; + //! Prevent rotation + bool rotation = false; + }; + +public: + /** + * \brief struct for Rigidbody data + * + * This struct holds the data for the Rigidbody. + */ + struct Data { + //! objects mass + float mass = 1; + /** + * \brief Gravity scale factor. + * + * The `gravity_scale` controls how much gravity affects the object. It is a multiplier applied to the default + * gravity force, allowing for fine-grained control over how the object responds to gravity. + * + */ + float gravity_scale = 0; + + //! Defines the type of the physics body, which determines how the physics system interacts with the object. + BodyType body_type = BodyType::DYNAMIC; + + /** + * \name Linear (positional) motion + * + * These variables define the linear motion (movement along the position) of an object. + * The linear velocity is applied to the object's position in each update of the PhysicsSystem. + * The motion is affected by the object's linear velocity, its maximum velocity, and a coefficient + * that can scale the velocity over time. + * + * \{ + */ + //! Linear velocity of the object (speed and direction). + vec2 linear_velocity; + //! Maximum linear velocity of the object. This limits the object's speed. + float max_linear_velocity = INFINITY; + //! Linear velocity coefficient. This scales the object's velocity for adjustment or damping. + vec2 linear_velocity_coefficient = {1, 1}; + //! \} + + /** + * \name Angular (rotational) motion + * + * These variables define the angular motion (rotation) of an object. + * The angular velocity determines how quickly the object rotates, while the maximum angular velocity + * sets a limit on the rotation speed. The angular velocity coefficient applies damping or scaling + * to the angular velocity, which can be used to simulate friction or other effects that slow down rotation. + * + * \{ + */ + //! Angular velocity of the object, representing the rate of rotation (in degrees). + float angular_velocity = 0; + //! Maximum angular velocity of the object. This limits the maximum rate of rotation. + float max_angular_velocity = INFINITY; + //! Angular velocity coefficient. This scales the object's angular velocity, typically used for damping. + float angular_velocity_coefficient = 1; + //! \} + + /** + * \brief Movement constraints for an object. + * + * The `PhysicsConstraints` struct defines the constraints that restrict an object's movement + * in certain directions or prevent rotation. These constraints effect only the physics system + * to prevent the object from moving or rotating in specified ways. + * + */ + PhysicsConstraints constraints; + + /** + * \brief Elasticity factor of the material (bounce factor). + * + * The `elasticity_coefficient` controls how much of the object's velocity is retained after a collision. + * It represents the material's ability to bounce or recover velocity upon impact. The coefficient is a value + * above 0.0. + * + */ + float elastisity_coefficient = 0.0; + + /** + * \brief Offset of all colliders relative to the object's transform position. + * + * The `offset` defines a positional shift applied to all colliders associated with the object, relative to the object's + * transform position. This allows for the colliders to be placed at a different position than the object's actual + * position, without modifying the object's transform itself. + * + */ + vec2 offset; + + /** + * \brief Defines the collision layers of a GameObject. + * + * The `collision_layers` specifies the layers that the GameObject will collide with. + * Each element represents a layer ID, and the GameObject will only detect + * collisions with other GameObjects that belong to these layers. + */ + std::set<int> collision_layers = {0}; + }; + +public: + /** + * \param game_object_id id of the gameobject the rigibody is added to. + * \param data struct to configure the rigidbody. + */ + Rigidbody(game_object_id_t id, const Data & data); + //! struct to hold data of rigidbody + Data data; + +public: + /** + * \brief add a linear force to the Rigidbody. + * + * \param force Vector2 that is added to the linear force. + */ + void add_force_linear(const vec2 & force); + /** + * \brief add a angular force to the Rigidbody. + * + * \param force Vector2 that is added to the angular force. + */ + void add_force_angular(float force); + +protected: + /** + * Ensures there is at most one Rigidbody component per entity. + * \return Always returns 1, indicating this constraint. + */ + virtual int get_instances_max() const { return 1; } + //! ComponentManager instantiates all components + friend class ComponentManager; }; } // namespace crepe diff --git a/src/crepe/api/Scene.cpp b/src/crepe/api/Scene.cpp new file mode 100644 index 0000000..ad729d2 --- /dev/null +++ b/src/crepe/api/Scene.cpp @@ -0,0 +1,15 @@ +#include "Scene.h" + +using namespace crepe; + +SaveManager & Scene::get_save_manager() const { return mediator->save_manager; } + +GameObject Scene::new_object(const std::string & name, const std::string & tag, + const vec2 & position, double rotation, double scale) { + // Forward the call to ComponentManager's new_object method + return mediator->component_manager->new_object(name, tag, position, rotation, scale); +} + +void Scene::set_persistent(const Asset & asset, bool persistent) { + mediator->resource_manager->set_persistent(asset, persistent); +} diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h new file mode 100644 index 0000000..dcca9d4 --- /dev/null +++ b/src/crepe/api/Scene.h @@ -0,0 +1,94 @@ +#pragma once + +#include <string> + +#include "../manager/ComponentManager.h" +#include "../manager/Mediator.h" +#include "../manager/ResourceManager.h" +#include "../util/Log.h" +#include "../util/OptionalRef.h" + +#include "GameObject.h" + +namespace crepe { + +class SceneManager; +class ComponentManager; +class Asset; + +/** + * \brief Represents a Scene + * + * This class represents a Scene. The Scene class is only used as an interface for the game + * programmer. + */ +class Scene { +protected: + // NOTE: This must be the only constructor on Scene, see "Late references" below + Scene() = default; + //! SceneManager instances Scene + friend class SceneManager; + +public: + virtual ~Scene() = default; + +public: + //! Load the scene + virtual void load_scene() = 0; + /** + * \brief Get the scene's name + * \return The scene's name + */ + virtual std::string get_name() const = 0; + + // TODO: Late references should ALWAYS be private! This is currently kept as-is so unit tests + // keep passing, but this reference should not be directly accessible by the user!!! + +private: + /** + * \name Late references + * + * These references are set by SceneManager immediately after calling the constructor of Scene. + * + * \note Scene must have a constructor without arguments so the game programmer doesn't need to + * manually add `using Scene::Scene` to their concrete scene class, if they want to add a + * constructor with arguments (e.g. for passing references to their own concrete Scene classes). + * + * \{ + */ + //! Mediator reference + OptionalRef<Mediator> mediator; + //! \} + +protected: + /** + * \brief Retrieve the reference to the SaveManager instance + * + * \returns A reference to the SaveManager instance held by the Mediator. + */ + SaveManager & get_save_manager() const; + + //! \copydoc ComponentManager::new_object + GameObject new_object(const std::string & name, const std::string & tag = "", + const vec2 & position = {0, 0}, double rotation = 0, + double scale = 1); + + //! \copydoc ResourceManager::set_persistent + void set_persistent(const Asset & asset, bool persistent); + /** + * \name Logging functions + * \see Log + * \{ + */ + //! \copydoc Log::logf + template <class... Args> + void logf(const Log::Level & level, std::format_string<Args...> fmt, Args &&... args); + //! \copydoc Log::logf + template <class... Args> + void logf(std::format_string<Args...> fmt, Args &&... args); + //! \} +}; + +} // namespace crepe + +#include "Scene.hpp" diff --git a/src/crepe/api/Scene.hpp b/src/crepe/api/Scene.hpp new file mode 100644 index 0000000..14635df --- /dev/null +++ b/src/crepe/api/Scene.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "../util/Log.h" + +#include "Scene.h" + +namespace crepe { + +template <class... Args> +void Scene::logf(const Log::Level & level, std::format_string<Args...> fmt, Args &&... args) { + Log::logf(level, fmt, std::forward<Args>(args)...); +} + +template <class... Args> +void Scene::logf(std::format_string<Args...> fmt, Args &&... args) { + Log::logf(fmt, std::forward<Args>(args)...); +} + +} // namespace crepe diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index 390cec7..583c04f 100644 --- a/src/crepe/api/Script.cpp +++ b/src/crepe/api/Script.cpp @@ -1,3 +1,40 @@ +#include <string> + +#include "../facade/SDLContext.h" +#include "../manager/SceneManager.h" #include "Script.h" using namespace crepe; +using namespace std; + +Script::~Script() { + EventManager & mgr = this->mediator->event_manager; + for (auto id : this->listeners) { + mgr.unsubscribe(id); + } +} + +template <> +void Script::subscribe(const EventHandler<CollisionEvent> & 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); +} + +SaveManager & Script::get_save_manager() const { return this->mediator->save_manager; } + +const keyboard_state_t & Script::get_keyboard_state() const { + SDLContext & sdl_context = this->mediator->sdl_context; + return sdl_context.get_keyboard_state(); +} + +bool Script::get_key_state(Keycode key) const noexcept { + try { + return this->get_keyboard_state().at(key); + } catch (...) { + return false; + } +} diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 49e625f..65306cd 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -2,37 +2,233 @@ #include <vector> -namespace crepe { -class ScriptSystem; -} +#include "../api/KeyCodes.h" +#include "../manager/EventManager.h" +#include "../manager/Mediator.h" +#include "../system/CollisionSystem.h" +#include "../types.h" +#include "../util/OptionalRef.h" namespace crepe { +class ScriptSystem; class BehaviorScript; +class ComponentManager; +/** + * \brief Script interface + * + * This class is used as a base class for user-defined scripts that can be added to game + * objects using the \c BehaviorScript component. + * + * \note Additional *events* (like Unity's OnDisable and OnEnable) should be implemented as + * member or lambda methods in derivative user script classes and registered in \c init(). + * + * \warning Concrete scripts are allowed do create a custom constructor, but the utility + * functions should not be called inside the constructor as they rely on late references that + * are only available after the constructor returns. + * + * \see feature_script + */ class Script { - friend class crepe::ScriptSystem; - protected: + /** + * \name Interface functions + * \{ + */ + /** + * \brief Script initialization function (empty by default) + * + * This function is called during the ScriptSystem::update() routine *before* + * Script::update() if it (a) has not yet been called and (b) the \c BehaviorScript component + * holding this script instance is active. + */ virtual void init() {} + /** + * \brief Script update function (empty by default) + * + * This function is called during the ScriptSystem::update() routine if the \c BehaviorScript + * component holding this script instance is active. + */ virtual void update() {} - // NOTE: additional *events* (like unity's OnDisable and OnEnable) should be - // implemented as member methods in derivative user script classes and - // registered in init(), otherwise this class will balloon in size with each - // added event. + //! \} + + //! ScriptSystem calls \c init() and \c update() + friend class crepe::ScriptSystem; protected: + /** + * \name Utility functions + * \{ + */ + + /** + * \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> - T & get_component(); + RefVector<T> get_components() const; + /** + * \copydoc ComponentManager::get_components_by_id + * \see ComponentManager::get_components_by_id + */ template <typename T> - std::vector<std::reference_wrapper<T>> get_components(); + 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 + * \param args Log::logf parameters + */ + template <typename... Args> + void logf(Args &&... args); + + /** + * \brief Subscribe to an event with an explicit channel + * \see 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 + */ + template <typename EventType> + void subscribe(const EventHandler<EventType> & callback); + + /** + * \brief Set the next scene using SceneManager + * \see SceneManager::set_next_scene + */ + void set_next_scene(const std::string & name); + + //! Retrieve SaveManager reference + SaveManager & get_save_manager() const; + /** + * \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; + /** + * \brief Utility function to retrieve a single key state. + * \see SDLContext::get_keyboard_state + * + * \return Keycode state (true if pressed, false if not pressed). + * + */ + bool get_key_state(Keycode key) const noexcept; + //! \} private: - friend class crepe::BehaviorScript; - BehaviorScript * parent = nullptr; + /** + * \brief Internal subscribe function + * + * This function exists so certain template specializations of Script::subscribe can be + * explicitly deleted, and does the following: + * - Wrap the user-provided callback in a check that tests if the parent BehaviorScript + * component is still active + * - Store the subscriber handle returned by the event manager so this listener is + * automatically unsubscribed at the end of this Script instance's life + * + * \tparam EventType concrete Event class + * \param callback User-provided callback function + * \param channel Event channel (may have been overridden by template specializations) + */ + template <typename EventType> + void subscribe_internal(const EventHandler<EventType> & callback, event_channel_t channel); + +protected: + // NOTE: This must be the only constructor on Script, see "Late references" below + Script() = default; + //! Only \c BehaviorScript instantiates Script + friend class BehaviorScript; + +public: + // std::unique_ptr destroys script + virtual ~Script(); + +private: + Script(const Script &) = delete; + Script(Script &&) = delete; + Script & operator=(const Script &) = delete; + Script & operator=(Script &&) = delete; + +private: + /** + * \name Late references + * + * These references are set by BehaviorScript immediately after calling the constructor of + * Script. + * + * \note Script must have a constructor without arguments so the game programmer doesn't need + * to manually add `using Script::Script` to their concrete script class if they want to + * implement a non-default constructor (e.g. for passing references to their own concrete + * Script classes). + * + * \{ + */ + //! Game object ID of game object parent BehaviorScript is attached to + game_object_id_t game_object_id; + //! Reference to parent component + OptionalRef<bool> active; + //! Mediator reference + OptionalRef<Mediator> mediator; + //! \} + +private: + //! Flag to indicate if \c init() has been called already + bool initialized = false; + //! List of subscribed events + std::vector<subscription_t> listeners; }; +/** + * \brief Subscribe to CollisionEvent 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 CollisionEvent events that apply to the + * current GameObject the parent BehaviorScript is attached to. + */ +template <> +void Script::subscribe(const EventHandler<CollisionEvent> & callback); +template <> +void Script::subscribe(const EventHandler<CollisionEvent> & 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 d96c0e8..225a51c 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../ComponentManager.h" +#include "../manager/ComponentManager.h" #include "BehaviorScript.h" #include "Script.h" @@ -8,18 +8,70 @@ namespace crepe { template <typename T> -T & Script::get_component() { - std::vector<std::reference_wrapper<T>> all_components - = this->get_components<T>(); - if (all_components.size() < 1) throw nullptr; // TODO +T & Script::get_component() const { + using namespace std; + 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())); return all_components.back().get(); } template <typename T> -std::vector<std::reference_wrapper<T>> Script::get_components() { - ComponentManager & mgr = ComponentManager::get_instance(); - return mgr.get_components_by_id<T>(this->parent->game_object_id); +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 <typename EventType> +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 { + bool & active = this->active; + if (!active) return false; + return callback(data); + }, + channel); + this->listeners.push_back(listener); +} + +template <typename EventType> +void Script::subscribe(const EventHandler<EventType> & callback, event_channel_t channel) { + this->subscribe_internal(callback, channel); +} + +template <typename EventType> +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 3dd44f2..ba684ba 100644 --- a/src/crepe/api/Sprite.cpp +++ b/src/crepe/api/Sprite.cpp @@ -1,18 +1,20 @@ -#include <cstdint> -#include <memory> +#include <cmath> -#include "../util/log.h" +#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(uint32_t id, shared_ptr<Texture> image, const Color & color, - const FlipSettings & flip) - : Component(id), color(color), flip(flip), sprite_image(image) { +Sprite::Sprite(game_object_id_t id, const Asset & texture, const Sprite::Data & data) + : Component(id), + source(texture), + data(data) { + dbg_trace(); } diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h index bdb4da9..a2409c2 100644 --- a/src/crepe/api/Sprite.h +++ b/src/crepe/api/Sprite.h @@ -1,32 +1,114 @@ #pragma once -#include <SDL2/SDL_rect.h> -#include <cstdint> -#include <memory> +#include "../Component.h" +#include "api/Asset.h" -#include "api/Color.h" -#include "api/Texture.h" - -#include "Component.h" +#include "Color.h" +#include "types.h" namespace crepe { -struct FlipSettings { - bool flip_x = 1; - bool flip_y = 1; -}; +class SDLContext; +class Animator; +class AnimatorSystem; +/** + * \brief Represents a renderable sprite component. + * + * A renderable sprite that can be displayed in the game. It includes a texture, color, and + * flip settings, and is managed in layers with defined sorting orders. + */ class Sprite : public Component { +public: + //! settings to flip the image + struct FlipSettings { + //! horizantal flip + bool flip_x = false; + //! vertical flip + bool flip_y = false; + }; + + //! Sprite data that does not have to be set in the constructor + struct Data { + /** + * \brief Sprite tint (multiplied) + * + * The sprite texture's pixels are multiplied by this color before being displayed + * (including alpha channel for transparency). + */ + Color color = Color::WHITE; + + //! Flip settings for the sprite + FlipSettings flip; + + //! Layer sorting level of the sprite + const int sorting_in_layer = 0; + + //! Order within the sorting layer + const int order_in_layer = 0; + + /** + * \brief width and height of the sprite in game units + * + * - if exclusively width is specified, the height is calculated using the texture's aspect + * ratio + * - if exclusively height is specified, the width is calculated using the texture's aspect + * ratio + * - if both are specified the texture is streched to fit the specified size + */ + vec2 size = {0, 0}; + + //! independent sprite angle. rotating clockwise direction in degrees + float angle_offset = 0; + + //! independent sprite scale multiplier + float scale_offset = 1; + + //! independent sprite offset position + vec2 position_offset; + }; public: - Sprite(uint32_t game_id, std::shared_ptr<Texture> image, - const Color & color, const FlipSettings & flip); + /** + * \param game_id Unique identifier for the game object this sprite belongs to. + * \param texture asset of the image + * \param ctx all the sprite data + */ + Sprite(game_object_id_t id, const Asset & texture, const Data & data); ~Sprite(); - std::shared_ptr<Texture> sprite_image; - Color color; - FlipSettings flip; - uint8_t sorting_in_layer; - uint8_t order_in_layer; + + //! Texture used for the sprite + const Asset source; + + Data data; + +private: + //! Reads the mask of sprite + friend class SDLContext; + + //! Reads the all the variables plus the mask + friend class Animator; + + //! 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; + int x = 0; + int y = 0; + }; + //! 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; }; } // namespace crepe diff --git a/src/crepe/api/Text.cpp b/src/crepe/api/Text.cpp new file mode 100644 index 0000000..54a4370 --- /dev/null +++ b/src/crepe/api/Text.cpp @@ -0,0 +1,12 @@ +#include "../facade/FontFacade.h" + +#include "Text.h" + +using namespace crepe; + +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) + : UIObject(id, dimensions, offset), + text(text), + data(data), + font_family(font_family) {} diff --git a/src/crepe/api/Text.h b/src/crepe/api/Text.h new file mode 100644 index 0000000..c30dc80 --- /dev/null +++ b/src/crepe/api/Text.h @@ -0,0 +1,66 @@ +#pragma once + +#include <optional> +#include <string> + +#include "../Component.h" + +#include "Asset.h" +#include "Color.h" +#include "UIObject.h" + +namespace crepe { +/** + * \brief Text UIObject component for displaying text + * + * This class can be used to display text on screen. By setting the font_family to a font already stored on the current device it will automatically be loaded in. + */ +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; + + //! Label text color. + Color text_color = Color::BLACK; + }; + +public: + /** + * + * \param dimensions Width and height of the UIObject. + * \param offset Offset of the UIObject relative to its transform + * \param text The text to be displayed. + * \param font_family The font style name to be displayed. + * \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 = ""); + + //! Label text. + std::string text = ""; + //! font family name + std::string font_family = ""; + //! Font asset variable if this is not set, it will use the font_family to create an asset. + std::optional<Asset> font; + //! Data instance + Data data; +}; + +} // namespace crepe diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp deleted file mode 100644 index 8fc5c13..0000000 --- a/src/crepe/api/Texture.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include <SDL2/SDL_render.h> - -#include "../facade/SDLContext.h" -#include "../util/log.h" - -#include "Asset.h" -#include "Texture.h" - -using namespace crepe; -using namespace std; - -Texture::Texture(unique_ptr<Asset> res) { - dbg_trace(); - this->load(std::move(res)); -} - -Texture::Texture(const char * src) { - dbg_trace(); - this->load(make_unique<Asset>(src)); -} - -Texture::~Texture() { - dbg_trace(); - if (this->texture != nullptr) { - SDL_DestroyTexture(this->texture); - } -} - -void Texture::load(unique_ptr<Asset> res) { - SDLContext & ctx = SDLContext::get_instance(); - this->texture = ctx.texture_from_path(res->canonical()); -} diff --git a/src/crepe/api/Texture.h b/src/crepe/api/Texture.h deleted file mode 100644 index 9a86f6f..0000000 --- a/src/crepe/api/Texture.h +++ /dev/null @@ -1,33 +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 <memory> - -#include "Asset.h" - -namespace crepe { -class SDLContext; -} - -namespace crepe { - -class Texture { - -public: - Texture(const char * src); - Texture(std::unique_ptr<Asset> res); - ~Texture(); - -private: - void load(std::unique_ptr<Asset> res); - -private: - SDL_Texture * texture = nullptr; - - friend class crepe::SDLContext; -}; - -} // namespace crepe diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp index 70b16bd..a85b792 100644 --- a/src/crepe/api/Transform.cpp +++ b/src/crepe/api/Transform.cpp @@ -1,16 +1,13 @@ -#include <cstdint> +#include "../util/Log.h" -#include "api/Point.h" -#include "util/log.h" - -#include "Component.h" #include "Transform.h" using namespace crepe; -Transform::Transform(uint32_t game_id, const Point & point, double rot, - double scale) - : Component(game_id), position(point), rotation(rot), scale(scale) { +Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, double scale) + : Component(id), + position(point), + rotation(rotation), + scale(scale) { dbg_trace(); } -Transform::~Transform() { dbg_trace(); } diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h index d416088..7ee6d65 100644 --- a/src/crepe/api/Transform.h +++ b/src/crepe/api/Transform.h @@ -1,27 +1,40 @@ #pragma once -#include <cstdint> - -#include "api/Point.h" - #include "Component.h" +#include "types.h" namespace crepe { +/** + * \brief Transform component + * + * This class represents the Transform component. It stores the position, rotation and scale of + * a GameObject. + */ class Transform : public Component { - // FIXME: What's the difference between the `Point` and `Position` - // classes/structs? How about we replace both with a universal `Vec2` that - // works similar (or the same) as those found in GLSL? - public: - Transform(uint32_t id, const Point &, double, double); - ~Transform(); //! Translation (shift) - Point position; - //! Rotation, in radians - double rotation; + vec2 position = {0, 0}; + //! Rotation, in degrees clockwise + float rotation = 0; //! Multiplication factor - double scale; + float scale = 0; + +protected: + /** + * \param id The id of the GameObject this component belongs to + * \param point The position of the GameObject + * \param rotation The rotation of the GameObject + * \param scale The scale of the GameObject + */ + Transform(game_object_id_t id, const vec2 & point, double rotation, double scale); + /** + * There is always exactly one transform component per entity + * \return 1 + */ + virtual int get_instances_max() const { return 1; } + //! ComponentManager instantiates all components + friend class ComponentManager; }; } // namespace crepe diff --git a/src/crepe/api/UIObject.cpp b/src/crepe/api/UIObject.cpp new file mode 100644 index 0000000..d239b89 --- /dev/null +++ b/src/crepe/api/UIObject.cpp @@ -0,0 +1,8 @@ +#include "UIObject.h" + +using namespace crepe; + +UIObject::UIObject(game_object_id_t id, const vec2 & dimensions, const vec2 & offset) + : Component(id), + dimensions(dimensions), + offset(offset) {} diff --git a/src/crepe/api/UIObject.h b/src/crepe/api/UIObject.h new file mode 100644 index 0000000..f7f4fba --- /dev/null +++ b/src/crepe/api/UIObject.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../Component.h" + +namespace crepe { + +/** + * \brief Represents a UI object in the game, derived from the Component class. + */ +class UIObject : public Component { +public: + /** + * \brief Constructs a UiObject with the specified game object ID. + * \param id The unique ID of the game object associated with this UI object. + * \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); + //! Width and height of the UIObject + vec2 dimensions; + //! Position offset relative to this GameObjects Transform + vec2 offset; +}; + +} // namespace crepe diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h new file mode 100644 index 0000000..bf9d124 --- /dev/null +++ b/src/crepe/api/Vector2.h @@ -0,0 +1,97 @@ +#pragma once + +namespace crepe { + +//! 2D vector +template <class T> +struct Vector2 { + //! X component of the vector + T x = 0; + //! Y component of the vector + T y = 0; + + //! Subtracts another vector from this vector and returns the result. + Vector2 operator-(const Vector2<T> & other) const; + + //! Subtracts a scalar value from both components of this vector and returns the result. + Vector2 operator-(T scalar) const; + + //! Adds another vector to this vector and returns the result. + Vector2 operator+(const Vector2<T> & other) const; + + //! Adds a scalar value to both components of this vector and returns the result. + Vector2 operator+(T scalar) const; + + //! Multiplies this vector by another vector element-wise and returns the result. + Vector2 operator*(const Vector2<T> & other) const; + + //! Multiplies this vector by a scalar and returns the result. + Vector2 operator*(T scalar) const; + + //! Divides this vector by another vector element-wise and returns the result. + Vector2 operator/(const Vector2<T> & other) const; + + //! Divides this vector by a scalar and returns the result. + Vector2 operator/(T scalar) const; + + //! Adds another vector to this vector and updates this vector. + Vector2 & operator+=(const Vector2<T> & other); + + //! Adds a scalar value to both components of this vector and updates this vector. + Vector2 & operator+=(T other); + + //! Subtracts another vector from this vector and updates this vector. + Vector2 & operator-=(const Vector2<T> & other); + + //! Subtracts a scalar value from both components of this vector and updates this vector. + Vector2 & operator-=(T other); + + //! Multiplies this vector by another vector element-wise and updates this vector. + Vector2 & operator*=(const Vector2<T> & other); + + //! Multiplies this vector by a scalar and updates this vector. + Vector2 & operator*=(T other); + + //! Divides this vector by another vector element-wise and updates this vector. + Vector2 & operator/=(const Vector2<T> & other); + + //! Divides this vector by a scalar and updates this vector. + Vector2 & operator/=(T other); + + //! Returns the negation of this vector. + Vector2 operator-() const; + + //! Checks if this vector is equal to another vector. + bool operator==(const Vector2<T> & other) const; + + //! 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 + +#include "Vector2.hpp" diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp new file mode 100644 index 0000000..ff53cb0 --- /dev/null +++ b/src/crepe/api/Vector2.hpp @@ -0,0 +1,166 @@ +#pragma once + +#include <cmath> + +#include "Vector2.h" + +namespace crepe { + +template <class T> +Vector2<T> Vector2<T>::operator-(const Vector2<T> & other) const { + return {x - other.x, y - other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator-(T scalar) const { + return {x - scalar, y - scalar}; +} + +template <class T> +Vector2<T> Vector2<T>::operator+(const Vector2<T> & other) const { + return {x + other.x, y + other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator+(T scalar) const { + return {x + scalar, y + scalar}; +} + +template <class T> +Vector2<T> Vector2<T>::operator*(const Vector2<T> & other) const { + return {x * other.x, y * other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator*(T scalar) const { + return {x * scalar, y * scalar}; +} + +template <class T> +Vector2<T> Vector2<T>::operator/(const Vector2<T> & other) const { + return {x / other.x, y / other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator/(T scalar) const { + return {x / scalar, y / scalar}; +} + +template <class T> +Vector2<T> & Vector2<T>::operator+=(const Vector2<T> & other) { + x += other.x; + y += other.y; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator+=(T other) { + x += other; + y += other; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator-=(const Vector2<T> & other) { + x -= other.x; + y -= other.y; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator-=(T other) { + x -= other; + y -= other; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator*=(const Vector2<T> & other) { + x *= other.x; + y *= other.y; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator*=(T other) { + x *= other; + y *= other; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator/=(const Vector2<T> & other) { + x /= other.x; + y /= other.y; + return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator/=(T other) { + x /= other; + y /= other; + return *this; +} + +template <class T> +Vector2<T> Vector2<T>::operator-() const { + return {-x, -y}; +} + +template <class T> +bool Vector2<T>::operator==(const Vector2<T> & other) const { + return x == other.x && y == other.y; +} + +template <class T> +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 |