diff --git a/source/core/defaults/physics.hpp b/source/core/defaults/physics.hpp index 617a73b..f3ad7db 100644 --- a/source/core/defaults/physics.hpp +++ b/source/core/defaults/physics.hpp @@ -25,15 +25,17 @@ namespace Defaults::Physics { constexpr float ACCELERACIO = -60.0F; // Fricció/desacceleració (px/s²) constexpr float ROTACIO_MIN = 0.1F; // Rotación mínima (rad/s ~5.7°/s) constexpr float ROTACIO_MAX = 0.3F; // Rotación màxima (rad/s ~17.2°/s) - constexpr float TEMPS_VIDA = 2.0F; // Vida mínima garantida (s) — després pot morir per velocitat baixa - constexpr float TEMPS_VIDA_NAU = 3.0F; // Ship debris min lifetime (matches DEATH_DURATION) - constexpr float SHRINK_RATE = 0.5F; // Reducció de mida (factor sobre min_lifetime) - // Política de mort: passat el min_lifetime, el fragment mor quan la - // seva velocity cau per sota d'aquest llindar. Així els fragments - // ràpids no "popen" en moviment. - constexpr float MIN_SPEED_TO_DIE = 5.0F; // px/s — al cuadrat per evitar sqrt en update - constexpr float MIN_SPEED_TO_DIE_SQ = MIN_SPEED_TO_DIE * MIN_SPEED_TO_DIE; + // Política de mort en dos fases: + // 1. INTACTE: size_factor = 1.0 durant INTACT_TIME segons. Si la velocity + // cau per sota de SHRINK_SPEED_THRESHOLD abans, dispara la transició + // (el que arribi primer). + // 2. MENGUANT: size_factor decreix linealment a SHRINK_RATE per segon + // fins arribar a 0 → el fragment mor (mai "popa" en moviment). + constexpr float INTACT_TIME = 2.0F; // Default temps intacte (s) + constexpr float SHRINK_SPEED_THRESHOLD = 30.0F; // Velocity sota la qual dispara la mengua (px/s) + constexpr float SHRINK_SPEED_THRESHOLD_SQ = SHRINK_SPEED_THRESHOLD * SHRINK_SPEED_THRESHOLD; + constexpr float SHRINK_RATE = 1.0F; // 1/s = 1 segon per encongir de 100% a 0% // Herència de velocity angular (trayectorias curvas) constexpr float FACTOR_HERENCIA_MIN = 0.7F; // Mínimo 70% del drotacio heredat @@ -49,7 +51,7 @@ namespace Defaults::Physics { // Tuneig específic de l'explosió d'enemic (overrides als defaults // que es passen com a paràmetres opcionals a explode()). - constexpr float ENEMY_LIFETIME = 3.0F; // Vida mínima del debris (s) — els que segueixen movent-se viuen més + constexpr float ENEMY_INTACT_TIME = 1.5F; // Temps intacte abans de menguar (s) constexpr float ENEMY_FRICTION = -30.0F; // Fricció més suau perquè s'estenguin més constexpr int ENEMY_SEGMENT_MULTIPLIER = 3; // Còpies de cada segment (5 cares × 3 = 15 trossos) diff --git a/source/game/effects/debris.hpp b/source/game/effects/debris.hpp index bc8f365..62f3ce3 100644 --- a/source/game/effects/debris.hpp +++ b/source/game/effects/debris.hpp @@ -24,16 +24,17 @@ namespace Effects { float velocitat_rot; // Velocidad de rotación de TRAYECTORIA (rad/s) float velocitat_rot_visual; // Velocidad de rotación VISUAL del segment (rad/s) - // Estat de vida - // Política: viu sempre durant min_lifetime, després mor quan - // |velocity| < MIN_SPEED_TO_DIE (definit en Defaults). Així els - // fragments ràpids no "popen" en moviment. - float temps_vida; // Temps transcorregut (segons) - float min_lifetime; // Temps mínim garantit (segons) - bool active; // Està actiu? - - // Shrinking (reducció de distancia entre points) - float factor_shrink; // Factor de reducció per segon (0.0-1.0) + // Estat de vida en dos fases: + // 1. INTACTE (shrinking=false): size_factor=1.0 durant intact_time segons, + // o fins que la velocity cau per sota de SHRINK_SPEED_THRESHOLD. + // 2. MENGUANT (shrinking=true): size_factor decreix a shrink_rate/s fins + // arribar a 0 → mor. Mai pop visual: sempre fade-out. + float temps_vida; // Temps transcorregut (segons) + float intact_time; // Temps en fase INTACTE (segons) + bool shrinking; // false=intacte, true=menguant + float size_factor; // Mida actual (1.0=ple, 0.0=mort) + float shrink_rate; // Velocity de menguada (per segon) + bool active; // Està actiu? // Rendering float brightness; // Factor de brightness (0.0-1.0, heretat de l'objecte original) diff --git a/source/game/effects/debris_manager.cpp b/source/game/effects/debris_manager.cpp index 0c263e0..195e499 100644 --- a/source/game/effects/debris_manager.cpp +++ b/source/game/effects/debris_manager.cpp @@ -136,11 +136,14 @@ namespace Effects { debris->angle_rotacio = 0.0F; - // Vida i shrinking — min_lifetime és el temps mínim garantit; després - // el fragment mor quan |velocity| < MIN_SPEED_TO_DIE. + // Estat inicial: INTACTE durant `lifetime` segons (o fins que la + // velocity baixi de SHRINK_SPEED_THRESHOLD). Després MENGUANT a + // SHRINK_RATE per segon fins arribar a size_factor=0 → mor. debris->temps_vida = 0.0F; - debris->min_lifetime = lifetime; - debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE; + debris->intact_time = lifetime; + debris->shrinking = false; + debris->size_factor = 1.0F; + debris->shrink_rate = Defaults::Physics::Debris::SHRINK_RATE; // Visuals heretades debris->brightness = brightness; @@ -212,102 +215,100 @@ namespace Effects { } } + // INTACTE → MENGUANT → mort. Retorna true si el debris segueix viu. + static auto updateLifecycle(Debris& debris, float delta_time) -> bool { + debris.temps_vida += delta_time; + + const float SPEED_SQ = (debris.velocity.x * debris.velocity.x) + + (debris.velocity.y * debris.velocity.y); + + if (!debris.shrinking) { + const bool TIME_TRIGGER = debris.temps_vida >= debris.intact_time; + const bool SPEED_TRIGGER = SPEED_SQ < Defaults::Physics::Debris::SHRINK_SPEED_THRESHOLD_SQ; + if (TIME_TRIGGER || SPEED_TRIGGER) { + debris.shrinking = true; + } + } + + if (debris.shrinking) { + debris.size_factor -= debris.shrink_rate * delta_time; + if (debris.size_factor <= 0.0F) { + debris.active = false; + return false; + } + } + return true; + } + + // Fricció lineal: redueix |velocity| en acceleration per segon. + static void applyLinearFriction(Debris& debris, float delta_time) { + const float SPEED = std::sqrt((debris.velocity.x * debris.velocity.x) + + (debris.velocity.y * debris.velocity.y)); + if (SPEED > 1.0F) { + const float DIR_X = debris.velocity.x / SPEED; + const float DIR_Y = debris.velocity.y / SPEED; + const float NEW_SPEED = std::max(SPEED + (debris.acceleration * delta_time), 0.0F); + debris.velocity.x = DIR_X * NEW_SPEED; + debris.velocity.y = DIR_Y * NEW_SPEED; + } else { + debris.velocity.x = 0.0F; + debris.velocity.y = 0.0F; + } + } + + // Rota el vector velocity (trajectòria corba) i aplica fricció angular. + static void applyAngularDynamics(Debris& debris, float delta_time) { + if (std::abs(debris.velocitat_rot) <= 0.01F) { + return; + } + + // Rotar vector de velocity amb matriu 2D + const float DANGLE = debris.velocitat_rot * delta_time; + const float VX = debris.velocity.x; + const float VY = debris.velocity.y; + const float COS_A = std::cos(DANGLE); + const float SIN_A = std::sin(DANGLE); + debris.velocity.x = (VX * COS_A) - (VY * SIN_A); + debris.velocity.y = (VX * SIN_A) + (VY * COS_A); + + // Fricció angular (desacceleració gradual) + const float SIGN = (debris.velocitat_rot > 0) ? 1.0F : -1.0F; + const float REDUCCION = Defaults::Physics::Debris::FRICCIO_ANGULAR * delta_time; + debris.velocitat_rot -= SIGN * REDUCCION; + if ((debris.velocitat_rot > 0) != (SIGN > 0)) { + debris.velocitat_rot = 0.0F; + } + } + void DebrisManager::update(float delta_time) { for (auto& debris : debris_pool_) { if (!debris.active) { continue; } - // 1. Actualitzar time de vida - debris.temps_vida += delta_time; - - // Política de mort: viu sí o sí durant min_lifetime; després mor - // quan la velocity cau per sota d'un llindar. Així els fragments - // ràpids no desapareixen en moviment. - if (debris.temps_vida >= debris.min_lifetime) { - const float SPEED_SQ = (debris.velocity.x * debris.velocity.x) + - (debris.velocity.y * debris.velocity.y); - if (SPEED_SQ < Defaults::Physics::Debris::MIN_SPEED_TO_DIE_SQ) { - debris.active = false; - continue; - } + if (!updateLifecycle(debris, delta_time)) { + continue; } - // 2. Actualitzar velocity (desacceleració) - // Aplicar fricció en la direcció del movement - float speed = std::sqrt((debris.velocity.x * debris.velocity.x) + - (debris.velocity.y * debris.velocity.y)); + applyLinearFriction(debris, delta_time); + applyAngularDynamics(debris, delta_time); - if (speed > 1.0F) { - // Calcular direcció normalitzada - float dir_x = debris.velocity.x / speed; - float dir_y = debris.velocity.y / speed; - - // Aplicar aceleración negativa (fricció) - float nova_speed = speed + (debris.acceleration * delta_time); - nova_speed = std::max(nova_speed, 0.0F); - - debris.velocity.x = dir_x * nova_speed; - debris.velocity.y = dir_y * nova_speed; - } else { - // Velocidad mucho baixa, aturar - debris.velocity.x = 0.0F; - debris.velocity.y = 0.0F; - } - - // 2b. Rotar vector de velocity (trayectoria curva) - if (std::abs(debris.velocitat_rot) > 0.01F) { - // Calcular angle de rotación este frame - float dangle = debris.velocitat_rot * delta_time; - - // Rotar vector de velocity usant matriu de rotación 2D - float vel_x_old = debris.velocity.x; - float vel_y_old = debris.velocity.y; - - float cos_a = std::cos(dangle); - float sin_a = std::sin(dangle); - - debris.velocity.x = (vel_x_old * cos_a) - (vel_y_old * sin_a); - debris.velocity.y = (vel_x_old * sin_a) + (vel_y_old * cos_a); - } - - // 2c. Aplicar fricció angular (desacceleració gradual) - if (std::abs(debris.velocitat_rot) > 0.01F) { - float sign = (debris.velocitat_rot > 0) ? 1.0F : -1.0F; - float reduccion = - Defaults::Physics::Debris::FRICCIO_ANGULAR * delta_time; - debris.velocitat_rot -= sign * reduccion; - - // Evitar canvi de signe (no pot passar de CW a CCW) - if ((debris.velocitat_rot > 0) != (sign > 0)) { - debris.velocitat_rot = 0.0F; - } - } - - // 3. Calcular centro del segment + // Posició: nou centre = centre antic + velocity · dt Vec2 centro = {.x = (debris.p1.x + debris.p2.x) / 2.0F, .y = (debris.p1.y + debris.p2.y) / 2.0F}; - - // 4. Actualitzar posición del centro centro.x += debris.velocity.x * delta_time; centro.y += debris.velocity.y * delta_time; - // 5. Actualitzar rotación VISUAL + // Rotació visual del segment debris.angle_rotacio += debris.velocitat_rot_visual * delta_time; - // 6. Aplicar shrinking (reducció de distancia entre points). - // El shrink es normalitza al min_lifetime (capat a 1.0) perquè els - // fragments que viuen més no es continuïn fent més petits per sempre. - const float SHRINK_T = std::min(debris.temps_vida / debris.min_lifetime, 1.0F); - float shrink_factor = 1.0F - (debris.factor_shrink * SHRINK_T); - shrink_factor = std::max(0.0F, shrink_factor); // No negatiu - - // Calcular distancia original entre points + // Mida actual = size_factor (1.0 intacte, decreix durant MENGUANT) + const float SHRINK_FACTOR = std::max(0.0F, debris.size_factor); float dx = debris.p2.x - debris.p1.x; float dy = debris.p2.y - debris.p1.y; // 7. Reconstruir segment con nueva mida i rotación - float half_length = std::sqrt((dx * dx) + (dy * dy)) * shrink_factor / 2.0F; + float half_length = std::sqrt((dx * dx) + (dy * dy)) * SHRINK_FACTOR / 2.0F; float original_angle = std::atan2(dy, dx); float new_angle = original_angle + debris.angle_rotacio; diff --git a/source/game/effects/debris_manager.hpp b/source/game/effects/debris_manager.hpp index 6681deb..95b45e2 100644 --- a/source/game/effects/debris_manager.hpp +++ b/source/game/effects/debris_manager.hpp @@ -48,7 +48,7 @@ namespace Effects { float factor_herencia_visual = 0.0F, const std::string& sound = Defaults::Sound::EXPLOSION, SDL_Color color = {0, 0, 0, 0}, // alpha==0 → fragmentos usan oscilador global - float lifetime = Defaults::Physics::Debris::TEMPS_VIDA, + float lifetime = Defaults::Physics::Debris::INTACT_TIME, float friction = Defaults::Physics::Debris::ACCELERACIO, int segment_multiplier = 1); diff --git a/source/game/systems/collision_system.cpp b/source/game/systems/collision_system.cpp index 85c362a..f000d1e 100644 --- a/source/game/systems/collision_system.cpp +++ b/source/game/systems/collision_system.cpp @@ -74,7 +74,7 @@ namespace Systems::Collision { 0.0F, // sin herencia visual Defaults::Sound::EXPLOSION, COLOR, - Defaults::Physics::Debris::ENEMY_LIFETIME, + Defaults::Physics::Debris::ENEMY_INTACT_TIME, Defaults::Physics::Debris::ENEMY_FRICTION, Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER); }