diff options
author | Loek Le Blansch <loek@pipeframe.xyz> | 2024-11-14 11:10:31 +0100 |
---|---|---|
committer | Loek Le Blansch <loek@pipeframe.xyz> | 2024-11-14 11:10:31 +0100 |
commit | 6e13510f3c6d4155707f748d237bb1fa05243450 (patch) | |
tree | b21ca972d14aac56b7c58c85c54d8a2fd1278a7e /src/crepe/system | |
parent | 319d511135da1b58acb78ca939d7ee01fee4bbf3 (diff) | |
parent | be1e97bc7a494963ab1567492fafcda99e36f683 (diff) |
merge `master` into `loek/audio`
Diffstat (limited to 'src/crepe/system')
-rw-r--r-- | src/crepe/system/AnimatorSystem.cpp | 37 | ||||
-rw-r--r-- | src/crepe/system/AnimatorSystem.h | 44 | ||||
-rw-r--r-- | src/crepe/system/AudioSystem.cpp | 2 | ||||
-rw-r--r-- | src/crepe/system/AudioSystem.h | 8 | ||||
-rw-r--r-- | src/crepe/system/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/crepe/system/ParticleSystem.cpp | 157 | ||||
-rw-r--r-- | src/crepe/system/ParticleSystem.h | 67 | ||||
-rw-r--r-- | src/crepe/system/PhysicsSystem.h | 10 | ||||
-rw-r--r-- | src/crepe/system/RenderSystem.cpp | 36 | ||||
-rw-r--r-- | src/crepe/system/RenderSystem.h | 49 |
10 files changed, 344 insertions, 68 deletions
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 |