diff options
Diffstat (limited to 'src/crepe')
52 files changed, 1555 insertions, 349 deletions
| diff --git a/src/crepe/Exception.cpp b/src/crepe/Exception.cpp index f27d5a8..dab8f2e 100644 --- a/src/crepe/Exception.cpp +++ b/src/crepe/Exception.cpp @@ -6,9 +6,7 @@  using namespace std;  using namespace crepe; -const char * Exception::what() { -	return error.c_str(); -} +const char * Exception::what() { return error.c_str(); }  Exception::Exception(const char * fmt, ...) {  	va_list args; @@ -16,4 +14,3 @@ Exception::Exception(const char * fmt, ...) {  	this->error = va_stringf(args, fmt);  	va_end(args);  } - diff --git a/src/crepe/Exception.h b/src/crepe/Exception.h index e4a7bb8..6473043 100644 --- a/src/crepe/Exception.h +++ b/src/crepe/Exception.h @@ -17,7 +17,6 @@ protected:  	Exception() = default;  	//! Formatted error message  	std::string error; -  }; -} +} // namespace crepe diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp index 4810e80..582edf4 100644 --- a/src/crepe/Particle.cpp +++ b/src/crepe/Particle.cpp @@ -2,19 +2,32 @@  using namespace crepe; -Particle::Particle() { this->active = false; } - -void Particle::reset(float lifespan, Position position, Position velocity) { +void Particle::reset(uint32_t lifespan, const Vector2 & position, +					 const Vector2 & velocity, double angle) { +	// Initialize the particle state  	this->time_in_life = 0;  	this->lifespan = lifespan;  	this->position = position;  	this->velocity = velocity; +	this->angle = angle;  	this->active = true; +	// Reset force accumulation +	this->force_over_time = {0, 0}; +} + +void Particle::update() { +	// Deactivate particle if it has exceeded its lifespan +	if (++time_in_life >= lifespan) { +		this->active = false; +		return; +	} + +	// Update velocity based on accumulated force and update position +	this->velocity += force_over_time; +	this->position += velocity;  } -void Particle::update(float deltaTime) { -	time_in_life += deltaTime; -	position.x += velocity.x * deltaTime; -	position.y += velocity.y * deltaTime; -	if (time_in_life >= lifespan) this->active = false; +void Particle::stop_movement() { +	// Reset velocity to halt movement +	this->velocity = {0, 0};  } diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h index 21e691d..3eaebc3 100644 --- a/src/crepe/Particle.h +++ b/src/crepe/Particle.h @@ -1,22 +1,64 @@  #pragma once -#include "Position.h" +#include <cstdint> + +#include "api/Vector2.h"  namespace crepe { +/** + * \brief Represents a particle in the particle emitter. + * + * This class stores information about a single particle, including its position, + * velocity, lifespan, and other properties. Particles can be updated over time + * to simulate movement and can also be reset or stopped. + */  class Particle { +	// TODO: add friend particleSsytem and rendersystem. Unit test will fail. +  public: -	Position position; -	// FIXME: `Position` is an awkward name for a 2D vector. See FIXME comment in -	// api/Transform.h for fix proposal. -	Position velocity; -	float lifespan; -	bool active; +	//! Position of the particle in 2D space. +	Vector2 position; +	//! Velocity vector indicating the speed and direction of the particle. +	Vector2 velocity; +	//! Accumulated force affecting the particle over time. +	Vector2 force_over_time; +	//! Total lifespan of the particle in milliseconds. +	uint32_t lifespan; +	//! Active state of the particle; true if it is in use, false otherwise. +	bool active = false; +	//! The time the particle has been alive, in milliseconds. +	uint32_t time_in_life = 0; +	//! The angle at which the particle is oriented or moving. +	double angle = 0; -	Particle(); -	void reset(float lifespan, Position position, Position velocity); -	void update(float deltaTime); -	float time_in_life; +	/** +	 * \brief Resets the particle with new properties. +	 * +	 * This method initializes the particle with a specific lifespan, position, +	 * velocity, and angle, marking it as active and resetting its life counter. +	 * +	 * \param lifespan  The lifespan of the particle in amount of updates. +	 * \param position  The starting position of the particle. +	 * \param velocity  The initial velocity of the particle. +	 * \param angle     The angle of the particle's trajectory or orientation. +	 */ +	void reset(uint32_t lifespan, const Vector2 & position, +			   const Vector2 & velocity, double angle); +	/** +	 * \brief Updates the particle's state. +	 * +	 * Advances the particle's position based on its velocity and applies accumulated forces.  +	 * Deactivates the particle if its lifespan has expired. +	 */ +	void update(); +	/** +	 * \brief Stops the particle's movement. +	 * +	 * Sets the particle's velocity to zero, effectively halting any further +	 * movement. +	 */ +	void stop_movement();  };  } // namespace crepe diff --git a/src/crepe/ValueBroker.h b/src/crepe/ValueBroker.h index 88988b4..d844d6a 100644 --- a/src/crepe/ValueBroker.h +++ b/src/crepe/ValueBroker.h @@ -23,10 +23,12 @@ public:  	virtual const T & get();  	typedef std::function<void(const T & target)> setter_t; -	typedef std::function<const T & ()> getter_t; +	typedef std::function<const T &()> getter_t; +  private:  	setter_t setter;  	getter_t getter; +  public:  	/**  	 * \param setter  Function that sets the variable @@ -35,7 +37,6 @@ public:  	ValueBroker(const setter_t & setter, const getter_t & getter);  }; -} +} // namespace crepe  #include "ValueBroker.hpp" - diff --git a/src/crepe/ValueBroker.hpp b/src/crepe/ValueBroker.hpp index 0d08333..5c3bed9 100644 --- a/src/crepe/ValueBroker.hpp +++ b/src/crepe/ValueBroker.hpp @@ -5,11 +5,9 @@  namespace crepe {  template <typename T> -ValueBroker<T>::ValueBroker(const setter_t & setter, const getter_t & getter) : -	setter(setter), -	getter(getter) -	{ -} +ValueBroker<T>::ValueBroker(const setter_t & setter, const getter_t & getter) +	: setter(setter), +	  getter(getter) {}  template <typename T>  const T & ValueBroker<T>::get() { @@ -21,5 +19,4 @@ void ValueBroker<T>::set(const T & value) {  	this->setter(value);  } -} - +} // namespace crepe diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp new file mode 100644 index 0000000..58fee2a --- /dev/null +++ b/src/crepe/api/Animator.cpp @@ -0,0 +1,26 @@ + +#include <cstdint> + +#include "util/log.h" + +#include "Animator.h" +#include "Component.h" +#include "Sprite.h" + +using namespace crepe; + +Animator::Animator(uint32_t id, Sprite & ss, int row, int col, int col_animator) +	: Component(id), +	  spritesheet(ss), +	  row(row), +	  col(col) { +	dbg_trace(); + +	animator_rect = spritesheet.sprite_rect; +	animator_rect.h /= col; +	animator_rect.w /= row; +	animator_rect.x = 0; +	animator_rect.y = col_animator * animator_rect.h; +	this->active = false; +} +Animator::~Animator() { dbg_trace(); } diff --git a/src/crepe/api/Animator.h b/src/crepe/api/Animator.h new file mode 100644 index 0000000..def0240 --- /dev/null +++ b/src/crepe/api/Animator.h @@ -0,0 +1,76 @@ +#pragma once + +#include <cstdint> + +#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: +	//TODO: need to implement this +	void loop(); +	void stop(); + +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 A reference to the Sprite object which holds the sprite sheet for animation. +	 * \param row The maximum number of rows in the sprite sheet. +	 * \param col The maximum number of columns in the sprite sheet. +	 * \param col__animate The specific col index of the sprite sheet to animate. This allows selecting which col to animate from multiple col in the sheet. +	 * +	 * This constructor sets up the Animator with the given parameters, and initializes the animation system. +	 */ +	Animator(uint32_t id, Sprite & spritesheet, int row, int col, +			 int col_animate); + +	~Animator(); // dbg_trace +	Animator(const Animator &) = delete; +	Animator(Animator &&) = delete; +	Animator & operator=(const Animator &) = delete; +	Animator & operator=(Animator &&) = delete; + +private: +	//! A reference to the Sprite sheet containing the animation frames. +	Sprite & spritesheet; + +	//! The maximum number of columns in the sprite sheet. +	const int col; + +	//! The maximum number of rows in the sprite sheet. +	const int row; + +	//! The current col being animated. +	int curr_col = 0; + +	//! The current row being animated. +	int curr_row = 0; + +	Rect animator_rect; + +	//TODO: Is this necessary? +	//int fps; + +private: +	//! AnimatorSystem adjust the private member parameters of Animator; +	friend class AnimatorSystem; + +	//! SDLContext reads the Animator member var's +	friend class SDLContext; +}; +} // namespace crepe +// diff --git a/src/crepe/api/AssetManager.h b/src/crepe/api/AssetManager.h index fefbed9..86a9902 100644 --- a/src/crepe/api/AssetManager.h +++ b/src/crepe/api/AssetManager.h @@ -7,9 +7,19 @@  namespace crepe { +/** + * \brief The AssetManager is responsible for storing and managing assets over + * multiple scenes. + *  + * The AssetManager ensures that assets are loaded once and can be accessed + * across different scenes. It caches assets to avoid reloading them every time + * a scene is loaded. Assets are retained in memory until the AssetManager is + * destroyed, at which point the cached assets are cleared. + */  class AssetManager {  private: +	//! A cache that holds all the assets, accessible by their file path, over multiple scenes.  	std::unordered_map<std::string, std::any> asset_cache;  private: @@ -22,12 +32,32 @@ public:  	AssetManager & operator=(const AssetManager &) = delete;  	AssetManager & operator=(AssetManager &&) = delete; +	/** +	 * \brief Retrieves the singleton instance of the AssetManager. +	 * +	 * \return A reference to the single instance of the AssetManager. +	 */  	static AssetManager & get_instance();  public: -	template <typename asset> -	std::shared_ptr<asset> cache(const std::string & file_path, -								 bool reload = false); +	/** +	 * \brief Caches an asset by loading it from the given file path. +	 * +	 * \param file_path The path to the asset file to load. +	 * \param reload If true, the asset will be reloaded from the file, even if +	 * it is already cached. +	 * \tparam T The type of asset to cache (e.g., texture, sound, etc.). +	 *  +	 * \return A shared pointer to the cached asset. +	 *  +	 * This template function caches the asset at the given file path. If the +	 * asset is already cached and `reload` is false, the existing cached version +	 * will be returned. Otherwise, the asset will be reloaded and added to the +	 * cache. +	 */ +	template <typename T> +	std::shared_ptr<T> cache(const std::string & file_path, +							 bool reload = false);  };  } // namespace crepe diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index fd2b6f1..5bc70f9 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -11,7 +11,7 @@ namespace crepe {  //! Audio source component  class AudioSource : public Component {  public: -	AudioSource(game_object_id_t id, std::unique_ptr<Asset> audio_clip); +	AudioSource(game_object_id_t id, const Asset & source);  	virtual ~AudioSource() = default;  public: diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 3b20142..85696c4 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -16,6 +16,10 @@ target_sources(crepe PUBLIC  	Scene.cpp  	SceneManager.cpp  	Vector2.cpp +	Camera.cpp +	Animator.cpp +	LoopManager.cpp +	LoopTimer.cpp  )  target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -38,4 +42,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	Metadata.h  	SceneManager.h  	SceneManager.hpp +	Camera.h +	Animator.h +	LoopManager.h +	LoopTimer.h  ) diff --git a/src/crepe/api/Camera.cpp b/src/crepe/api/Camera.cpp new file mode 100644 index 0000000..6355a03 --- /dev/null +++ b/src/crepe/api/Camera.cpp @@ -0,0 +1,18 @@ + +#include <cstdint> + +#include "util/log.h" + +#include "Camera.h" +#include "Color.h" +#include "Component.h" + +using namespace crepe; + +Camera::Camera(uint32_t id, const Color & bg_color) +	: Component(id), +	  bg_color(bg_color) { +	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..ba3a9ef --- /dev/null +++ b/src/crepe/api/Camera.h @@ -0,0 +1,55 @@ +#pragma once + +#include <cstdint> + +#include "Color.h" +#include "Component.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: +	/** +	 * \brief Constructs a Camera with the specified ID and background color. +	 * \param id Unique identifier for the camera component. +	 * \param bg_color Background color for the camera view. +	 */ +	Camera(uint32_t id, const Color & bg_color); +	~Camera(); // dbg_trace only + +public: +	//! Background color of the camera view. +	Color bg_color; + +	//! Aspect ratio height for the camera. +	double aspect_height = 480; + +	//! Aspect ratio width for the camera. +	double aspect_width = 640; + +	//! X-coordinate of the camera position. +	double x = 0.0; + +	//! Y-coordinate of the camera position. +	double y = 0.0; + +	//! Zoom level of the camera view. +	double zoom = 1.0; + +public: +	/** +	 * \brief Gets the maximum number of camera instances allowed. +	 * \return Maximum instance count as an integer. +	 */ +	virtual int get_instances_max() const { return 10; } +}; +} // namespace crepe diff --git a/src/crepe/api/CircleCollider.h b/src/crepe/api/CircleCollider.h index caa7e43..e77a592 100644 --- a/src/crepe/api/CircleCollider.h +++ b/src/crepe/api/CircleCollider.h @@ -6,7 +6,8 @@ namespace crepe {  class CircleCollider : public Collider {  public:  	CircleCollider(game_object_id_t game_object_id, int radius) -		: Collider(game_object_id), radius(radius) {} +		: Collider(game_object_id), +		  radius(radius) {}  	int radius;  }; diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp index fc6313d..9e5f187 100644 --- a/src/crepe/api/Color.cpp +++ b/src/crepe/api/Color.cpp @@ -11,8 +11,7 @@ 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) { +Color::Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) {  	this->a = alpha;  	this->r = red;  	this->g = green; diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h index 6b54888..aa47bf4 100644 --- a/src/crepe/api/Color.h +++ b/src/crepe/api/Color.h @@ -1,14 +1,17 @@  #pragma once +#include <cstdint> +  namespace crepe { +// TODO: make Color a struct w/o constructors/destructors  class Color {  	// FIXME: can't these colors be defined as a `static constexpr const Color`  	// instead?  public: -	Color(double red, double green, double blue, double alpha); +	Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha);  	static const Color & get_white();  	static const Color & get_red();  	static const Color & get_green(); @@ -19,10 +22,11 @@ public:  	static const Color & get_black();  private: -	double r; -	double g; -	double b; -	double a; +	// TODO: why are these private!? +	uint8_t r; +	uint8_t g; +	uint8_t b; +	uint8_t a;  	static Color white;  	static Color red; @@ -32,6 +36,9 @@ private:  	static Color magenta;  	static Color yellow;  	static Color black; + +private: +	friend class SDLContext;  };  } // namespace crepe diff --git a/src/crepe/api/Config.cpp b/src/crepe/api/Config.cpp index d6206da..0100bcc 100644 --- a/src/crepe/api/Config.cpp +++ b/src/crepe/api/Config.cpp @@ -6,4 +6,3 @@ Config & Config::get_instance() {  	static Config instance;  	return instance;  } - diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 56e3af5..8c9e643 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -7,6 +7,7 @@ namespace crepe {  class Config {  private:  	Config() = default; +  public:  	~Config() = default; @@ -16,8 +17,8 @@ public:  	// singleton  	Config(const Config &) = delete;  	Config(Config &&) = delete; -	Config & operator = (const Config &) = delete; -	Config & operator = (Config &&) = delete; +	Config & operator=(const Config &) = delete; +	Config & operator=(Config &&) = delete;  public:  	//! Logging-related settings @@ -60,4 +61,3 @@ public:  };  } // namespace crepe - diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp new file mode 100644 index 0000000..2e9823f --- /dev/null +++ b/src/crepe/api/LoopManager.cpp @@ -0,0 +1,55 @@ + +#include "../facade/SDLContext.h" +#include "../system/RenderSystem.h" +#include "../system/ScriptSystem.h" + +#include "LoopManager.h" +#include "LoopTimer.h" + +using namespace crepe; + +LoopManager::LoopManager() {} +void LoopManager::process_input() { +	SDLContext::get_instance().handle_events(this->game_running); +} +void LoopManager::start() { +	this->setup(); +	this->loop(); +} +void LoopManager::set_running(bool running) { this->game_running = running; } + +void LoopManager::fixed_update() {} + +void LoopManager::loop() { +	LoopTimer & timer = LoopTimer::get_instance(); +	timer.start(); + +	while (game_running) { +		timer.update(); + +		while (timer.get_lag() >= timer.get_fixed_delta_time()) { +			this->process_input(); +			this->fixed_update(); +			timer.advance_fixed_update(); +		} + +		this->update(); +		this->render(); + +		timer.enforce_frame_rate(); +	} +} + +void LoopManager::setup() { +	this->game_running = true; +	LoopTimer::get_instance().start(); +	LoopTimer::get_instance().set_fps(60); +} + +void LoopManager::render() { +	if (this->game_running) { +		RenderSystem::get_instance().update(); +	} +} + +void LoopManager::update() { LoopTimer & timer = LoopTimer::get_instance(); } diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h new file mode 100644 index 0000000..2f03193 --- /dev/null +++ b/src/crepe/api/LoopManager.h @@ -0,0 +1,79 @@ +#pragma once + +#include <memory> + +class RenderSystem; +class SDLContext; +class LoopTimer; +class ScriptSystem; +class SoundSystem; +class ParticleSystem; +class PhysicsSystem; +class AnimatorSystem; +class CollisionSystem; +namespace crepe { + +class LoopManager { +public: +	void start(); +	LoopManager(); + +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 Function for handling input-related system calls. +	 * +	 * Processes user inputs from keyboard and mouse. +	 */ +	void process_input(); + +	/** +	 * \brief Per-frame update. +	 * +	 * Updates the game state based on the elapsed time since the last frame. +	 */ +	void update(); + +	/** +	 * \brief Late update which is called after update(). +	 * +	 * This function can be used for final adjustments before rendering. +	 */ +	void late_update(); + +	/** +	 * \brief Fixed update executed at a fixed rate. +	 * +	 * This function updates physics and game logic based on LoopTimer's fixed_delta_time. +	 */ +	void fixed_update(); +	/** +	 * \brief Set game running variable +	 * +	 * \param running running (false = game shutdown, true = game running) +	 */ +	void set_running(bool running); +	/** +	 * \brief Function for executing render-related systems. +	 * +	 * Renders the current state of the game to the screen. +	 */ +	void render(); + +	bool game_running = false; +	//#TODO add system instances +}; + +} // namespace crepe diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp new file mode 100644 index 0000000..8f09e41 --- /dev/null +++ b/src/crepe/api/LoopTimer.cpp @@ -0,0 +1,86 @@ +#include <chrono> + +#include "../facade/SDLContext.h" +#include "../util/log.h" + +#include "LoopTimer.h" + +using namespace crepe; + +LoopTimer::LoopTimer() { dbg_trace(); } + +LoopTimer & LoopTimer::get_instance() { +	static LoopTimer instance; +	return instance; +} + +void LoopTimer::start() { +	this->last_frame_time = std::chrono::steady_clock::now(); +	this->elapsed_time = std::chrono::milliseconds(0); +	this->elapsed_fixed_time = std::chrono::milliseconds(0); +	this->delta_time = std::chrono::milliseconds(0); +} + +void LoopTimer::update() { +	auto current_frame_time = std::chrono::steady_clock::now(); +	// Convert to duration in seconds for delta time +	this->delta_time +		= std::chrono::duration_cast<std::chrono::duration<double>>( +			current_frame_time - last_frame_time); + +	if (this->delta_time > this->maximum_delta_time) { +		this->delta_time = this->maximum_delta_time; +	} + +	this->delta_time *= this->game_scale; +	this->elapsed_time += this->delta_time; +	this->last_frame_time = current_frame_time; +} + +double LoopTimer::get_delta_time() const { return this->delta_time.count(); } + +double LoopTimer::get_current_time() const { +	return this->elapsed_time.count(); +} + +void LoopTimer::advance_fixed_update() { +	this->elapsed_fixed_time += this->fixed_delta_time; +} + +double LoopTimer::get_fixed_delta_time() const { +	return this->fixed_delta_time.count(); +} + +void LoopTimer::set_fps(int fps) { +	this->fps = fps; +	// target time per frame in seconds +	this->frame_target_time = std::chrono::seconds(1) / fps; +} + +int LoopTimer::get_fps() const { return this->fps; } + +void LoopTimer::set_game_scale(double value) { this->game_scale = value; } + +double LoopTimer::get_game_scale() const { return this->game_scale; } +void LoopTimer::enforce_frame_rate() { +	std::chrono::steady_clock::time_point current_frame_time +		= std::chrono::steady_clock::now(); +	std::chrono::milliseconds frame_duration +		= std::chrono::duration_cast<std::chrono::milliseconds>( +			current_frame_time - this->last_frame_time); + +	if (frame_duration < this->frame_target_time) { +		std::chrono::milliseconds delay_time +			= std::chrono::duration_cast<std::chrono::milliseconds>( +				this->frame_target_time - frame_duration); +		if (delay_time.count() > 0) { +			SDLContext::get_instance().delay(delay_time.count()); +		} +	} + +	this->last_frame_time = current_frame_time; +} + +double LoopTimer::get_lag() const { +	return (this->elapsed_time - this->elapsed_fixed_time).count(); +} diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h new file mode 100644 index 0000000..85687be --- /dev/null +++ b/src/crepe/api/LoopTimer.h @@ -0,0 +1,147 @@ +#pragma once + +#include <chrono> +#include <cstdint> + +namespace crepe { + +class LoopTimer { +public: +	/** +     * \brief Get the singleton instance of LoopTimer. +     * +     * \return A reference to the LoopTimer instance. +     */ +	static LoopTimer & get_instance(); + +	/** +     * \brief Get the current delta time for the current frame. +     * +     * \return Delta time in seconds since the last frame. +     */ +	double get_delta_time() const; + +	/** +     * \brief Get the current game time. +     * +     * \note The current game time may vary from real-world elapsed time. +     * It is the cumulative sum of each frame's delta time. +     * +     * \return Elapsed game time in seconds. +     */ +	double get_current_time() const; + +	/** +     * \brief Set the target frames per second (FPS). +     * +     * \param fps The desired frames rendered per second. +     */ +	void set_fps(int fps); + +	/** +     * \brief Get the current frames per second (FPS). +     * +     * \return Current FPS. +     */ +	int get_fps() const; + +	/** +     * \brief Get the current game scale. +     * +     * \return The current game scale, where 0 = paused, 1 = normal speed, and values > 1 speed up the game. +     */ +	double get_game_scale() const; + +	/** +     * \brief Set the game scale. +     * +     * \param game_scale The desired game scale (0 = pause, 1 = normal speed, > 1 = speed up). +     */ +	void set_game_scale(double game_scale); + +private: +	friend class LoopManager; + +	/** +     * \brief Start the loop timer. +     * +     * Initializes the timer to begin tracking frame times. +     */ +	void start(); + +	/** +     * \brief Enforce the frame rate limit. +     * +     * Ensures that the game loop does not exceed the target FPS by delaying +     * frame updates as necessary. +     */ +	void enforce_frame_rate(); + +	/** +     * \brief Get the fixed delta time for consistent updates. +     * +     * Fixed delta time is used for operations that require uniform time steps, +     * such as physics calculations. +     * +     * \return Fixed delta time in seconds. +     */ +	double get_fixed_delta_time() const; + +	/** +     * \brief Get the accumulated lag in the game loop. +     * +     * Lag represents the difference between the target frame time and the +     * actual frame time, useful for managing fixed update intervals. +     * +     * \return Accumulated lag in seconds. +     */ +	double get_lag() const; + +	/** +     * \brief Construct a new LoopTimer object. +     * +     * Private constructor for singleton pattern to restrict instantiation +     * outside the class. +     */ +	LoopTimer(); + +	/** +     * \brief Update the timer to the current frame. +     * +     * Calculates and updates the delta time for the current frame and adds it to +     * the cumulative game time. +     */ +	void update(); + +	/** +     * \brief Advance the game loop by a fixed update interval. +     * +     * This method progresses the game state by a consistent, fixed time step, +     * allowing for stable updates independent of frame rate fluctuations. +     */ +	void advance_fixed_update(); + +private: +	//! Current frames per second +	int fps = 50; +	//! Current game scale +	double game_scale = 1; +	//! Maximum delta time in seconds to avoid large jumps +	std::chrono::duration<double> maximum_delta_time{0.25}; +	//! Delta time for the current frame in seconds +	std::chrono::duration<double> delta_time{0.0}; +	//! Target time per frame in seconds +	std::chrono::duration<double> frame_target_time +		= std::chrono::seconds(1) / fps; +	//! Fixed delta time for fixed updates in seconds +	std::chrono::duration<double> fixed_delta_time +		= std::chrono::seconds(1) / 50; +	//! Total elapsed game time in seconds +	std::chrono::duration<double> elapsed_time{0.0}; +	//! Total elapsed time for fixed updates in seconds +	std::chrono::duration<double> elapsed_fixed_time{0.0}; +	//! Time of the last frame +	std::chrono::steady_clock::time_point last_frame_time; +}; + +} // namespace crepe diff --git a/src/crepe/api/Metadata.cpp b/src/crepe/api/Metadata.cpp index 76f11d7..d421de5 100644 --- a/src/crepe/api/Metadata.cpp +++ b/src/crepe/api/Metadata.cpp @@ -4,4 +4,6 @@ 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) {} +	: Component(id), +	  name(name), +	  tag(tag) {} diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp index 3b2e2f2..35f960d 100644 --- a/src/crepe/api/ParticleEmitter.cpp +++ b/src/crepe/api/ParticleEmitter.cpp @@ -1,36 +1,12 @@ -#include <ctime> -#include <iostream> - -#include "Particle.h"  #include "ParticleEmitter.h"  using namespace crepe; -ParticleEmitter::ParticleEmitter(game_object_id_t 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(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++) { -		this->particles.emplace_back(); -	} -} - -ParticleEmitter::~ParticleEmitter() { -	std::vector<Particle>::iterator it = this->particles.begin(); -	while (it != this->particles.end()) { -		it = this->particles.erase(it); +ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, +								 const Data & data) +	: Component(game_object_id), +	  data(data) { +	for (size_t i = 0; i < this->data.max_particles; i++) { +		this->data.particles.emplace_back();  	}  } diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h index 5939723..a9e872f 100644 --- a/src/crepe/api/ParticleEmitter.h +++ b/src/crepe/api/ParticleEmitter.h @@ -1,42 +1,85 @@  #pragma once -#include <cstdint>  #include <vector>  #include "Component.h"  #include "Particle.h" +#include "Vector2.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(game_object_id_t 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; - -	//! collection of particles -	std::vector<Particle> particles; +	/** +	 * \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) +		double width = 0.0; +		//! boundary height (midpoint is emitter location) +		double height = 0.0; +		//! boundary offset from particle emitter location +		Vector2 offset; +		//! reset on exit or stop velocity and set max postion +		bool reset_on_exit = false; +	}; + +	/** +	 * \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 { +		//! position of the emitter +		Vector2 position; +		//! maximum number of particles +		const unsigned int max_particles = 0; +		//! rate of particle emission per update (Lowest value = 0.001 any lower is ignored) +		double emission_rate = 0; +		//! min speed of the particles +		double min_speed = 0; +		//! min speed of the particles +		double max_speed = 0; +		//! min angle of particle emission +		double min_angle = 0; +		//! max angle of particle emission +		double max_angle = 0; +		//! begin Lifespan of particle (only visual) +		double begin_lifespan = 0.0; +		//! end Lifespan of particle +		double end_lifespan = 0.0; +		//! force over time (physics) +		Vector2 force_over_time; +		//! particle boundary +		Boundary boundary; +		//! collection of particles +		std::vector<Particle> particles; +		//! sprite reference +		const Sprite & sprite; +	}; + +public: +	/** +	 * \param game_object_id  Identifier for the game object using this emitter. +	 * \param data            Configuration data defining particle properties. +	 */ +	ParticleEmitter(game_object_id_t game_object_id, const Data & data); + +public: +	//! Configuration data for particle emission settings. +	Data data;  };  } // namespace crepe diff --git a/src/crepe/api/Rigidbody.cpp b/src/crepe/api/Rigidbody.cpp index cbf1325..3bf1c5b 100644 --- a/src/crepe/api/Rigidbody.cpp +++ b/src/crepe/api/Rigidbody.cpp @@ -3,7 +3,8 @@  using namespace crepe;  crepe::Rigidbody::Rigidbody(uint32_t game_object_id, const Data & data) -	: Component(game_object_id), data(data) {} +	: Component(game_object_id), +	  data(data) {}  void crepe::Rigidbody::add_force_linear(const Vector2 & force) {  	this->data.linear_velocity += force; diff --git a/src/crepe/api/SaveManager.cpp b/src/crepe/api/SaveManager.cpp index 23587e4..43276c5 100644 --- a/src/crepe/api/SaveManager.cpp +++ b/src/crepe/api/SaveManager.cpp @@ -2,8 +2,8 @@  #include "../util/log.h"  #include "Config.h" -#include "ValueBroker.h"  #include "SaveManager.h" +#include "ValueBroker.h"  using namespace std;  using namespace crepe; @@ -65,17 +65,33 @@ string SaveManager::deserialize(const string & value) const noexcept {  	return value;  } -template <> uint8_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<uint64_t>(value); } -template <> int8_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<int64_t>(value); } -template <> uint16_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<uint64_t>(value); } -template <> int16_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<int64_t>(value); } -template <> uint32_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<uint64_t>(value); } -template <> int32_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<int64_t>(value); } - -SaveManager::SaveManager() { -	dbg_trace(); +template <> +uint8_t SaveManager::deserialize(const string & value) const noexcept { +	return deserialize<uint64_t>(value); +} +template <> +int8_t SaveManager::deserialize(const string & value) const noexcept { +	return deserialize<int64_t>(value); +} +template <> +uint16_t SaveManager::deserialize(const string & value) const noexcept { +	return deserialize<uint64_t>(value); +} +template <> +int16_t SaveManager::deserialize(const string & value) const noexcept { +	return deserialize<int64_t>(value); +} +template <> +uint32_t SaveManager::deserialize(const string & value) const noexcept { +	return deserialize<uint64_t>(value); +} +template <> +int32_t SaveManager::deserialize(const string & value) const noexcept { +	return deserialize<int64_t>(value);  } +SaveManager::SaveManager() { dbg_trace(); } +  SaveManager & SaveManager::get_instance() {  	dbg_trace();  	static SaveManager instance; @@ -118,17 +134,19 @@ template void SaveManager::set(const string &, const double &);  template <typename T>  ValueBroker<T> SaveManager::get(const string & key, const T & default_value) { -	if (!this->has(key)) -		this->set<T>(key, default_value); +	if (!this->has(key)) this->set<T>(key, default_value);  	return this->get<T>(key);  }  template ValueBroker<uint8_t> SaveManager::get(const string &, const uint8_t &);  template ValueBroker<int8_t> SaveManager::get(const string &, const int8_t &); -template ValueBroker<uint16_t> SaveManager::get(const string &, const uint16_t &); +template ValueBroker<uint16_t> SaveManager::get(const string &, +												const uint16_t &);  template ValueBroker<int16_t> SaveManager::get(const string &, const int16_t &); -template ValueBroker<uint32_t> SaveManager::get(const string &, const uint32_t &); +template ValueBroker<uint32_t> SaveManager::get(const string &, +												const uint32_t &);  template ValueBroker<int32_t> SaveManager::get(const string &, const int32_t &); -template ValueBroker<uint64_t> SaveManager::get(const string &, const uint64_t &); +template ValueBroker<uint64_t> SaveManager::get(const string &, +												const uint64_t &);  template ValueBroker<int64_t> SaveManager::get(const string &, const int64_t &);  template ValueBroker<float> SaveManager::get(const string &, const float &);  template ValueBroker<double> SaveManager::get(const string &, const double &); @@ -138,8 +156,8 @@ template <typename T>  ValueBroker<T> SaveManager::get(const string & key) {  	T value;  	return { -		[this, key] (const T & target) { this->set<T>(key, target); }, -		[this, key, value] () mutable -> const T & { +		[this, key](const T & target) { this->set<T>(key, target); }, +		[this, key, value]() mutable -> const T & {  			value = this->deserialize<T>(this->get_db().get(key));  			return value;  		}, @@ -156,4 +174,3 @@ template ValueBroker<int64_t> SaveManager::get(const string &);  template ValueBroker<float> SaveManager::get(const string &);  template ValueBroker<double> SaveManager::get(const string &);  template ValueBroker<string> SaveManager::get(const string &); - diff --git a/src/crepe/api/SaveManager.h b/src/crepe/api/SaveManager.h index 3073656..4be85fb 100644 --- a/src/crepe/api/SaveManager.h +++ b/src/crepe/api/SaveManager.h @@ -93,8 +93,8 @@ public:  	static SaveManager & get_instance();  	SaveManager(const SaveManager &) = delete;  	SaveManager(SaveManager &&) = delete; -	SaveManager & operator = (const SaveManager &) = delete; -	SaveManager & operator = (SaveManager &&) = delete; +	SaveManager & operator=(const SaveManager &) = delete; +	SaveManager & operator=(SaveManager &&) = delete;  private:  	/** @@ -110,5 +110,4 @@ private:  	static DB & get_db();  }; -} - +} // namespace crepe diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp index d3465c7..6f0433f 100644 --- a/src/crepe/api/Sprite.cpp +++ b/src/crepe/api/Sprite.cpp @@ -1,7 +1,7 @@ -#include <cstdint>  #include <memory>  #include "../util/log.h" +#include "facade/SDLContext.h"  #include "Component.h"  #include "Sprite.h" @@ -10,10 +10,16 @@  using namespace std;  using namespace crepe; -Sprite::Sprite(game_object_id_t id, shared_ptr<Texture> image, +Sprite::Sprite(game_object_id_t id, const shared_ptr<Texture> image,  			   const Color & color, const FlipSettings & flip) -	: Component(id), color(color), flip(flip), sprite_image(image) { +	: Component(id), +	  color(color), +	  flip(flip), +	  sprite_image(image) {  	dbg_trace(); + +	this->sprite_rect.w = sprite_image->get_width(); +	this->sprite_rect.h = sprite_image->get_height();  }  Sprite::~Sprite() { dbg_trace(); } diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h index 00dcb27..deb3f93 100644 --- a/src/crepe/api/Sprite.h +++ b/src/crepe/api/Sprite.h @@ -1,32 +1,88 @@  #pragma once -#include <SDL2/SDL_rect.h>  #include <cstdint>  #include <memory> -#include "api/Color.h" -#include "api/Texture.h" - +#include "Color.h"  #include "Component.h" +#include "Texture.h"  namespace crepe { +struct Rect { +	int w = 0; +	int h = 0; +	int x = 0; +	int y = 0; +}; +  struct FlipSettings { -	bool flip_x = true; -	bool flip_y = true; +	bool flip_x = false; +	bool flip_y = false;  }; +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: -	Sprite(game_object_id_t id, std::shared_ptr<Texture> image, +	// TODO: Loek comment in github #27 will be looked another time +	// about shared_ptr Texture +	/** +	 * \brief Constructs a Sprite with specified parameters. +	 * \param game_id Unique identifier for the game object this sprite belongs to. +	 * \param image Shared pointer to the texture for this sprite. +	 * \param color Color tint applied to the sprite. +	 * \param flip Flip settings for horizontal and vertical orientation. +	 */ +	Sprite(game_object_id_t id, const std::shared_ptr<Texture> image,  		   const Color & color, const FlipSettings & flip); + +	/** +	 * \brief Destroys the Sprite instance. +	 */  	~Sprite(); -	std::shared_ptr<Texture> sprite_image; + +	//! Texture used for the sprite +	const std::shared_ptr<Texture> sprite_image; +	//! Color tint of the sprite  	Color color; +	//! Flip settings for the sprite  	FlipSettings flip; -	uint8_t sorting_in_layer; -	uint8_t order_in_layer; +	//! Layer sorting level of the sprite +	uint8_t sorting_in_layer = 0; +	//! Order within the sorting layer +	uint8_t order_in_layer = 0; + +public: +	/** +	 * \brief Gets the maximum number of instances allowed for this sprite. +	 * \return Maximum instance count as an integer. +	 * +	 * For now is this number randomly picked. I think it will eventually be 1.  +	 */ +	virtual int get_instances_max() const { return 10; } + +private: +	//! Reads the sprite_rect of sprite +	friend class SDLContext; + +	//! Reads the all the variables plus the  sprite_rect +	friend class Animator; + +	//! Reads the all the variables plus the  sprite_rect +	friend class AnimatorSystem; + +	//! Render area of the sprite this will also be adjusted by the AnimatorSystem if an Animator object is present in GameObject +	Rect sprite_rect;  };  } // namespace crepe diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp index 8fc5c13..5ebd23d 100644 --- a/src/crepe/api/Texture.cpp +++ b/src/crepe/api/Texture.cpp @@ -21,12 +21,19 @@ Texture::Texture(const char * src) {  Texture::~Texture() {  	dbg_trace(); -	if (this->texture != nullptr) { -		SDL_DestroyTexture(this->texture); -	} +	this->texture.reset();  }  void Texture::load(unique_ptr<Asset> res) {  	SDLContext & ctx = SDLContext::get_instance(); -	this->texture = ctx.texture_from_path(res->canonical()); +	this->texture = std::move(ctx.texture_from_path(res->canonical())); +} + +int Texture::get_width() const { +	if (this->texture == nullptr) return 0; +	return SDLContext::get_instance().get_width(*this); +} +int Texture::get_height() const { +	if (this->texture == nullptr) return 0; +	return SDLContext::get_instance().get_width(*this);  } diff --git a/src/crepe/api/Texture.h b/src/crepe/api/Texture.h index 9a86f6f..b89bc17 100644 --- a/src/crepe/api/Texture.h +++ b/src/crepe/api/Texture.h @@ -3,31 +3,75 @@  // FIXME: this header can't be included because this is an API header, and SDL2  // development headers won't be bundled with crepe. Why is this facade in the  // API namespace? +  #include <SDL2/SDL_render.h> +#include <functional>  #include <memory>  #include "Asset.h"  namespace crepe { -class SDLContext; -} -namespace crepe { +class SDLContext; +class Animator; +/** + * \class Texture + * \brief Manages texture loading and properties. + * + * The Texture class is responsible for loading an image from a source + * and providing access to its dimensions. Textures can be used for rendering. + */  class Texture {  public: +	/** +	 * \brief Constructs a Texture from a file path. +	 * \param src Path to the image file to be loaded as a texture. +	 */  	Texture(const char * src); + +	/** +	 * \brief Constructs a Texture from an Asset resource. +	 * \param res Unique pointer to an Asset resource containing texture data. +	 */  	Texture(std::unique_ptr<Asset> res); + +	/** +	 * \brief Destroys the Texture instance, freeing associated resources. +	 */  	~Texture(); +	// FIXME: this constructor shouldn't be necessary because this class doesn't +	// manage memory + +	/** +	 * \brief Gets the width of the texture. +	 * \return Width of the texture in pixels. +	 */ +	int get_width() const; + +	/** +	 * \brief Gets the height of the texture. +	 * \return Height of the texture in pixels. +	 */ +	int get_height() const;  private: +	/** +	 * \brief Loads the texture from an Asset resource. +	 * \param res Unique pointer to an Asset resource to load the texture from. +	 */  	void load(std::unique_ptr<Asset> res);  private: -	SDL_Texture * texture = nullptr; +	//! The texture of the class from the library +	std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> texture; + +	//! Grants SDLContext access to private members. +	friend class SDLContext; -	friend class crepe::SDLContext; +	//! Grants Animator access to private members. +	friend class Animator;  };  } // namespace crepe diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp index a244bc5..e401120 100644 --- a/src/crepe/api/Transform.cpp +++ b/src/crepe/api/Transform.cpp @@ -6,6 +6,9 @@ using namespace crepe;  Transform::Transform(game_object_id_t id, const Vector2 & point,  					 double rotation, double scale) -	: Component(id), position(point), rotation(rotation), scale(scale) { +	: Component(id), +	  position(point), +	  rotation(rotation), +	  scale(scale) {  	dbg_trace();  } diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h index d7a5b8a..756e45b 100644 --- a/src/crepe/api/Transform.h +++ b/src/crepe/api/Transform.h @@ -32,7 +32,7 @@ public:  public:  	//! Translation (shift)  	Vector2 position; -	//! Rotation, in radians +	//! Rotation, in degrees  	double rotation;  	//! Multiplication factor  	double scale; diff --git a/src/crepe/api/Vector2.cpp b/src/crepe/api/Vector2.cpp index 09bb59b..09b3fa3 100644 --- a/src/crepe/api/Vector2.cpp +++ b/src/crepe/api/Vector2.cpp @@ -3,7 +3,7 @@  namespace crepe {  // Constructor with initial values -Vector2::Vector2(float x, float y) : x(x), y(y) {} +Vector2::Vector2(double x, double y) : x(x), y(y) {}  // Subtracts another vector from this vector and returns the result.  Vector2 Vector2::operator-(const Vector2 & other) const { @@ -16,7 +16,7 @@ Vector2 Vector2::operator+(const Vector2 & other) const {  }  // Multiplies this vector by a scalar and returns the result. -Vector2 Vector2::operator*(float scalar) const { +Vector2 Vector2::operator*(double scalar) const {  	return {x * scalar, y * scalar};  } @@ -35,7 +35,7 @@ Vector2 & Vector2::operator+=(const Vector2 & other) {  }  // Adds a scalar value to both components of this vector and updates this vector. -Vector2 & Vector2::operator+=(float other) { +Vector2 & Vector2::operator+=(double other) {  	x += other;  	y += other;  	return *this; diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h index 741951b..5a57484 100644 --- a/src/crepe/api/Vector2.h +++ b/src/crepe/api/Vector2.h @@ -6,15 +6,15 @@ namespace crepe {  class Vector2 {  public:  	//! X component of the vector -	float x; +	double x;  	//! Y component of the vector -	float y; +	double y;  	//! Default constructor  	Vector2() = default;  	//! Constructor with initial values -	Vector2(float x, float y); +	Vector2(double x, double y);  	//! Subtracts another vector from this vector and returns the result.  	Vector2 operator-(const Vector2 & other) const; @@ -23,7 +23,7 @@ public:  	Vector2 operator+(const Vector2 & other) const;  	//! Multiplies this vector by a scalar and returns the result. -	Vector2 operator*(float scalar) const; +	Vector2 operator*(double scalar) const;  	//! Multiplies this vector by another vector element-wise and updates this vector.  	Vector2 & operator*=(const Vector2 & other); @@ -32,7 +32,7 @@ public:  	Vector2 & operator+=(const Vector2 & other);  	//! Adds a scalar value to both components of this vector and updates this vector. -	Vector2 & operator+=(float other); +	Vector2 & operator+=(double other);  	//! Returns the negation of this vector.  	Vector2 operator-() const; diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp index c885560..0a2f455 100644 --- a/src/crepe/facade/DB.cpp +++ b/src/crepe/facade/DB.cpp @@ -1,7 +1,7 @@  #include <cstring> -#include "util/log.h"  #include "Exception.h" +#include "util/log.h"  #include "DB.h" @@ -16,20 +16,21 @@ DB::DB(const string & path) {  	libdb::DB * db;  	if ((ret = libdb::db_create(&db, NULL, 0)) != 0)  		throw Exception("db_create: %s", libdb::db_strerror(ret)); -	this->db = { db, [] (libdb::DB * db) { db->close(db, 0); } }; +	this->db = {db, [](libdb::DB * db) { db->close(db, 0); }};  	// load or create database file -	if ((ret = this->db->open(this->db.get(), NULL, path.c_str(), NULL, libdb::DB_BTREE, DB_CREATE, 0)) != 0) +	if ((ret = this->db->open(this->db.get(), NULL, path.c_str(), NULL, +							  libdb::DB_BTREE, DB_CREATE, 0)) +		!= 0)  		throw Exception("db->open: %s", libdb::db_strerror(ret));  	// create cursor  	libdb::DBC * cursor;  	if ((ret = this->db->cursor(this->db.get(), NULL, &cursor, 0)) != 0)  		throw Exception("db->cursor: %s", libdb::db_strerror(ret)); -	this->cursor = { cursor, [] (libdb::DBC * cursor) { cursor->close(cursor); } }; +	this->cursor = {cursor, [](libdb::DBC * cursor) { cursor->close(cursor); }};  } -  libdb::DBT DB::to_thing(const string & thing) const noexcept {  	libdb::DBT thang;  	memset(&thang, 0, sizeof(libdb::DBT)); @@ -44,17 +45,15 @@ string DB::get(const string & key) {  	memset(&db_val, 0, sizeof(libdb::DBT));  	int ret = this->cursor->get(this->cursor.get(), &db_key, &db_val, DB_FIRST); -	if (ret != 0) -		throw Exception("cursor->get: %s", libdb::db_strerror(ret)); -	return { static_cast<char *>(db_val.data), db_val.size }; +	if (ret != 0) throw Exception("cursor->get: %s", libdb::db_strerror(ret)); +	return {static_cast<char *>(db_val.data), db_val.size};  }  void DB::set(const string & key, const string & value) {  	libdb::DBT db_key = this->to_thing(key);  	libdb::DBT db_val = this->to_thing(value);  	int ret = this->db->put(this->db.get(), NULL, &db_key, &db_val, 0); -	if (ret != 0) -		throw Exception("cursor->get: %s", libdb::db_strerror(ret)); +	if (ret != 0) throw Exception("cursor->get: %s", libdb::db_strerror(ret));  }  bool DB::has(const std::string & key) noexcept { @@ -65,4 +64,3 @@ bool DB::has(const std::string & key) noexcept {  	}  	return true;  } - diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h index b62a974..7c757a2 100644 --- a/src/crepe/facade/DB.h +++ b/src/crepe/facade/DB.h @@ -1,14 +1,14 @@  #pragma once -#include <string>  #include <functional>  #include <memory> +#include <string>  namespace libdb {  extern "C" {  #include <db.h>  } -} +} // namespace libdb  namespace crepe { @@ -71,5 +71,4 @@ private:  	libdb::DBT to_thing(const std::string & thing) const noexcept;  }; -} - +} // namespace crepe diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 8da93e9..236bf8c 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -1,16 +1,22 @@  #include <SDL2/SDL.h>  #include <SDL2/SDL_image.h> +#include <SDL2/SDL_rect.h>  #include <SDL2/SDL_render.h>  #include <SDL2/SDL_surface.h>  #include <SDL2/SDL_video.h>  #include <cmath>  #include <cstddef> +#include <functional>  #include <iostream> +#include <memory> +#include <string> +#include <utility>  #include "../api/Sprite.h"  #include "../api/Texture.h"  #include "../api/Transform.h"  #include "../util/log.h" +#include "Exception.h"  #include "SDLContext.h" @@ -21,34 +27,6 @@ SDLContext & SDLContext::get_instance() {  	return instance;  } -void SDLContext::handle_events(bool & running) { -	SDL_Event event; -	while (SDL_PollEvent(&event)) { -		if (event.type == SDL_QUIT) { -			running = false; -		} -	} -} - -SDLContext::~SDLContext() { -	dbg_trace(); - -	if (this->game_renderer != nullptr) -		SDL_DestroyRenderer(this->game_renderer); - -	if (this->game_window != nullptr) { -		SDL_DestroyWindow(this->game_window); -	} - -	// TODO: how are we going to ensure that these are called from the same -	// thread that SDL_Init() was called on? This has caused problems for me -	// before. -	IMG_Quit(); -	SDL_Quit(); -} - -void SDLContext::clear_screen() { SDL_RenderClear(this->game_renderer); } -  SDLContext::SDLContext() {  	dbg_trace();  	// FIXME: read window defaults from config manager @@ -59,26 +37,32 @@ SDLContext::SDLContext() {  				  << std::endl;  		return;  	} - -	this->game_window = SDL_CreateWindow( +	SDL_Window * tmp_window = SDL_CreateWindow(  		"Crepe Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, -		1920, 1080, SDL_WINDOW_SHOWN); -	if (!this->game_window) { +		this->viewport.w, this->viewport.h, 0); +	if (!tmp_window) {  		// FIXME: throw exception  		std::cerr << "Window could not be created! SDL_Error: "  				  << SDL_GetError() << std::endl; +		return;  	} +	this->game_window +		= {tmp_window, [](SDL_Window * window) { SDL_DestroyWindow(window); }}; -	this->game_renderer -		= SDL_CreateRenderer(this->game_window, -1, SDL_RENDERER_ACCELERATED); -	if (!this->game_renderer) { +	SDL_Renderer * tmp_renderer = SDL_CreateRenderer( +		this->game_window.get(), -1, SDL_RENDERER_ACCELERATED); +	if (!tmp_renderer) {  		// FIXME: throw exception  		std::cerr << "Renderer could not be created! SDL_Error: "  				  << SDL_GetError() << std::endl; -		SDL_DestroyWindow(this->game_window); +		SDL_DestroyWindow(this->game_window.get());  		return;  	} +	this->game_renderer = {tmp_renderer, [](SDL_Renderer * renderer) { +							   SDL_DestroyRenderer(renderer); +						   }}; +  	int img_flags = IMG_INIT_PNG;  	if (!(IMG_Init(img_flags) & img_flags)) {  		// FIXME: throw exception @@ -87,71 +71,124 @@ SDLContext::SDLContext() {  	}  } -void SDLContext::present_screen() { SDL_RenderPresent(this->game_renderer); } +SDLContext::~SDLContext() { +	dbg_trace(); -void SDLContext::draw(const Sprite & sprite, const Transform & transform) { +	this->game_renderer.reset(); +	this->game_window.reset(); -	static SDL_RendererFlip render_flip +	// TODO: how are we going to ensure that these are called from the same +	// thread that SDL_Init() was called on? This has caused problems for me +	// before. +	IMG_Quit(); +	SDL_Quit(); +} + +void SDLContext::handle_events(bool & running) { +	//TODO: wouter i need events +	/* +	SDL_Event event; +	SDL_PollEvent(&event); +	switch (event.type) { +		case SDL_QUIT: +			running = false; +			break; +		case SDL_KEYDOWN: +			triggerEvent(KeyPressedEvent(getCustomKey(event.key.keysym.sym))); +			break; +		case SDL_MOUSEBUTTONDOWN: +			int x, y; +			SDL_GetMouseState(&x, &y); +			triggerEvent(MousePressedEvent(x, y)); +			break; +	} +	*/ +} + +void SDLContext::clear_screen() { SDL_RenderClear(this->game_renderer.get()); } +void SDLContext::present_screen() { +	SDL_RenderPresent(this->game_renderer.get()); +} + +void SDLContext::draw(const Sprite & sprite, const Transform & transform, +					  const Camera & cam) { + +	SDL_RendererFlip render_flip  		= (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x)  							  | (SDL_FLIP_VERTICAL * sprite.flip.flip_y)); -	int w, h; -	SDL_QueryTexture(sprite.sprite_image->texture, NULL, NULL, &w, &h); -	// needs maybe camera for position +	double adjusted_x = (transform.position.x - cam.x) * cam.zoom; +	double adjusted_y = (transform.position.y - cam.y) * cam.zoom; +	double adjusted_w = sprite.sprite_rect.w * transform.scale * cam.zoom; +	double adjusted_h = sprite.sprite_rect.h * transform.scale * cam.zoom; + +	SDL_Rect srcrect = { +		.x = sprite.sprite_rect.x, +		.y = sprite.sprite_rect.y, +		.w = sprite.sprite_rect.w, +		.h = sprite.sprite_rect.h, +	}; +  	SDL_Rect dstrect = { -		.x = static_cast<int>(transform.position.x), -		.y = static_cast<int>(transform.position.y), -		.w = static_cast<int>(w * transform.scale), -		.h = static_cast<int>(h * transform.scale), +		.x = static_cast<int>(adjusted_x), +		.y = static_cast<int>(adjusted_y), +		.w = static_cast<int>(adjusted_w), +		.h = static_cast<int>(adjusted_h),  	}; -	double degrees = transform.rotation * 180 / M_PI; -	SDL_RenderCopyEx(this->game_renderer, sprite.sprite_image->texture, NULL, -					 &dstrect, degrees, NULL, render_flip); +	SDL_RenderCopyEx(this->game_renderer.get(), +					 sprite.sprite_image->texture.get(), &srcrect, + +					 &dstrect, transform.rotation, NULL, render_flip);  } -/* -SDL_Texture * SDLContext::setTextureFromPath(const char * path, SDL_Rect & clip, -											 const int row, const int col) { -	dbg_trace(); +void SDLContext::camera(const Camera & cam) { +	this->viewport.w = static_cast<int>(cam.aspect_width); +	this->viewport.h = static_cast<int>(cam.aspect_height); +	this->viewport.x = static_cast<int>(cam.x) - (SCREEN_WIDTH / 2); +	this->viewport.y = static_cast<int>(cam.y) - (SCREEN_HEIGHT / 2); -	SDL_Surface * tmp = IMG_Load(path); -	if (!tmp) { -		std::cerr << "Error surface " << IMG_GetError << std::endl; -	} +	SDL_SetRenderDrawColor(this->game_renderer.get(), cam.bg_color.r, +						   cam.bg_color.g, cam.bg_color.b, cam.bg_color.a); +} -	clip. -		w = tmp->w / col; -	clip.h = tmp->h / row; +uint64_t SDLContext::get_ticks() const { return SDL_GetTicks64(); } -	SDL_Texture * CreatedTexture -		= SDL_CreateTextureFromSurface(this->game_renderer, tmp); +std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> +SDLContext::texture_from_path(const std::string & path) { -	if (!CreatedTexture) { -		std::cerr << "Error could not create texture " << IMG_GetError -				  << std::endl; +	SDL_Surface * tmp = IMG_Load(path.c_str()); +	if (tmp == nullptr) { +		tmp = IMG_Load("../asset/texture/ERROR.png");  	} -	SDL_FreeSurface(tmp); -	return CreatedTexture; -} -*/ +	std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> +		img_surface; +	img_surface +		= {tmp, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }}; -SDL_Texture * SDLContext::texture_from_path(const char * path) { -	dbg_trace(); +	SDL_Texture * tmp_texture = SDL_CreateTextureFromSurface( +		this->game_renderer.get(), img_surface.get()); -	SDL_Surface * tmp = IMG_Load(path); -	if (!tmp) { -		std::cerr << "Error surface " << IMG_GetError << std::endl; +	if (tmp_texture == nullptr) { +		throw Exception("Texture cannot be load from %s", path.c_str());  	} -	SDL_Texture * created_texture -		= SDL_CreateTextureFromSurface(this->game_renderer, tmp); -	if (!created_texture) { -		std::cerr << "Error could not create texture " << IMG_GetError -				  << std::endl; -	} -	SDL_FreeSurface(tmp); +	std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> +		img_texture; +	img_texture = {tmp_texture, +				   [](SDL_Texture * texture) { SDL_DestroyTexture(texture); }}; -	return created_texture; +	return img_texture; +} +int SDLContext::get_width(const Texture & ctx) const { +	int w; +	SDL_QueryTexture(ctx.texture.get(), NULL, NULL, &w, NULL); +	return w; +} +int SDLContext::get_height(const Texture & ctx) const { +	int h; +	SDL_QueryTexture(ctx.texture.get(), NULL, NULL, NULL, &h); +	return h;  } +void SDLContext::delay(int ms) const { SDL_Delay(ms); } diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index f1ba8a6..536dec5 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -1,48 +1,157 @@  #pragma once +#include <SDL2/SDL_keycode.h>  #include <SDL2/SDL_render.h>  #include <SDL2/SDL_video.h> +#include <functional> +#include <memory> +#include <string>  #include "../api/Sprite.h"  #include "../api/Transform.h" -#include "../system/RenderSystem.h" +#include "api/Camera.h" + +// FIXME: this needs to be removed +const int SCREEN_WIDTH = 640; +const int SCREEN_HEIGHT = 480;  namespace crepe { +// TODO: SDL_Keycode is defined in a header not distributed with crepe, which +// means this typedef is unusable when crepe is packaged. Wouter will fix this +// later. +typedef SDL_Keycode CREPE_KEYCODES; +  class Texture; +class LoopManager; + +/** + * \class SDLContext + * \brief Facade for the SDL library + *  + * SDLContext is a singleton that handles the SDL window and renderer, provides methods + * for event handling, and rendering to the screen. It is never used directly by the user + */  class SDLContext {  public: -	// singleton +	/** +	 * \brief Gets the singleton instance of SDLContext. +	 * \return Reference to the SDLContext instance. +	 */  	static SDLContext & get_instance(); +  	SDLContext(const SDLContext &) = delete;  	SDLContext(SDLContext &&) = delete;  	SDLContext & operator=(const SDLContext &) = delete;  	SDLContext & operator=(SDLContext &&) = delete; -	//TODO decide events wouter? -  private: +	//! will only use handle_events +	friend class LoopManager; +	/** +	 * \brief Handles SDL events such as window close and input. +	 * \param running Reference to a boolean flag that controls the main loop. +	 */  	void handle_events(bool & running);  private: +	//! Will only use get_ticks +	friend class AnimatorSystem; +	//! Will only use delay +	friend class LoopTimer; +	/** +	 * \brief Gets the current SDL ticks since the program started. +	 * \return Current ticks in milliseconds as a constant uint64_t. +	 */ +	uint64_t get_ticks() const; +	/** +	 * \brief Pauses the execution for a specified duration. +	 * +	 * This function uses SDL's delay function to halt the program execution +	 * for a given number of milliseconds, allowing for frame rate control +	 * or other timing-related functionality. +	 * +	 * \param ms Duration of the delay in milliseconds. +	 */ +	void delay(int ms) const; + +private: +	/** +	 * \brief Constructs an SDLContext instance. +	 * Initializes SDL, creates a window and renderer. +	 */  	SDLContext(); -	virtual ~SDLContext(); + +	/** +	 * \brief Destroys the SDLContext instance. +	 * Cleans up SDL resources, including the window and renderer. +	 */ +	~SDLContext();  private: +	//! Will use the funtions: texture_from_path, get_width,get_height.  	friend class Texture; -	SDL_Texture * texture_from_path(const char *); -	//SDL_Texture* setTextureFromPath(const char*, SDL_Rect& clip, const int row, const int col); + +	//! Will use the funtions: texture_from_path, get_width,get_height. +	friend class Animator; + +	/** +	 * \brief Loads a texture from a file path. +	 * \param path Path to the image file. +	 * \return Pointer to the created SDL_Texture. +	 */ +	std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> +	texture_from_path(const std::string & path); +	/** +	 * \brief Gets the width of a texture. +	 * \param texture Reference to the Texture object. +	 * \return Width of the texture as an integer. +	 */ +	int get_width(const Texture &) const; + +	/** +	 * \brief Gets the height of a texture. +	 * \param texture Reference to the Texture object. +	 * \return Height of the texture as an integer. +	 */ +	int get_height(const Texture &) const;  private: +	//! Will use draw,clear_screen, present_screen, camera.  	friend class RenderSystem; -	void draw(const Sprite &, const Transform &); + +	/** +	 * \brief Draws a sprite to the screen using the specified transform and camera. +	 * \param sprite Reference to the Sprite to draw. +	 * \param transform Reference to the Transform for positioning. +	 * \param camera Reference to the Camera for view adjustments. +	 */ +	void draw(const Sprite & sprite, const Transform & transform, +			  const Camera & camera); + +	//! Clears the screen, preparing for a new frame.  	void clear_screen(); + +	//! Presents the rendered frame to the screen.  	void present_screen(); +	/** +	 * \brief Sets the current camera for rendering. +	 * \param camera Reference to the Camera object. +	 */ +	void camera(const Camera & camera); +  private: -	SDL_Window * game_window = nullptr; -	SDL_Renderer * game_renderer = nullptr; +	//! sdl Window +	std::unique_ptr<SDL_Window, std::function<void(SDL_Window *)>> game_window; + +	//! renderer for the crepe engine +	std::unique_ptr<SDL_Renderer, std::function<void(SDL_Renderer *)>> +		game_renderer; + +	//! viewport for the camera window +	SDL_Rect viewport = {0, 0, 640, 480};  };  } // namespace crepe diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp new file mode 100644 index 0000000..bf45362 --- /dev/null +++ b/src/crepe/system/AnimatorSystem.cpp @@ -0,0 +1,37 @@ + +#include <cstdint> +#include <functional> +#include <vector> + +#include "api/Animator.h" +#include "facade/SDLContext.h" +#include "util/log.h" + +#include "AnimatorSystem.h" +#include "ComponentManager.h" + +using namespace crepe; + +AnimatorSystem::AnimatorSystem() { dbg_trace(); } +AnimatorSystem::~AnimatorSystem() { dbg_trace(); } + +AnimatorSystem & AnimatorSystem::get_instance() { +	static AnimatorSystem instance; +	return instance; +} + +void AnimatorSystem::update() { +	ComponentManager & mgr = ComponentManager::get_instance(); + +	std::vector<std::reference_wrapper<Animator>> animations +		= mgr.get_components_by_type<Animator>(); + +	uint64_t tick = SDLContext::get_instance().get_ticks(); +	for (Animator & a : animations) { +		if (a.active) { +			a.curr_row = (tick / 100) % a.row; +			a.animator_rect.x = (a.curr_row * a.animator_rect.w) + a.curr_col; +			a.spritesheet.sprite_rect = a.animator_rect; +		} +	} +} diff --git a/src/crepe/system/AnimatorSystem.h b/src/crepe/system/AnimatorSystem.h new file mode 100644 index 0000000..969e9d1 --- /dev/null +++ b/src/crepe/system/AnimatorSystem.h @@ -0,0 +1,44 @@ +#pragma once + +#include "System.h" + +//TODO: +// control if flip works with animation system + +namespace crepe { + +/** + * \brief The AnimatorSystem is responsible for managing and updating all Animator components. + * + * This system is responsible for controlling the behavior of the animations for all entities + * that have the Animator component attached. It updates the animations by controlling their + * frame changes, looping behavior, and overall animation state. + */ +class AnimatorSystem : public System { + +public: +	/** +	 * \brief Retrieves the singleton instance of the AnimatorSystem. +	 * +	 * \return A reference to the single instance of the AnimatorSystem. +	 * +	 * This method ensures that there is only one instance of the AnimatorSystem, following the +	 * singleton design pattern. It can be used to access the system globally. +	 */ +	static AnimatorSystem & get_instance(); + +	/** +	 * \brief Updates the Animator components. +	 * +	 * This method is called periodically (likely every frame) to update the state of all +	 * Animator components, moving the animations forward and managing their behavior (e.g., looping). +	 */ +	void update() override; + +private: +	// private because singleton +	AnimatorSystem(); // dbg_trace +	~AnimatorSystem(); // dbg_trace +}; + +} // namespace crepe diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index 93c0955..9fdd7eb 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -1,5 +1,3 @@ -#pragma once -  #include "AudioSystem.h"  #include "ComponentManager.h" diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index 8fb681f..e037f51 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -8,13 +8,11 @@ namespace crepe {  class AudioSystem : public System {  public: -	AudioSystem(SoundContext & ctx); - -public: -	void update(); +	using System::System; +	void update() override;  private: -	SoundContext & ctx; +	SoundContext context {};  };  } // namespace crepe diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt index ca62add..f507b90 100644 --- a/src/crepe/system/CMakeLists.txt +++ b/src/crepe/system/CMakeLists.txt @@ -6,6 +6,7 @@ target_sources(crepe PUBLIC  	CollisionSystem.cpp  	RenderSystem.cpp  	AudioSystem.cpp +	AnimatorSystem.cpp  )  target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -15,4 +16,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	CollisionSystem.h  	RenderSystem.h  	AudioSystem.h +	AnimatorSystem.h  ) diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp index 397b586..e7a3bec 100644 --- a/src/crepe/system/ParticleSystem.cpp +++ b/src/crepe/system/ParticleSystem.cpp @@ -1,62 +1,141 @@  #include <cmath> +#include <cstdlib>  #include <ctime> -#include "../ComponentManager.h" -#include "../api/ParticleEmitter.h" +#include "api/ParticleEmitter.h" +#include "api/Transform.h" +#include "api/Vector2.h" +#include "ComponentManager.h"  #include "ParticleSystem.h"  using namespace crepe; -ParticleSystem::ParticleSystem() : elapsed_time(0.0f) {} -  void ParticleSystem::update() { +	// Get all emitters  	ComponentManager & mgr = ComponentManager::get_instance();  	std::vector<std::reference_wrapper<ParticleEmitter>> emitters  		= mgr.get_components_by_type<ParticleEmitter>(); -	float delta_time = 0.10; +  	for (ParticleEmitter & emitter : emitters) { -		float update_amount = 1 / static_cast<float>(emitter.emission_rate); -		for (float i = 0; i < delta_time; i += update_amount) { -			emit_particle(emitter); +		// Get transform linked to emitter +		const Transform & transform +			= mgr.get_components_by_id<Transform>(emitter.game_object_id) +				  .front() +				  .get(); + +		// Emit particles based on emission_rate +		int updates +			= calculate_update(this->update_count, emitter.data.emission_rate); +		for (size_t i = 0; i < updates; i++) { +			emit_particle(emitter, transform);  		} -		for (size_t j = 0; j < emitter.particles.size(); j++) { -			if (emitter.particles[j].active) { -				emitter.particles[j].update(delta_time); + +		// Update all particles +		for (Particle & particle : emitter.data.particles) { +			if (particle.active) { +				particle.update();  			}  		} + +		// Check if within boundary +		check_bounds(emitter, transform);  	} + +	this->update_count = (this->update_count + 1) % this->MAX_UPDATE_COUNT;  } -void ParticleSystem::emit_particle(ParticleEmitter & emitter) { -	Position initial_position = {emitter.position.x, emitter.position.y}; -	float random_angle = 0.0f; -	if (emitter.max_angle < emitter.min_angle) { -		random_angle = ((emitter.min_angle -						 + (std::rand() -							% (static_cast<uint32_t>(emitter.max_angle + 360 -													 - emitter.min_angle + 1)))) -						% 360); -	} else { -		random_angle = emitter.min_angle -					   + (std::rand() -						  % (static_cast<uint32_t>(emitter.max_angle -												   - emitter.min_angle + 1))); -	} -	float angle_in_radians = random_angle * (M_PI / 180.0f); -	float random_speed_offset = (static_cast<float>(std::rand()) / RAND_MAX) -									* (2 * emitter.speed_offset) -								- emitter.speed_offset; -	float velocity_x -		= (emitter.speed + random_speed_offset) * std::cos(angle_in_radians); -	float velocity_y -		= (emitter.speed + random_speed_offset) * std::sin(angle_in_radians); -	Position initial_velocity = {velocity_x, velocity_y}; -	for (size_t i = 0; i < emitter.particles.size(); i++) { -		if (!emitter.particles[i].active) { -			emitter.particles[i].reset(emitter.end_lifespan, initial_position, -									   initial_velocity); +void ParticleSystem::emit_particle(ParticleEmitter & emitter, +								   const Transform & transform) { +	constexpr double DEG_TO_RAD = M_PI / 180.0; + +	Vector2 initial_position = emitter.data.position + transform.position; +	double random_angle +		= generate_random_angle(emitter.data.min_angle, emitter.data.max_angle); + +	double random_speed +		= generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); +	double angle_radians = random_angle * DEG_TO_RAD; + +	Vector2 velocity = {random_speed * std::cos(angle_radians), +						random_speed * std::sin(angle_radians)}; + +	for (Particle & particle : emitter.data.particles) { +		if (!particle.active) { +			particle.reset(emitter.data.end_lifespan, initial_position, +						   velocity, random_angle);  			break;  		}  	}  } + +int ParticleSystem::calculate_update(int count, double emission) const { +	double integer_part = std::floor(emission); +	double fractional_part = emission - integer_part; + +	if (fractional_part > 0) { +		int denominator = static_cast<int>(1.0 / fractional_part); +		return (count % denominator == 0) ? 1 : 0; +	} + +	return static_cast<int>(emission); +} + +void ParticleSystem::check_bounds(ParticleEmitter & emitter, +								  const Transform & transform) { +	Vector2 offset = emitter.data.boundary.offset + transform.position +					 + emitter.data.position; +	double half_width = emitter.data.boundary.width / 2.0; +	double half_height = emitter.data.boundary.height / 2.0; + +	const double LEFT = offset.x - half_width; +	const double RIGHT = offset.x + half_width; +	const double TOP = offset.y - half_height; +	const double BOTTOM = offset.y + half_height; + +	for (Particle & particle : emitter.data.particles) { +		const Vector2 & position = particle.position; +		bool within_bounds = (position.x >= LEFT && position.x <= RIGHT +							  && position.y >= TOP && position.y <= BOTTOM); + +		if (!within_bounds) { +			if (emitter.data.boundary.reset_on_exit) { +				particle.active = false; +			} else { +				particle.velocity = {0, 0}; +				if (position.x < LEFT) particle.position.x = LEFT; +				else if (position.x > RIGHT) particle.position.x = RIGHT; +				if (position.y < TOP) particle.position.y = TOP; +				else if (position.y > BOTTOM) particle.position.y = BOTTOM; +			} +		} +	} +} + +double ParticleSystem::generate_random_angle(double min_angle, +											 double max_angle) const { +	if (min_angle == max_angle) { +		return min_angle; +	} else if (min_angle < max_angle) { +		return min_angle +			   + static_cast<double>(std::rand() +									 % static_cast<int>(max_angle - min_angle)); +	} else { +		double angle_offset = (360 - min_angle) + max_angle; +		double random_angle = min_angle +							  + static_cast<double>( +								  std::rand() % static_cast<int>(angle_offset)); +		return (random_angle >= 360) ? random_angle - 360 : random_angle; +	} +} + +double ParticleSystem::generate_random_speed(double min_speed, +											 double max_speed) const { +	if (min_speed == max_speed) { +		return min_speed; +	} else { +		return min_speed +			   + static_cast<double>(std::rand() +									 % static_cast<int>(max_speed - min_speed)); +	} +} diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h index 3ac1d3f..d7ca148 100644 --- a/src/crepe/system/ParticleSystem.h +++ b/src/crepe/system/ParticleSystem.h @@ -1,18 +1,71 @@  #pragma once -#include "../api/ParticleEmitter.h" +#include <cstdint> -namespace crepe { +#include "System.h" -class ParticleSystem { +namespace crepe { +class ParticleEmitter; +class Transform; +/** + 	* \brief ParticleSystem class responsible for managing particle emission, updates, and bounds checking. + 	*/ +class ParticleSystem : public System {  public: -	ParticleSystem(); -	void update(); +	/** +		* \brief Updates all particle emitters by emitting particles, updating particle states, and checking bounds. +		*/ +	void update() override;  private: -	void emit_particle(ParticleEmitter & emitter); //emits a new particle +	/** +		* \brief Emits a particle from the specified emitter based on its emission properties. +		*  +		* \param emitter Reference to the ParticleEmitter. +		* \param transform Const reference to the Transform component associated with the emitter. +		*/ +	void emit_particle(ParticleEmitter & emitter, const Transform & transform); + +	/** +		* \brief Calculates the number of times particles should be emitted based on emission rate and update count. +		*  +		* \param count Current update count. +		* \param emission Emission rate. +		* \return The number of particles to emit. +		*/ +	int calculate_update(int count, double emission) const; + +	/** +		* \brief Checks whether particles are within the emitter’s boundary, resets or stops particles if they exit. +		*  +		* \param emitter Reference to the ParticleEmitter. +		* \param transform Const reference to the Transform component associated with the emitter. +		*/ +	void check_bounds(ParticleEmitter & emitter, const Transform & transform); -	float elapsed_time; //elapsed time since the last emission +	/** +		* \brief Generates a random angle for particle emission within the specified range. +		*  +		* \param min_angle Minimum emission angle in degrees. +		* \param max_angle Maximum emission angle in degrees. +		* \return Random angle in degrees. +		*/ +	double generate_random_angle(double min_angle, double max_angle) const; + +	/** +		* \brief Generates a random speed for particle emission within the specified range. +		*  +		* \param min_speed Minimum emission speed. +		* \param max_speed Maximum emission speed. +		* \return Random speed. +		*/ +	double generate_random_speed(double min_speed, double max_speed) const; + +private: +	//! Counter to count updates to determine how many times emit_particle is called. +	unsigned int update_count = 0; +	//! Determines the lowest amount of emission rate (1000 = 0.001 = 1 particle per 1000 updates). +	static constexpr unsigned int MAX_UPDATE_COUNT = 100;  };  } // namespace crepe diff --git a/src/crepe/system/PhysicsSystem.h b/src/crepe/system/PhysicsSystem.h index cc13b70..038c120 100644 --- a/src/crepe/system/PhysicsSystem.h +++ b/src/crepe/system/PhysicsSystem.h @@ -1,5 +1,7 @@  #pragma once +#include "System.h" +  namespace crepe {  /**   * \brief System that controls all physics @@ -7,18 +9,14 @@ namespace crepe {   * This class is a physics system that uses a rigidbody and transform   * to add physics to a game object.   */ -class PhysicsSystem { +class PhysicsSystem : public System {  public:  	/** -	 * Constructor is default -	 */ -	PhysicsSystem() = default; -	/**  	 * \brief updates the physics system.  	 *   	 * It calculates new velocties and changes the postion in the transform.  	 */ -	void update(); +	void update() override;  };  } // namespace crepe diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index 5a07cc2..10211a3 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -20,7 +20,25 @@ RenderSystem & RenderSystem::get_instance() {  	return instance;  } -void RenderSystem::update() { +void RenderSystem::clear_screen() const { +	SDLContext::get_instance().clear_screen(); +} + +void RenderSystem::present_screen() const { +	SDLContext::get_instance().present_screen(); +} +void RenderSystem::update_camera() { +	ComponentManager & mgr = ComponentManager::get_instance(); + +	std::vector<std::reference_wrapper<Camera>> cameras +		= mgr.get_components_by_type<Camera>(); + +	for (Camera & cam : cameras) { +		SDLContext::get_instance().camera(cam); +		this->curr_cam = &cam; +	} +} +void RenderSystem::render_sprites() const {  	ComponentManager & mgr = ComponentManager::get_instance(); @@ -28,14 +46,16 @@ void RenderSystem::update() {  		= mgr.get_components_by_type<Sprite>();  	SDLContext & render = SDLContext::get_instance(); -	render.clear_screen(); -  	for (const Sprite & sprite : sprites) { -		std::vector<std::reference_wrapper<Transform>> transforms +		auto transforms  			= mgr.get_components_by_id<Transform>(sprite.game_object_id); -		for (const Transform & transform : transforms) { -			render.draw(sprite, transform); -		} +		render.draw(sprite, transforms[0], *curr_cam);  	} -	render.present_screen(); +} + +void RenderSystem::update() { +	this->clear_screen(); +	this->update_camera(); +	this->render_sprites(); +	this->present_screen();  } diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h index 4b910a4..70db21a 100644 --- a/src/crepe/system/RenderSystem.h +++ b/src/crepe/system/RenderSystem.h @@ -1,17 +1,64 @@  #pragma once +#include "api/Camera.h" +  #include "System.h"  namespace crepe { +/** + * \class RenderSystem + * \brief Manages rendering operations for all game objects. + * + * RenderSystem is responsible for rendering sprites, clearing and presenting the screen,  + * and managing the active camera. It functions as a singleton, providing centralized  + * rendering services for the application. + */  class RenderSystem : public System {  public: +	/** +	 * \brief Gets the singleton instance of RenderSystem. +	 * \return Reference to the RenderSystem instance. +	 */  	static RenderSystem & get_instance(); -	void update(); + +	/** +	 * \brief Updates the RenderSystem for the current frame. +	 * This method is called to perform all rendering operations for the current game frame. +	 */ +	void update() override;  private: +	// Private constructor to enforce singleton pattern.  	RenderSystem();  	~RenderSystem(); + +	//! Clears the screen in preparation for rendering. +	void clear_screen() const; + +	//! Presents the rendered frame to the display. +	void present_screen() const; + +	//! Updates the active camera used for rendering. +	void update_camera(); + +	//! Renders all active sprites to the screen. +	void render_sprites() const; + +	/** +	 * \todo Include color handling for sprites. +	 * \todo Implement particle emitter rendering with sprites. +	 * \todo Add text rendering using SDL_ttf for text components. +	 * \todo Implement a text component and a button component. +	 * \todo Ensure each sprite is checked for active status before rendering. +	 * \todo Sort all layers by order before rendering. +	 * \todo Consider adding text input functionality. +	 */ + +private: +	//! Pointer to the current active camera for rendering +	Camera * curr_cam = nullptr; +	// TODO: needs a better solution  };  } // namespace crepe diff --git a/src/crepe/util/Proxy.h b/src/crepe/util/Proxy.h index fbfed0c..f84e462 100644 --- a/src/crepe/util/Proxy.h +++ b/src/crepe/util/Proxy.h @@ -16,9 +16,9 @@ template <typename T>  class Proxy {  public:  	//! Set operator -	Proxy & operator = (const T &); +	Proxy & operator=(const T &);  	//! Get operator -	operator const T & (); +	operator const T &();  public:  	Proxy(ValueBroker<T>); @@ -27,7 +27,6 @@ private:  	ValueBroker<T> broker;  }; -} +} // namespace crepe  #include "Proxy.hpp" - diff --git a/src/crepe/util/Proxy.hpp b/src/crepe/util/Proxy.hpp index 4aec9e9..b9923db 100644 --- a/src/crepe/util/Proxy.hpp +++ b/src/crepe/util/Proxy.hpp @@ -5,18 +5,17 @@  namespace crepe {  template <typename T> -Proxy<T>::Proxy(ValueBroker<T> broker) : broker(broker) { } +Proxy<T>::Proxy(ValueBroker<T> broker) : broker(broker) {}  template <typename T> -Proxy<T> & Proxy<T>::operator = (const T & val) { +Proxy<T> & Proxy<T>::operator=(const T & val) {  	this->broker.set(val);  	return *this;  }  template <typename T> -Proxy<T>::operator const T & () { +Proxy<T>::operator const T &() {  	return this->broker.get();  } -} - +} // namespace crepe |