aboutsummaryrefslogtreecommitdiff
path: root/src/crepe
diff options
context:
space:
mode:
Diffstat (limited to 'src/crepe')
-rw-r--r--src/crepe/Particle.cpp29
-rw-r--r--src/crepe/Particle.h64
-rw-r--r--src/crepe/ValueBroker.hpp3
-rw-r--r--src/crepe/api/Animator.cpp5
-rw-r--r--src/crepe/api/AssetManager.h36
-rw-r--r--src/crepe/api/CMakeLists.txt10
-rw-r--r--src/crepe/api/Camera.cpp3
-rw-r--r--src/crepe/api/CircleCollider.h7
-rw-r--r--src/crepe/api/LoopManager.cpp55
-rw-r--r--src/crepe/api/LoopManager.h79
-rw-r--r--src/crepe/api/LoopTimer.cpp86
-rw-r--r--src/crepe/api/LoopTimer.h147
-rw-r--r--src/crepe/api/Metadata.cpp4
-rw-r--r--src/crepe/api/ParticleEmitter.cpp36
-rw-r--r--src/crepe/api/ParticleEmitter.h99
-rw-r--r--src/crepe/api/Rigidbody.cpp3
-rw-r--r--src/crepe/api/Sprite.cpp5
-rw-r--r--src/crepe/api/Transform.cpp5
-rw-r--r--src/crepe/facade/SDLContext.cpp3
-rw-r--r--src/crepe/facade/SDLContext.h13
-rw-r--r--src/crepe/system/ParticleSystem.cpp157
-rw-r--r--src/crepe/system/ParticleSystem.h67
-rw-r--r--src/crepe/system/PhysicsSystem.h10
23 files changed, 785 insertions, 141 deletions
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.hpp b/src/crepe/ValueBroker.hpp
index 927142f..5c3bed9 100644
--- a/src/crepe/ValueBroker.hpp
+++ b/src/crepe/ValueBroker.hpp
@@ -6,7 +6,8 @@ namespace crepe {
template <typename T>
ValueBroker<T>::ValueBroker(const setter_t & setter, const getter_t & getter)
- : setter(setter), getter(getter) {}
+ : setter(setter),
+ getter(getter) {}
template <typename T>
const T & ValueBroker<T>::get() {
diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp
index 8b396af..58fee2a 100644
--- a/src/crepe/api/Animator.cpp
+++ b/src/crepe/api/Animator.cpp
@@ -10,7 +10,10 @@
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) {
+ : Component(id),
+ spritesheet(ss),
+ row(row),
+ col(col) {
dbg_trace();
animator_rect = spritesheet.sprite_rect;
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/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index 6915074..de1b4b3 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -18,8 +18,13 @@ target_sources(crepe PUBLIC
Vector2.cpp
Camera.cpp
Animator.cpp
+<<<<<<< HEAD
BoxCollider.cpp
CircleCollider.cpp
+=======
+ LoopManager.cpp
+ LoopTimer.cpp
+>>>>>>> be1e97bc7a494963ab1567492fafcda99e36f683
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -44,6 +49,11 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
SceneManager.hpp
Camera.h
Animator.h
+<<<<<<< HEAD
BoxCollider.h
CircleCollider.h
+=======
+ LoopManager.h
+ LoopTimer.h
+>>>>>>> be1e97bc7a494963ab1567492fafcda99e36f683
)
diff --git a/src/crepe/api/Camera.cpp b/src/crepe/api/Camera.cpp
index 820a6a8..6355a03 100644
--- a/src/crepe/api/Camera.cpp
+++ b/src/crepe/api/Camera.cpp
@@ -10,7 +10,8 @@
using namespace crepe;
Camera::Camera(uint32_t id, const Color & bg_color)
- : Component(id), bg_color(bg_color) {
+ : Component(id),
+ bg_color(bg_color) {
dbg_trace();
}
diff --git a/src/crepe/api/CircleCollider.h b/src/crepe/api/CircleCollider.h
index 29a9c1e..e4f0452 100644
--- a/src/crepe/api/CircleCollider.h
+++ b/src/crepe/api/CircleCollider.h
@@ -8,8 +8,15 @@ namespace crepe {
class CircleCollider : public Collider {
public:
+<<<<<<< HEAD
CircleCollider(game_object_id_t game_object_id,Vector2 offset, int radius);
double radius;
+=======
+ CircleCollider(game_object_id_t game_object_id, int radius)
+ : Collider(game_object_id),
+ radius(radius) {}
+ int radius;
+>>>>>>> be1e97bc7a494963ab1567492fafcda99e36f683
};
} // 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/Sprite.cpp b/src/crepe/api/Sprite.cpp
index f9cd761..6f0433f 100644
--- a/src/crepe/api/Sprite.cpp
+++ b/src/crepe/api/Sprite.cpp
@@ -12,7 +12,10 @@ using namespace crepe;
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();
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/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index c4c96e2..236bf8c 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -159,7 +159,7 @@ SDLContext::texture_from_path(const std::string & path) {
SDL_Surface * tmp = IMG_Load(path.c_str());
if (tmp == nullptr) {
- throw Exception("surface cannot be load from %s", path.c_str());
+ tmp = IMG_Load("../asset/texture/ERROR.png");
}
std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>>
@@ -191,3 +191,4 @@ int SDLContext::get_height(const Texture & ctx) const {
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 e358c21..536dec5 100644
--- a/src/crepe/facade/SDLContext.h
+++ b/src/crepe/facade/SDLContext.h
@@ -58,12 +58,23 @@ private:
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:
/**
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