fix(debris): bug rotacion cuadratica + shrink exponencial; geometria autoritativa
This commit is contained in:
@@ -52,7 +52,8 @@ namespace Defaults::Physics {
|
|||||||
|
|
||||||
// Tuneig específic de l'explosió d'enemic (overrides als defaults
|
// Tuneig específic de l'explosió d'enemic (overrides als defaults
|
||||||
// que es passen com a paràmetres opcionals a explode()).
|
// que es passen com a paràmetres opcionals a explode()).
|
||||||
constexpr float ENEMY_INTACT_TIME = 1.5F; // Temps intacte abans de menguar (s)
|
constexpr float ENEMY_INTACT_TIME = 0.5F; // Temps intacte abans de menguar (s) — comencen aviat
|
||||||
|
constexpr float ENEMY_SHRINK_RATE = 0.33F; // 1/3 per segon → 3s de fade-out lent
|
||||||
constexpr float ENEMY_FRICTION = -30.0F; // Fricció més suau perquè s'estenguin 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)
|
constexpr int ENEMY_SEGMENT_MULTIPLIER = 3; // Còpies de cada segment (5 cares × 3 = 15 trossos)
|
||||||
|
|
||||||
|
|||||||
@@ -9,18 +9,29 @@
|
|||||||
namespace Effects {
|
namespace Effects {
|
||||||
|
|
||||||
// Debris: un segment de línia que vola perpendicular a sí mismo
|
// Debris: un segment de línia que vola perpendicular a sí mismo
|
||||||
// Representa un fragment de una shape destruïda (ship, enemy, bullet)
|
// 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).
|
||||||
struct Debris {
|
struct Debris {
|
||||||
// Geometria del segment (2 points en coordenades mundials)
|
// Geometria del segment (2 points en coordenades mundials, derivats)
|
||||||
Vec2 p1; // Vec2 inicial del segment
|
Vec2 p1;
|
||||||
Vec2 p2; // Vec2 final del segment
|
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)
|
||||||
|
|
||||||
// Física
|
// Física
|
||||||
Vec2 velocity; // Velocidad en px/s (components x, y)
|
Vec2 velocity; // Velocidad en px/s (components x, y)
|
||||||
float acceleration; // Aceleración negativa (fricció) en px/s²
|
float acceleration; // Aceleración negativa (fricció) en px/s²
|
||||||
|
|
||||||
// Rotación
|
// Rotación
|
||||||
float angle_rotacio; // Angle de rotación acumulat (radians)
|
float angle_rotacio; // Acumulat de rotació visual des del spawn (radians)
|
||||||
float velocitat_rot; // Velocidad de rotación de TRAYECTORIA (rad/s)
|
float velocitat_rot; // Velocidad de rotación de TRAYECTORIA (rad/s)
|
||||||
float velocitat_rot_visual; // Velocidad de rotación VISUAL del segment (rad/s)
|
float velocitat_rot_visual; // Velocidad de rotación VISUAL del segment (rad/s)
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ namespace Effects {
|
|||||||
SDL_Color color,
|
SDL_Color color,
|
||||||
float lifetime,
|
float lifetime,
|
||||||
float friction,
|
float friction,
|
||||||
int segment_multiplier) {
|
int segment_multiplier,
|
||||||
|
float shrink_rate) {
|
||||||
if (!shape || !shape->isValid()) {
|
if (!shape || !shape->isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -79,7 +80,7 @@ namespace Effects {
|
|||||||
Vec2 world_p2 = transformPoint(local_p2, shape_centre, centro, angle, scale);
|
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
|
// 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)) {
|
if (!spawnDebris(world_p1, world_p2, centro, velocitat_base, brightness, velocitat_objecte, velocitat_angular, factor_herencia_visual, color, lifetime, friction, shrink_rate)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,14 +106,21 @@ namespace Effects {
|
|||||||
return segments;
|
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) -> 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, float shrink_rate) -> bool {
|
||||||
Debris* debris = findFreeSlot();
|
Debris* debris = findFreeSlot();
|
||||||
if (debris == nullptr) {
|
if (debris == nullptr) {
|
||||||
std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n";
|
std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Geometria
|
// 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;
|
||||||
debris->p1 = world_p1;
|
debris->p1 = world_p1;
|
||||||
debris->p2 = world_p2;
|
debris->p2 = world_p2;
|
||||||
|
|
||||||
@@ -138,12 +146,12 @@ namespace Effects {
|
|||||||
|
|
||||||
// Estat inicial: INTACTE durant `lifetime` segons (o fins que la
|
// Estat inicial: INTACTE durant `lifetime` segons (o fins que la
|
||||||
// velocity baixi de SHRINK_SPEED_THRESHOLD). Després MENGUANT a
|
// velocity baixi de SHRINK_SPEED_THRESHOLD). Després MENGUANT a
|
||||||
// SHRINK_RATE per segon fins arribar a size_factor=0 → mor.
|
// shrink_rate per segon fins arribar a size_factor=0 → mor.
|
||||||
debris->temps_vida = 0.0F;
|
debris->temps_vida = 0.0F;
|
||||||
debris->intact_time = lifetime;
|
debris->intact_time = lifetime;
|
||||||
debris->shrinking = false;
|
debris->shrinking = false;
|
||||||
debris->size_factor = 1.0F;
|
debris->size_factor = 1.0F;
|
||||||
debris->shrink_rate = Defaults::Physics::Debris::SHRINK_RATE;
|
debris->shrink_rate = shrink_rate;
|
||||||
|
|
||||||
// Visuals heretades
|
// Visuals heretades
|
||||||
debris->brightness = brightness;
|
debris->brightness = brightness;
|
||||||
@@ -305,32 +313,26 @@ namespace Effects {
|
|||||||
applyLinearFriction(debris, delta_time);
|
applyLinearFriction(debris, delta_time);
|
||||||
applyAngularDynamics(debris, delta_time);
|
applyAngularDynamics(debris, delta_time);
|
||||||
|
|
||||||
// Posició: nou centre = centre antic + velocity · dt
|
// Posició del centre: integra velocity (separat de p1/p2).
|
||||||
Vec2 centro = {.x = (debris.p1.x + debris.p2.x) / 2.0F,
|
debris.centro.x += debris.velocity.x * delta_time;
|
||||||
.y = (debris.p1.y + debris.p2.y) / 2.0F};
|
debris.centro.y += debris.velocity.y * delta_time;
|
||||||
centro.x += debris.velocity.x * delta_time;
|
|
||||||
centro.y += debris.velocity.y * delta_time;
|
|
||||||
|
|
||||||
// Rotació visual del segment. Modulada per size_factor: durant
|
// Acumular rotació visual. Modulada per size_factor: durant
|
||||||
// INTACTE (size=1) no afecta, però quan el segment mengua la
|
// INTACTE (size=1) no afecta, però quan el segment mengua la
|
||||||
// rotació també s'apaga, evitant l'efecte "hèlix d'avió" quan
|
// rotació també s'apaga.
|
||||||
// el segment es fa molt petit.
|
|
||||||
debris.angle_rotacio += debris.velocitat_rot_visual * debris.size_factor * delta_time;
|
debris.angle_rotacio += debris.velocitat_rot_visual * debris.size_factor * delta_time;
|
||||||
|
|
||||||
// Mida actual = size_factor (1.0 intacte, decreix durant MENGUANT)
|
// Reconstruir p1/p2 des de la geometria autoritaritzada:
|
||||||
const float SHRINK_FACTOR = std::max(0.0F, debris.size_factor);
|
// centro + (cos/sin(original_angle + angle_rotacio)) × original_half_length × size_factor
|
||||||
float dx = debris.p2.x - debris.p1.x;
|
// No iteratiu — evita la rotació quadràtica i el shrink exponencial.
|
||||||
float dy = debris.p2.y - debris.p1.y;
|
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);
|
||||||
// 7. Reconstruir segment con nueva mida i rotación
|
const float COS_A = std::cos(CURRENT_ANGLE);
|
||||||
float half_length = std::sqrt((dx * dx) + (dy * dy)) * SHRINK_FACTOR / 2.0F;
|
const float SIN_A = std::sin(CURRENT_ANGLE);
|
||||||
float original_angle = std::atan2(dy, dx);
|
debris.p1.x = debris.centro.x - (HALF_LEN * COS_A);
|
||||||
float new_angle = original_angle + debris.angle_rotacio;
|
debris.p1.y = debris.centro.y - (HALF_LEN * SIN_A);
|
||||||
|
debris.p2.x = debris.centro.x + (HALF_LEN * COS_A);
|
||||||
debris.p1.x = centro.x - (half_length * std::cos(new_angle));
|
debris.p2.y = debris.centro.y + (HALF_LEN * SIN_A);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,10 @@ namespace Effects {
|
|||||||
// - velocitat_objecte: velocity de l'objecte que explota (px/s, per defecte 0)
|
// - velocitat_objecte: velocity de l'objecte que explota (px/s, per defecte 0)
|
||||||
// - velocitat_angular: velocity angular heretada (rad/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)
|
// - factor_herencia_visual: factor de herència rotación visual (0.0-1.0, per defecte 0.0)
|
||||||
// - lifetime: temps de vida del debris (s, per defecte TEMPS_VIDA = 2s)
|
// - lifetime: temps INTACTE del debris (s, per defecte INTACT_TIME)
|
||||||
// - friction: desacceleració del debris (px/s², per defecte ACCELERACIO = -60)
|
// - friction: desacceleració del debris (px/s², per defecte ACCELERACIO = -60)
|
||||||
// - segment_multiplier: nombre de còpies per segment (per defecte 1 = sense duplicar)
|
// - 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<Graphics::Shape>& shape,
|
void explode(const std::shared_ptr<Graphics::Shape>& shape,
|
||||||
const Vec2& centro,
|
const Vec2& centro,
|
||||||
float angle,
|
float angle,
|
||||||
@@ -50,7 +51,8 @@ namespace Effects {
|
|||||||
SDL_Color color = {0, 0, 0, 0}, // alpha==0 → fragmentos usan oscilador global
|
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::INTACT_TIME,
|
||||||
float friction = Defaults::Physics::Debris::ACCELERACIO,
|
float friction = Defaults::Physics::Debris::ACCELERACIO,
|
||||||
int segment_multiplier = 1);
|
int segment_multiplier = 1,
|
||||||
|
float shrink_rate = Defaults::Physics::Debris::SHRINK_RATE);
|
||||||
|
|
||||||
// Actualitzar todos los fragments active
|
// Actualitzar todos los fragments active
|
||||||
void update(float delta_time);
|
void update(float delta_time);
|
||||||
@@ -86,7 +88,7 @@ namespace Effects {
|
|||||||
-> std::vector<std::pair<Vec2, Vec2>>;
|
-> std::vector<std::pair<Vec2, Vec2>>;
|
||||||
// Inicialitza un debris en un slot lliure i el deixa actiu. Retorna
|
// 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).
|
// 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) -> 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, float shrink_rate) -> bool;
|
||||||
static void applyAngularVelocity(Debris& debris, const Vec2& direccio, float velocitat_angular);
|
static void applyAngularVelocity(Debris& debris, const Vec2& direccio, float velocitat_angular);
|
||||||
static void applyVisualRotation(Debris& debris, float velocitat_angular, float factor_herencia_visual);
|
static void applyVisualRotation(Debris& debris, float velocitat_angular, float factor_herencia_visual);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ namespace Systems::Collision {
|
|||||||
COLOR,
|
COLOR,
|
||||||
Defaults::Physics::Debris::ENEMY_INTACT_TIME,
|
Defaults::Physics::Debris::ENEMY_INTACT_TIME,
|
||||||
Defaults::Physics::Debris::ENEMY_FRICTION,
|
Defaults::Physics::Debris::ENEMY_FRICTION,
|
||||||
Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER);
|
Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER,
|
||||||
|
Defaults::Physics::Debris::ENEMY_SHRINK_RATE);
|
||||||
}
|
}
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user