From ae1d1397b17446f000d6b0021204e38b83e3d36f Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Thu, 21 May 2026 13:46:25 +0200 Subject: [PATCH] revert: vuelve al modelo de efd18ff + ENEMY_LIFETIME 3.0 -> 4.5 --- source/core/defaults/physics.hpp | 28 ++- source/game/effects/debris.hpp | 42 ++--- source/game/effects/debris_manager.cpp | 218 +++++++++++------------ source/game/effects/debris_manager.hpp | 10 +- source/game/systems/collision_system.cpp | 5 +- 5 files changed, 133 insertions(+), 170 deletions(-) diff --git a/source/core/defaults/physics.hpp b/source/core/defaults/physics.hpp index da559b7..96cc9a9 100644 --- a/source/core/defaults/physics.hpp +++ b/source/core/defaults/physics.hpp @@ -23,25 +23,22 @@ namespace Defaults::Physics { constexpr float VELOCITAT_BASE = 80.0F; // Velocidad inicial (px/s) constexpr float VARIACIO_VELOCITAT = 40.0F; // ±variació aleatòria (px/s) constexpr float ACCELERACIO = -60.0F; // Fricció/desacceleració (px/s²) - constexpr float ROTACIO_MIN = 0.2F; // Rotación mínima (rad/s ~11.5°/s) - constexpr float ROTACIO_MAX = 0.5F; // Rotación màxima (rad/s ~28.6°/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 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% + // 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; // Herència de velocity angular (trayectorias curvas) constexpr float FACTOR_HERENCIA_MIN = 0.7F; // Mínimo 70% del drotacio heredat constexpr float FACTOR_HERENCIA_MAX = 1.0F; // Màxim 100% del drotacio heredat - constexpr float FRICCIO_ANGULAR = 0.5F; // Desacceleració de la rotació de TRAJECTÒRIA (rad/s²) - constexpr float FRICCIO_VISUAL = 0.05F; // Desacceleració de la rotació VISUAL (suau, persisteix més) + constexpr float FRICCIO_ANGULAR = 0.5F; // Desacceleració angular (rad/s²) // Velocity heredada de la nau a l'explosió (80% del feel original). constexpr float SHIP_VELOCITY_INHERITANCE = 0.8F; @@ -52,8 +49,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_INTACT_TIME = 0.5F; // Temps intacte abans de menguar (s) — comencen aviat - constexpr float ENEMY_SHRINK_RATE = 0.7F; // 0.7 per segon → ~1.4s de fade-out + constexpr float ENEMY_LIFETIME = 4.5F; // Vida mínima del debris (s) — els que segueixen movent-se viuen mé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 1211be2..bc8f365 100644 --- a/source/game/effects/debris.hpp +++ b/source/game/effects/debris.hpp @@ -9,43 +9,31 @@ namespace Effects { // Debris: un segment de línia que vola perpendicular a sí mismo - // Representa un fragment de una shape destruïda (ship, enemy, bullet). - // - // Representació autoritaritzada (font de veritat): - // centro + original_angle (orientació spawn) + angle_rotacio (acumulat) - // + original_half_length × size_factor (mida actual) - // p1/p2 es reconstrueixen cada frame des d'aquestes dades — no es modifiquen - // iterativament (evita bugs de rotació quadràtica i shrink exponencial). + // Representa un fragment de una shape destruïda (ship, enemy, bullet) struct Debris { - // Geometria del segment (2 points en coordenades mundials, derivats) - Vec2 p1; - Vec2 p2; - - // Geometria original (font de veritat, no canvia després del spawn) - Vec2 centro; // Centre actual del segment (es mou amb velocity) - float original_angle; // Orientació del segment al spawn (rad) - float original_half_length; // Mitja-longitud al spawn (px) + // Geometria del segment (2 points en coordenades mundials) + Vec2 p1; // Vec2 inicial del segment + Vec2 p2; // Vec2 final del segment // Física Vec2 velocity; // Velocidad en px/s (components x, y) float acceleration; // Aceleración negativa (fricció) en px/s² // Rotación - float angle_rotacio; // Acumulat de rotació visual des del spawn (radians) + float angle_rotacio; // Angle de rotación acumulat (radians) 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 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? + // 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) // 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 08cc966..0c263e0 100644 --- a/source/game/effects/debris_manager.cpp +++ b/source/game/effects/debris_manager.cpp @@ -57,8 +57,7 @@ namespace Effects { SDL_Color color, float lifetime, float friction, - int segment_multiplier, - float shrink_rate) { + int segment_multiplier) { if (!shape || !shape->isValid()) { return; } @@ -80,7 +79,7 @@ namespace Effects { Vec2 world_p2 = transformPoint(local_p2, shape_centre, centro, angle, scale); // Si el pool es ple, no té sentit continuar amb la resta de segments - if (!spawnDebris(world_p1, world_p2, centro, velocitat_base, brightness, velocitat_objecte, velocitat_angular, factor_herencia_visual, color, lifetime, friction, shrink_rate)) { + if (!spawnDebris(world_p1, world_p2, centro, velocitat_base, brightness, velocitat_objecte, velocitat_angular, factor_herencia_visual, color, lifetime, friction)) { return; } } @@ -106,21 +105,14 @@ namespace Effects { return segments; } - auto DebrisManager::spawnDebris(const Vec2& world_p1, const Vec2& world_p2, const Vec2& centro, float velocitat_base, float brightness, const Vec2& velocitat_objecte, float velocitat_angular, float factor_herencia_visual, SDL_Color color, float lifetime, float friction, float shrink_rate) -> bool { + auto DebrisManager::spawnDebris(const Vec2& world_p1, const Vec2& world_p2, const Vec2& centro, float velocitat_base, float brightness, const Vec2& velocitat_objecte, float velocitat_angular, float factor_herencia_visual, SDL_Color color, float lifetime, float friction) -> bool { Debris* debris = findFreeSlot(); if (debris == nullptr) { std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n"; return false; } - // Geometria autoritaritzada: centro + original_angle + original_half_length. - // p1/p2 es reconstrueixen cada frame en update() des d'aquestes dades. - const float DX = world_p2.x - world_p1.x; - const float DY = world_p2.y - world_p1.y; - debris->centro = {.x = (world_p1.x + world_p2.x) / 2.0F, - .y = (world_p1.y + world_p2.y) / 2.0F}; - debris->original_angle = std::atan2(DY, DX); - debris->original_half_length = std::sqrt((DX * DX) + (DY * DY)) / 2.0F; + // Geometria debris->p1 = world_p1; debris->p2 = world_p2; @@ -144,14 +136,11 @@ namespace Effects { debris->angle_rotacio = 0.0F; - // 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. + // Vida i shrinking — min_lifetime és el temps mínim garantit; després + // el fragment mor quan |velocity| < MIN_SPEED_TO_DIE. debris->temps_vida = 0.0F; - debris->intact_time = lifetime; - debris->shrinking = false; - debris->size_factor = 1.0F; - debris->shrink_rate = shrink_rate; + debris->min_lifetime = lifetime; + debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE; // Visuals heretades debris->brightness = brightness; @@ -223,116 +212,109 @@ 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; - } - } - - // Aplica fricció a una velocity angular (genèric per a trajectòria i visual). - static void decayAngular(float& vel, float friction, float delta_time) { - if (std::abs(vel) <= 0.01F) { - vel = 0.0F; - return; - } - const float SIGN = (vel > 0) ? 1.0F : -1.0F; - vel -= SIGN * friction * delta_time; - if ((vel > 0) != (SIGN > 0)) { - vel = 0.0F; - } - } - - // Rota el vector velocity (trajectòria corba) i aplica fricció a ambdues - // velocitats angulars (trajectòria i visual). - static void applyAngularDynamics(Debris& debris, float delta_time) { - // Rotar vector de velocity amb matriu 2D (només si encara gira la trajectòria) - if (std::abs(debris.velocitat_rot) > 0.01F) { - 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); - } - - // Decay independent de les dues velocitats angulars. - decayAngular(debris.velocitat_rot, - Defaults::Physics::Debris::FRICCIO_ANGULAR, - delta_time); - decayAngular(debris.velocitat_rot_visual, - Defaults::Physics::Debris::FRICCIO_VISUAL, - delta_time); - } - void DebrisManager::update(float delta_time) { for (auto& debris : debris_pool_) { if (!debris.active) { continue; } - if (!updateLifecycle(debris, delta_time)) { - 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; + } } - applyLinearFriction(debris, delta_time); - applyAngularDynamics(debris, delta_time); + // 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)); - // Posició del centre: integra velocity (separat de p1/p2). - debris.centro.x += debris.velocity.x * delta_time; - debris.centro.y += debris.velocity.y * delta_time; + if (speed > 1.0F) { + // Calcular direcció normalitzada + float dir_x = debris.velocity.x / speed; + float dir_y = debris.velocity.y / speed; - // Acumular rotació visual. Modulada per size_factor: durant - // INTACTE (size=1) no afecta, però quan el segment mengua la - // rotació també s'apaga. - debris.angle_rotacio += debris.velocitat_rot_visual * debris.size_factor * delta_time; + // Aplicar aceleración negativa (fricció) + float nova_speed = speed + (debris.acceleration * delta_time); + nova_speed = std::max(nova_speed, 0.0F); - // Reconstruir p1/p2 des de la geometria autoritaritzada: - // centro + (cos/sin(original_angle + angle_rotacio)) × original_half_length × size_factor - // No iteratiu — evita la rotació quadràtica i el shrink exponencial. - const float CURRENT_ANGLE = debris.original_angle + debris.angle_rotacio; - const float HALF_LEN = debris.original_half_length * std::max(0.0F, debris.size_factor); - const float COS_A = std::cos(CURRENT_ANGLE); - const float SIN_A = std::sin(CURRENT_ANGLE); - debris.p1.x = debris.centro.x - (HALF_LEN * COS_A); - debris.p1.y = debris.centro.y - (HALF_LEN * SIN_A); - debris.p2.x = debris.centro.x + (HALF_LEN * COS_A); - debris.p2.y = debris.centro.y + (HALF_LEN * SIN_A); + 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 + 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 + 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 + 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 original_angle = std::atan2(dy, dx); + float new_angle = original_angle + debris.angle_rotacio; + + debris.p1.x = centro.x - (half_length * std::cos(new_angle)); + debris.p1.y = centro.y - (half_length * std::sin(new_angle)); + debris.p2.x = centro.x + (half_length * std::cos(new_angle)); + debris.p2.y = centro.y + (half_length * std::sin(new_angle)); } } diff --git a/source/game/effects/debris_manager.hpp b/source/game/effects/debris_manager.hpp index 8647b1a..6681deb 100644 --- a/source/game/effects/debris_manager.hpp +++ b/source/game/effects/debris_manager.hpp @@ -34,10 +34,9 @@ namespace Effects { // - velocitat_objecte: velocity de l'objecte que explota (px/s, per defecte 0) // - velocitat_angular: velocity angular heretada (rad/s, per defecte 0) // - factor_herencia_visual: factor de herència rotación visual (0.0-1.0, per defecte 0.0) - // - lifetime: temps INTACTE del debris (s, per defecte INTACT_TIME) + // - lifetime: temps de vida del debris (s, per defecte TEMPS_VIDA = 2s) // - friction: desacceleració del debris (px/s², per defecte ACCELERACIO = -60) // - segment_multiplier: nombre de còpies per segment (per defecte 1 = sense duplicar) - // - shrink_rate: velocity de mengua durant MENGUANT (1/s, per defecte SHRINK_RATE) void explode(const std::shared_ptr& shape, const Vec2& centro, float angle, @@ -49,10 +48,9 @@ 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::INTACT_TIME, + float lifetime = Defaults::Physics::Debris::TEMPS_VIDA, float friction = Defaults::Physics::Debris::ACCELERACIO, - int segment_multiplier = 1, - float shrink_rate = Defaults::Physics::Debris::SHRINK_RATE); + int segment_multiplier = 1); // Actualitzar todos los fragments active void update(float delta_time); @@ -88,7 +86,7 @@ namespace Effects { -> std::vector>; // Inicialitza un debris en un slot lliure i el deixa actiu. Retorna // false si el pool está ple (la cridadora ha d'aturar el bucle). - auto spawnDebris(const Vec2& world_p1, const Vec2& world_p2, const Vec2& centro, float velocitat_base, float brightness, const Vec2& velocitat_objecte, float velocitat_angular, float factor_herencia_visual, SDL_Color color, float lifetime, float friction, float shrink_rate) -> bool; + auto spawnDebris(const Vec2& world_p1, const Vec2& world_p2, const Vec2& centro, float velocitat_base, float brightness, const Vec2& velocitat_objecte, float velocitat_angular, float factor_herencia_visual, SDL_Color color, float lifetime, float friction) -> bool; static void applyAngularVelocity(Debris& debris, const Vec2& direccio, float velocitat_angular); static void applyVisualRotation(Debris& debris, float velocitat_angular, float factor_herencia_visual); }; diff --git a/source/game/systems/collision_system.cpp b/source/game/systems/collision_system.cpp index 8a63fc5..85c362a 100644 --- a/source/game/systems/collision_system.cpp +++ b/source/game/systems/collision_system.cpp @@ -74,10 +74,9 @@ namespace Systems::Collision { 0.0F, // sin herencia visual Defaults::Sound::EXPLOSION, COLOR, - Defaults::Physics::Debris::ENEMY_INTACT_TIME, + Defaults::Physics::Debris::ENEMY_LIFETIME, Defaults::Physics::Debris::ENEMY_FRICTION, - Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER, - Defaults::Physics::Debris::ENEMY_SHRINK_RATE); + Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER); } } // anonymous namespace