diff options
Diffstat (limited to 'src/crepe')
-rw-r--r-- | src/crepe/Particle.cpp | 13 | ||||
-rw-r--r-- | src/crepe/Particle.h | 14 | ||||
-rw-r--r-- | src/crepe/api/LoopManager.cpp | 1 | ||||
-rw-r--r-- | src/crepe/api/ParticleEmitter.cpp | 7 | ||||
-rw-r--r-- | src/crepe/api/ParticleEmitter.h | 53 | ||||
-rw-r--r-- | src/crepe/api/Rigidbody.h | 11 | ||||
-rw-r--r-- | src/crepe/facade/SDLContext.cpp | 3 | ||||
-rw-r--r-- | src/crepe/system/CollisionSystem.cpp | 235 | ||||
-rw-r--r-- | src/crepe/system/CollisionSystem.h | 31 | ||||
-rw-r--r-- | src/crepe/system/ParticleSystem.cpp | 90 | ||||
-rw-r--r-- | src/crepe/system/ParticleSystem.h | 22 | ||||
-rw-r--r-- | src/crepe/system/RenderSystem.cpp | 9 | ||||
-rw-r--r-- | src/crepe/util/AbsoluutPosition.cpp | 20 | ||||
-rw-r--r-- | src/crepe/util/AbsoluutPosition.h | 14 | ||||
-rw-r--r-- | src/crepe/util/CMakeLists.txt | 2 |
15 files changed, 338 insertions, 187 deletions
diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp index 485a0d4..b340826 100644 --- a/src/crepe/Particle.cpp +++ b/src/crepe/Particle.cpp @@ -2,8 +2,8 @@ using namespace crepe; -void Particle::reset(uint32_t lifespan, const vec2 & position, const vec2 & velocity, - double angle) { +void Particle::reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity, + float angle) { // Initialize the particle state this->time_in_life = 0; this->lifespan = lifespan; @@ -15,16 +15,17 @@ void Particle::reset(uint32_t lifespan, const vec2 & position, const vec2 & velo this->force_over_time = {0, 0}; } -void Particle::update() { +void Particle::update(double dt) { // Deactivate particle if it has exceeded its lifespan - if (++time_in_life >= lifespan) { + time_in_life += dt; + 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; + this->velocity += force_over_time * dt; + this->position += velocity * dt; } void Particle::stop_movement() { diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h index d0397c9..ee0cd66 100644 --- a/src/crepe/Particle.h +++ b/src/crepe/Particle.h @@ -14,8 +14,6 @@ namespace crepe { * can also be reset or stopped. */ class Particle { - // TODO: add friend particleSsytem and rendersystem. Unit test will fail. - public: //! Position of the particle in 2D space. vec2 position; @@ -24,13 +22,13 @@ public: //! Accumulated force affecting the particle over time. vec2 force_over_time; //! Total lifespan of the particle in milliseconds. - uint32_t lifespan; + float 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; + float time_in_life = 0; //! The angle at which the particle is oriented or moving. - double angle = 0; + float angle = 0; /** * \brief Resets the particle with new properties. @@ -43,14 +41,16 @@ public: * \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 vec2 & position, const vec2 & velocity, double angle); + void reset(unsigned int lifespan, const vec2 & position, const vec2 & velocity, + float 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. + * \param dt The amount of fixed delta time that has passed. */ - void update(); + void update(double dt); /** * \brief Stops the particle's movement. * diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index b5e5ff7..7a78019 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -65,6 +65,7 @@ void LoopManager::fixed_update() { this->get_system<InputSystem>().update(); this->event_manager.dispatch_events(); this->get_system<ScriptSystem>().update(); + this->get_system<ParticleSystem>().update(); this->get_system<AISystem>().update(); this->get_system<PhysicsSystem>().update(); this->get_system<CollisionSystem>().update(); diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp index 90b77a0..4f54bbd 100644 --- a/src/crepe/api/ParticleEmitter.cpp +++ b/src/crepe/api/ParticleEmitter.cpp @@ -1,11 +1,14 @@ #include "ParticleEmitter.h" +#include "api/Sprite.h" using namespace crepe; -ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Data & data) +ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite, + const Data & data) : Component(game_object_id), + sprite(sprite), data(data) { for (size_t i = 0; i < this->data.max_particles; i++) { - this->data.particles.emplace_back(); + this->particles.emplace_back(); } } diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h index b83fd61..8ac2e72 100644 --- a/src/crepe/api/ParticleEmitter.h +++ b/src/crepe/api/ParticleEmitter.h @@ -1,7 +1,11 @@ #pragma once +#include <cmath> #include <vector> +#include "system/ParticleSystem.h" +#include "system/RenderSystem.h" + #include "Component.h" #include "Particle.h" #include "types.h" @@ -26,15 +30,18 @@ public: */ struct Boundary { //! boundary width (midpoint is emitter location) - double width = 0.0; + float width = INFINITY; //! boundary height (midpoint is emitter location) - double height = 0.0; + float height = INFINITY; //! boundary offset from particle emitter location vec2 offset; //! reset on exit or stop velocity and set max postion bool reset_on_exit = false; }; + //! sprite reference of displayed sprite + const Sprite & sprite; + /** * \brief Holds parameters that control particle emission. * @@ -42,32 +49,28 @@ public: * and the sprite used for rendering particles. */ struct Data { - //! position of the emitter - vec2 position; + //! offset of the emitter relative to transform + vec2 offset; //! 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; + const unsigned int max_particles = 256; + //! rate of particle emission per second + float emission_rate = 50; //! min speed of the particles - double min_speed = 0; + float min_speed = 100; //! min speed of the particles - double max_speed = 0; + float max_speed = 100; //! min angle of particle emission - double min_angle = 0; + float 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; + float max_angle = 0; + //! begin Lifespan of particle in seconds (only visual) + float begin_lifespan = 0.0; + //! end Lifespan of particle in seconds + float end_lifespan = 10.0; //! force over time (physics) vec2 force_over_time; //! particle boundary Boundary boundary; - //! collection of particles - std::vector<Particle> particles; - //! sprite reference - const Sprite & sprite; }; public: @@ -75,11 +78,21 @@ 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); + ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite, const Data & data); public: //! Configuration data for particle emission settings. Data data; + +private: + //! Only ParticleSystem can move and read particles + friend ParticleSystem; + //! Only RenderSystem can read particles + friend RenderSystem; + //! Saves time left over from last update event. + float spawn_accumulator = 0; + //! collection of particles + std::vector<Particle> particles; }; } // namespace crepe diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h index 6900295..0f6be21 100644 --- a/src/crepe/api/Rigidbody.h +++ b/src/crepe/api/Rigidbody.h @@ -122,15 +122,8 @@ public: */ float elastisity_coefficient = 0.0; - /** - * \brief Offset of all colliders relative to the object's transform position. - * - * The `offset` defines a positional shift applied to all colliders associated with the object, relative to the object's - * transform position. This allows for the colliders to be placed at a different position than the object's actual - * position, without modifying the object's transform itself. - * - */ - vec2 offset; + //! Enable collision handeling in collision system + bool kinematic_collision = true; /** * \brief Defines the collision layers of a GameObject. diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 20bb030..a1a6f97 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -235,8 +235,7 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const { } size *= cam_aux_data.render_scale * ctx.img_scale * data.scale_offset; - vec2 screen_pos = (ctx.pos + data.position_offset - cam_aux_data.cam_pos - + (cam_aux_data.zoomed_viewport) / 2) + vec2 screen_pos = (ctx.pos - cam_aux_data.cam_pos + (cam_aux_data.zoomed_viewport) / 2) * cam_aux_data.render_scale - size / 2 + cam_aux_data.bar_size; diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index af8adce..9604543 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -15,11 +15,12 @@ #include "api/Rigidbody.h" #include "api/Transform.h" #include "api/Vector2.h" +#include "util/AbsoluutPosition.h" +#include "util/OptionalRef.h" #include "Collider.h" #include "CollisionSystem.h" #include "types.h" -#include "util/OptionalRef.h" using namespace crepe; @@ -63,7 +64,7 @@ void CollisionSystem::update() { // For both objects call the collision handler for (auto & collision_pair : collided) { this->collision_handler_request(collision_pair.first, collision_pair.second); - this->collision_handler_request(collision_pair.second, collision_pair.first); + // this->collision_handler_request(collision_pair.second, collision_pair.first); } } @@ -138,10 +139,11 @@ CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal = std::get<std::reference_wrapper<BoxCollider>>(data1.collider); const BoxCollider & collider2 = std::get<std::reference_wrapper<BoxCollider>>(data2.collider); - vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, - data1.rigidbody); - vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, - data2.rigidbody); + + vec2 collider_pos1 + = AbsoluutPosition::get_position(data1.transform, collider1.offset); + vec2 collider_pos2 + = AbsoluutPosition::get_position(data2.transform, collider2.offset); resolution = this->get_box_box_resolution(collider1, collider2, collider_pos1, collider_pos2); break; @@ -151,10 +153,10 @@ CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal = std::get<std::reference_wrapper<BoxCollider>>(data1.collider); const CircleCollider & collider2 = std::get<std::reference_wrapper<CircleCollider>>(data2.collider); - vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, - data1.rigidbody); - vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, - data2.rigidbody); + vec2 collider_pos1 + = AbsoluutPosition::get_position(data1.transform, collider1.offset); + vec2 collider_pos2 + = AbsoluutPosition::get_position(data2.transform, collider2.offset); resolution = -this->get_circle_box_resolution(collider2, collider1, collider_pos2, collider_pos1); break; @@ -164,10 +166,10 @@ CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal = std::get<std::reference_wrapper<CircleCollider>>(data1.collider); const CircleCollider & collider2 = std::get<std::reference_wrapper<CircleCollider>>(data2.collider); - vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, - data1.rigidbody); - vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, - data2.rigidbody); + vec2 collider_pos1 + = AbsoluutPosition::get_position(data1.transform, collider1.offset); + vec2 collider_pos2 + = AbsoluutPosition::get_position(data2.transform, collider2.offset); resolution = this->get_circle_circle_resolution(collider1, collider2, collider_pos1, collider_pos2); break; @@ -177,10 +179,10 @@ CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal = std::get<std::reference_wrapper<CircleCollider>>(data1.collider); const BoxCollider & collider2 = std::get<std::reference_wrapper<BoxCollider>>(data2.collider); - vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, - data1.rigidbody); - vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, - data2.rigidbody); + vec2 collider_pos1 + = AbsoluutPosition::get_position(data1.transform, collider1.offset); + vec2 collider_pos2 + = AbsoluutPosition::get_position(data2.transform, collider2.offset); resolution = this->get_circle_box_resolution(collider1, collider2, collider_pos1, collider_pos2); break; @@ -302,16 +304,68 @@ vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider & circle_co } void CollisionSystem::determine_collision_handler(CollisionInfo & info) { - // Check rigidbody type for static - if (info.this_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) return; - // If second body is static perform the static collision handler in this system - if (info.other_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) { - this->static_collision_handler(info); + // Inverted collision info + CollisionInfo inverted = { + .this_collider = info.other_collider, + .this_transform = info.other_transform, + .this_rigidbody = info.other_rigidbody, + .this_metadata = info.other_metadata, + .other_collider = info.this_collider, + .other_transform = info.this_transform, + .other_rigidbody = info.this_rigidbody, + .other_metadata = info.this_metadata, + .resolution = -info.resolution, + .resolution_direction = info.resolution_direction, }; - // Call collision event for user + // If both objects are static skip handle call collision script + if (info.this_rigidbody.data.body_type == Rigidbody::BodyType::STATIC + && info.other_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) + return; + + // First body is not dynamic + if (info.this_rigidbody.data.body_type != Rigidbody::BodyType::DYNAMIC) { + bool static_collision + = info.this_rigidbody.data.body_type == Rigidbody::BodyType::STATIC + && info.other_rigidbody.data.body_type == Rigidbody::BodyType::DYNAMIC; + bool kinematic_collision + = info.this_rigidbody.data.body_type == Rigidbody::BodyType::KINEMATIC + && info.other_rigidbody.data.body_type == Rigidbody::BodyType::DYNAMIC + && info.this_rigidbody.data.kinematic_collision; + + if (static_collision || kinematic_collision) { + // Static collision + this->static_collision_handler(inverted); + }; + // Call scripts + this->call_collision_events(inverted, info); + return; + } + + // Second body is not dynamic + if (info.other_rigidbody.data.body_type != Rigidbody::BodyType::DYNAMIC) { + bool static_collision + = info.other_rigidbody.data.body_type == Rigidbody::BodyType::STATIC; + bool kinematic_collision + = info.other_rigidbody.data.body_type == Rigidbody::BodyType::KINEMATIC + && info.other_rigidbody.data.kinematic_collision; + if (static_collision || kinematic_collision) this->static_collision_handler(info); + this->call_collision_events(info, inverted); + return; + } + + //dynamic + this->dynamic_collision_handler(info); + this->call_collision_events(info, inverted); +} + +void CollisionSystem::call_collision_events(CollisionInfo & info, + CollisionInfo & info_inverted) { CollisionEvent data(info); + CollisionEvent data_inverted(info_inverted); EventManager & emgr = this->mediator.event_manager; emgr.trigger_event<CollisionEvent>(data, info.this_collider.game_object_id); + emgr.trigger_event<CollisionEvent>(data_inverted, + info_inverted.this_collider.game_object_id); } void CollisionSystem::static_collision_handler(CollisionInfo & info) { @@ -363,6 +417,80 @@ void CollisionSystem::static_collision_handler(CollisionInfo & info) { } } +void CollisionSystem::dynamic_collision_handler(CollisionInfo & info) { + info.this_transform.position += info.resolution / 2; + info.other_transform.position += -(info.resolution / 2); + + switch (info.resolution_direction) { + case Direction::BOTH: + if (info.this_rigidbody.data.elastisity_coefficient > 0) { + info.this_rigidbody.data.linear_velocity + = -info.this_rigidbody.data.linear_velocity + * info.this_rigidbody.data.elastisity_coefficient; + } else { + info.this_rigidbody.data.linear_velocity = {0, 0}; + } + + if (info.other_rigidbody.data.elastisity_coefficient > 0) { + info.other_rigidbody.data.linear_velocity + = -info.other_rigidbody.data.linear_velocity + * info.other_rigidbody.data.elastisity_coefficient; + } else { + info.other_rigidbody.data.linear_velocity = {0, 0}; + } + break; + case Direction::Y_DIRECTION: + if (info.this_rigidbody.data.elastisity_coefficient > 0) { + info.this_rigidbody.data.linear_velocity.y + = -info.this_rigidbody.data.linear_velocity.y + * info.this_rigidbody.data.elastisity_coefficient; + } + // Stop movement + else { + info.this_rigidbody.data.linear_velocity.y = 0; + info.this_transform.position.x -= info.resolution.x; + } + + if (info.other_rigidbody.data.elastisity_coefficient > 0) { + info.other_rigidbody.data.linear_velocity.y + = -info.other_rigidbody.data.linear_velocity.y + * info.other_rigidbody.data.elastisity_coefficient; + } + // Stop movement + else { + info.other_rigidbody.data.linear_velocity.y = 0; + info.other_transform.position.x -= info.resolution.x; + } + break; + case Direction::X_DIRECTION: + if (info.this_rigidbody.data.elastisity_coefficient > 0) { + info.this_rigidbody.data.linear_velocity.x + = -info.this_rigidbody.data.linear_velocity.x + * info.this_rigidbody.data.elastisity_coefficient; + } + // Stop movement + else { + info.this_rigidbody.data.linear_velocity.x = 0; + info.this_transform.position.y -= info.resolution.y; + } + + if (info.other_rigidbody.data.elastisity_coefficient > 0) { + info.other_rigidbody.data.linear_velocity.x + = -info.other_rigidbody.data.linear_velocity.x + * info.other_rigidbody.data.elastisity_coefficient; + } + // Stop movement + else { + info.other_rigidbody.data.linear_velocity.x = 0; + info.other_transform.position.y -= info.resolution.y; + } + break; + case Direction::NONE: + // Not possible + break; + } +} + std::vector<std::pair<CollisionSystem::CollisionInternal, CollisionSystem::CollisionInternal>> CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) { @@ -489,14 +617,18 @@ bool CollisionSystem::get_box_box_collision(const BoxCollider & box1, const BoxC const Rigidbody & rigidbody1, const Rigidbody & rigidbody2) const { // Get current positions of colliders - vec2 final_position1 = this->get_current_position(box1.offset, transform1, rigidbody1); - vec2 final_position2 = this->get_current_position(box2.offset, transform2, rigidbody2); + vec2 final_position1 = AbsoluutPosition::get_position(transform1, box1.offset); + vec2 final_position2 = AbsoluutPosition::get_position(transform2, box2.offset); + + // Scale dimensions + vec2 scaled_box1 = box1.dimensions * transform1.scale; + vec2 scaled_box2 = box2.dimensions * transform2.scale; // Calculate half-extents (half width and half height) - float half_width1 = box1.dimensions.x / 2.0; - float half_height1 = box1.dimensions.y / 2.0; - float half_width2 = box2.dimensions.x / 2.0; - float half_height2 = box2.dimensions.y / 2.0; + float half_width1 = scaled_box1.x / 2.0; + float half_height1 = scaled_box1.y / 2.0; + float half_width2 = scaled_box2.x / 2.0; + float half_height2 = scaled_box2.y / 2.0; // Check if the boxes overlap along the X and Y axes return (final_position1.x + half_width1 > final_position2.x - half_width2 @@ -512,12 +644,16 @@ bool CollisionSystem::get_box_circle_collision(const BoxCollider & box1, const Rigidbody & rigidbody1, const Rigidbody & rigidbody2) const { // Get current positions of colliders - vec2 final_position1 = this->get_current_position(box1.offset, transform1, rigidbody1); - vec2 final_position2 = this->get_current_position(circle2.offset, transform2, rigidbody2); + vec2 final_position1 = AbsoluutPosition::get_position(transform1, box1.offset); + vec2 final_position2 = AbsoluutPosition::get_position(transform2, circle2.offset); + + // Scale dimensions + vec2 scaled_box = box1.dimensions * transform1.scale; + float scaled_circle = circle2.radius * transform2.scale; // Calculate box half-extents - float half_width = box1.dimensions.x / 2.0; - float half_height = box1.dimensions.y / 2.0; + float half_width = scaled_box.x / 2.0; + float half_height = scaled_box.y / 2.0; // Find the closest point on the box to the circle's center float closest_x = std::max(final_position1.x - half_width, @@ -531,7 +667,7 @@ bool CollisionSystem::get_box_circle_collision(const BoxCollider & box1, float distance_squared = distance_x * distance_x + distance_y * distance_y; // Compare distance squared with the square of the circle's radius - return distance_squared < circle2.radius * circle2.radius; + return distance_squared < scaled_circle * scaled_circle; } bool CollisionSystem::get_circle_circle_collision(const CircleCollider & circle1, @@ -541,35 +677,20 @@ bool CollisionSystem::get_circle_circle_collision(const CircleCollider & circle1 const Rigidbody & rigidbody1, const Rigidbody & rigidbody2) const { // Get current positions of colliders - vec2 final_position1 = this->get_current_position(circle1.offset, transform1, rigidbody1); - vec2 final_position2 = this->get_current_position(circle2.offset, transform2, rigidbody2); + vec2 final_position1 = AbsoluutPosition::get_position(transform1, circle1.offset); + vec2 final_position2 = AbsoluutPosition::get_position(transform2, circle2.offset); + + // Scale dimensions + float scaled_circle1 = circle1.radius * transform1.scale; + float scaled_circle2 = circle2.radius * transform2.scale; float distance_x = final_position1.x - final_position2.x; float distance_y = final_position1.y - final_position2.y; float distance_squared = distance_x * distance_x + distance_y * distance_y; // Calculate the sum of the radii - float radius_sum = circle1.radius + circle2.radius; + float radius_sum = scaled_circle1 + scaled_circle2; // Check if the distance between the centers is less than or equal to the sum of the radii return distance_squared < radius_sum * radius_sum; } - -vec2 CollisionSystem::get_current_position(const vec2 & collider_offset, - const Transform & transform, - const Rigidbody & rigidbody) const { - // Get the rotation in radians - float radians1 = transform.rotation * (M_PI / 180.0); - - // Calculate total offset with scale - vec2 total_offset = (rigidbody.data.offset + collider_offset) * transform.scale; - - // Rotate - float rotated_total_offset_x1 - = total_offset.x * cos(radians1) - total_offset.y * sin(radians1); - float rotated_total_offset_y1 - = total_offset.x * sin(radians1) + total_offset.y * cos(radians1); - - // Final positions considering scaling and rotation - return (transform.position + vec2(rotated_total_offset_x1, rotated_total_offset_y1)); -} diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h index 5b136c6..23752e1 100644 --- a/src/crepe/system/CollisionSystem.h +++ b/src/crepe/system/CollisionSystem.h @@ -100,19 +100,6 @@ private: CollisionInternalType get_collider_type(const collider_variant & collider1, const collider_variant & collider2) const; - /** - * \brief Calculates the current position of a collider. - * - * Combines the Collider offset, Transform position, and Rigidbody offset to compute the position of the collider. - * - * \param collider_offset The offset of the collider. - * \param transform The Transform of the associated game object. - * \param rigidbody The Rigidbody of the associated game object. - * \return The calculated position of the collider. - */ - vec2 get_current_position(const vec2 & collider_offset, const Transform & transform, - const Rigidbody & rigidbody) const; - private: /** * \brief Handles collision resolution between two colliders. @@ -195,6 +182,15 @@ private: void determine_collision_handler(CollisionInfo & info); /** + * \brief Calls both collision script + * + * Calls both collision script to let user add additonal handeling or handle full collision. + * + * \param info Collision information containing data about both colliders. + */ + void call_collision_events(CollisionInfo & info, CollisionInfo & info_inverted); + + /** * \brief Handles collisions involving static objects. * * Resolves collisions by adjusting positions and modifying velocities if bounce is enabled. @@ -203,6 +199,15 @@ private: */ void static_collision_handler(CollisionInfo & info); + /** + * \brief Handles collisions involving dynamic objects. + * + * Resolves collisions by adjusting positions and modifying velocities if bounce is enabled. + * + * \param info Collision information containing data about both colliders. + */ + void dynamic_collision_handler(CollisionInfo & info); + private: /** * \brief Checks for collisions between colliders. diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp index b14c52f..56f1dfd 100644 --- a/src/crepe/system/ParticleSystem.cpp +++ b/src/crepe/system/ParticleSystem.cpp @@ -1,3 +1,4 @@ +#include <chrono> #include <cmath> #include <cstdlib> #include <ctime> @@ -5,6 +6,8 @@ #include "../api/ParticleEmitter.h" #include "../api/Transform.h" #include "../manager/ComponentManager.h" +#include "../manager/LoopTimerManager.h" +#include "util/AbsoluutPosition.h" #include "ParticleSystem.h" @@ -12,7 +15,11 @@ using namespace crepe; void ParticleSystem::update() { // Get all emitters - ComponentManager & mgr = this->mediator.component_manager; + const Mediator & mediator = this->mediator; + LoopTimerManager & loop_timer = mediator.loop_timer; + ComponentManager & mgr = mediator.component_manager; + float dt = loop_timer.get_scaled_fixed_delta_time().count(); + RefVector<ParticleEmitter> emitters = mgr.get_components_by_type<ParticleEmitter>(); for (ParticleEmitter & emitter : emitters) { @@ -21,38 +28,39 @@ void ParticleSystem::update() { = 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); + emitter.spawn_accumulator += emitter.data.emission_rate * dt; + while (emitter.spawn_accumulator >= 1.0) { + this->emit_particle(emitter, transform); + emitter.spawn_accumulator -= 1.0; } // Update all particles - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { if (particle.active) { - particle.update(); + particle.update(dt); } } // Check if within boundary - check_bounds(emitter, transform); + this->check_bounds(emitter, transform); } - - this->update_count = (this->update_count + 1) % this->MAX_UPDATE_COUNT; } void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & transform) { constexpr float DEG_TO_RAD = M_PI / 180.0; - vec2 initial_position = emitter.data.position + transform.position; - float random_angle = generate_random_angle(emitter.data.min_angle, emitter.data.max_angle); + vec2 initial_position = AbsoluutPosition::get_position(transform, emitter.data.offset); + float random_angle + = this->generate_random_angle(emitter.data.min_angle, emitter.data.max_angle); - float random_speed = generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); + float random_speed + = this->generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); float angle_radians = random_angle * DEG_TO_RAD; vec2 velocity = {random_speed * std::cos(angle_radians), random_speed * std::sin(angle_radians)}; - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { if (!particle.active) { particle.reset(emitter.data.end_lifespan, initial_position, velocity, random_angle); @@ -61,66 +69,54 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & } } -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) { - vec2 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; + vec2 offset = emitter.data.boundary.offset + transform.position + emitter.data.offset; + float half_width = emitter.data.boundary.width / 2.0; + float 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; + float left = offset.x - half_width; + float right = offset.x + half_width; + float top = offset.y - half_height; + float bottom = offset.y + half_height; - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { const vec2 & position = particle.position; - bool within_bounds = (position.x >= LEFT && position.x <= RIGHT && position.y >= TOP - && position.y <= BOTTOM); - + bool within_bounds = (position.x >= left && position.x <= right && position.y >= top + && position.y <= bottom); + //if not within bounds do a reset or stop velocity 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; + 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 { +float ParticleSystem::generate_random_angle(float min_angle, float 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)); + + static_cast<float>(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)); + float angle_offset = (360 - min_angle) + max_angle; + float random_angle + = min_angle + static_cast<float>(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 { +float ParticleSystem::generate_random_speed(float min_speed, float 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)); + + static_cast<float>(std::rand() % static_cast<int>(max_speed - min_speed)); } } diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h index 068f01c..154521d 100644 --- a/src/crepe/system/ParticleSystem.h +++ b/src/crepe/system/ParticleSystem.h @@ -32,16 +32,6 @@ private: 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. * @@ -57,7 +47,7 @@ private: * \param max_angle Maximum emission angle in degrees. * \return Random angle in degrees. */ - double generate_random_angle(double min_angle, double max_angle) const; + float generate_random_angle(float min_angle, float max_angle) const; /** * \brief Generates a random speed for particle emission within the specified range. @@ -66,15 +56,7 @@ private: * \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; + float generate_random_speed(float min_speed, float max_speed) const; }; } // namespace crepe diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index afd9548..c0717fc 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -13,6 +13,7 @@ #include "../facade/Texture.h" #include "../manager/ComponentManager.h" #include "../manager/ResourceManager.h" +#include "util/AbsoluutPosition.h" #include "RenderSystem.h" #include "types.h" @@ -83,11 +84,11 @@ bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) bool rendering_particles = false; for (const ParticleEmitter & em : emitters) { - if (&em.data.sprite != &sprite) continue; + if (&em.sprite != &sprite) continue; rendering_particles = true; if (!em.active) continue; - for (const Particle & p : em.data.particles) { + for (const Particle & p : em.particles) { if (!p.active) continue; ctx.draw(SDLContext::RenderContext{ @@ -105,11 +106,11 @@ void RenderSystem::render_normal(const Sprite & sprite, const Transform & tm) { SDLContext & ctx = this->mediator.sdl_context; ResourceManager & resource_manager = this->mediator.resource_manager; const Texture & res = resource_manager.get<Texture>(sprite.source); - + vec2 pos = AbsoluutPosition::get_position(tm, sprite.data.position_offset); ctx.draw(SDLContext::RenderContext{ .sprite = sprite, .texture = res, - .pos = tm.position, + .pos = pos, .angle = tm.rotation, .scale = tm.scale, }); diff --git a/src/crepe/util/AbsoluutPosition.cpp b/src/crepe/util/AbsoluutPosition.cpp new file mode 100644 index 0000000..296cc09 --- /dev/null +++ b/src/crepe/util/AbsoluutPosition.cpp @@ -0,0 +1,20 @@ +#include "AbsoluutPosition.h" + +using namespace crepe; + +vec2 AbsoluutPosition::get_position(const Transform & transform, const vec2 & offset) { + // Get the rotation in radians + float radians1 = transform.rotation * (M_PI / 180.0); + + // Calculate total offset with scale + vec2 total_offset = offset * transform.scale; + + // Rotate + float rotated_total_offset_x1 + = total_offset.x * cos(radians1) - total_offset.y * sin(radians1); + float rotated_total_offset_y1 + = total_offset.x * sin(radians1) + total_offset.y * cos(radians1); + + // Final positions considering scaling and rotation + return (transform.position + vec2(rotated_total_offset_x1, rotated_total_offset_y1)); +} diff --git a/src/crepe/util/AbsoluutPosition.h b/src/crepe/util/AbsoluutPosition.h new file mode 100644 index 0000000..30a7f93 --- /dev/null +++ b/src/crepe/util/AbsoluutPosition.h @@ -0,0 +1,14 @@ +#pragma once + +#include "api/Transform.h" + +#include "types.h" + +namespace crepe { + +class AbsoluutPosition { +public: + static vec2 get_position(const Transform & transform, const vec2 & offset); +}; + +} // namespace crepe diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt index 94ed906..b4b9221 100644 --- a/src/crepe/util/CMakeLists.txt +++ b/src/crepe/util/CMakeLists.txt @@ -1,6 +1,7 @@ target_sources(crepe PUBLIC LogColor.cpp Log.cpp + AbsoluutPosition.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -11,5 +12,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Proxy.hpp OptionalRef.h OptionalRef.hpp + AbsoluutPosition.h ) |