feat(debris): modelo INTACTO→MENGUANDO→0 (sin pop, fade-out por tamaño)
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user