feat(debris): vida híbrida (mínima + umbral velocidad) + multiplier para enemigos
This commit is contained in:
@@ -25,9 +25,15 @@ namespace Defaults::Physics {
|
|||||||
constexpr float ACCELERACIO = -60.0F; // Fricció/desacceleració (px/s²)
|
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_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 ROTACIO_MAX = 0.3F; // Rotación màxima (rad/s ~17.2°/s)
|
||||||
constexpr float TEMPS_VIDA = 2.0F; // Duració màxima (segons) - enemy/bullet debris
|
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 lifetime (matches DEATH_DURATION)
|
constexpr float TEMPS_VIDA_NAU = 3.0F; // Ship debris min lifetime (matches DEATH_DURATION)
|
||||||
constexpr float SHRINK_RATE = 0.5F; // Reducció de mida (factor/s)
|
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;
|
||||||
|
|
||||||
// Herència de velocity angular (trayectorias curvas)
|
// Herència de velocity angular (trayectorias curvas)
|
||||||
constexpr float FACTOR_HERENCIA_MIN = 0.7F; // Mínimo 70% del drotacio heredat
|
constexpr float FACTOR_HERENCIA_MIN = 0.7F; // Mínimo 70% del drotacio heredat
|
||||||
@@ -41,6 +47,12 @@ namespace Defaults::Physics {
|
|||||||
// 1.0 = inèrcia completa; >1.0 amplifica la deriva; <1.0 la atenua.
|
// 1.0 = inèrcia completa; >1.0 amplifica la deriva; <1.0 la atenua.
|
||||||
constexpr float ENEMY_VELOCITY_INHERITANCE = 1.0F;
|
constexpr float ENEMY_VELOCITY_INHERITANCE = 1.0F;
|
||||||
|
|
||||||
|
// 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_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)
|
||||||
|
|
||||||
// Angular velocity sin for trajectory inheritance
|
// Angular velocity sin for trajectory inheritance
|
||||||
// Excess above this threshold is converted to tangential linear velocity
|
// Excess above this threshold is converted to tangential linear velocity
|
||||||
// Prevents "vortex trap" problem with high-rotation enemies
|
// Prevents "vortex trap" problem with high-rotation enemies
|
||||||
|
|||||||
@@ -8,15 +8,15 @@
|
|||||||
|
|
||||||
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)
|
||||||
struct Debris {
|
struct Debris {
|
||||||
// Geometria del segment (2 points en coordenades mundials)
|
// Geometria del segment (2 points en coordenades mundials)
|
||||||
Vec2 p1; // Vec2 inicial del segment
|
Vec2 p1; // Vec2 inicial del segment
|
||||||
Vec2 p2; // Vec2 final del segment
|
Vec2 p2; // Vec2 final del segment
|
||||||
|
|
||||||
// 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
|
||||||
@@ -25,9 +25,12 @@ struct Debris {
|
|||||||
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)
|
||||||
|
|
||||||
// Estat de vida
|
// Estat de vida
|
||||||
float temps_vida; // Temps transcorregut (segons)
|
// Política: viu sempre durant min_lifetime, després mor quan
|
||||||
float temps_max; // Temps de vida màxim (segons)
|
// |velocity| < MIN_SPEED_TO_DIE (definit en Defaults). Així els
|
||||||
bool active; // Está active?
|
// 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)
|
// Shrinking (reducció de distancia entre points)
|
||||||
float factor_shrink; // Factor de reducció per segon (0.0-1.0)
|
float factor_shrink; // Factor de reducció per segon (0.0-1.0)
|
||||||
@@ -35,6 +38,6 @@ struct Debris {
|
|||||||
// Rendering
|
// Rendering
|
||||||
float brightness; // Factor de brightness (0.0-1.0, heretat de l'objecte original)
|
float brightness; // Factor de brightness (0.0-1.0, heretat de l'objecte original)
|
||||||
SDL_Color color{}; // Color heredado del padre. alpha==0 → usa global oscilador
|
SDL_Color color{}; // Color heredado del padre. alpha==0 → usa global oscilador
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Effects
|
} // namespace Effects
|
||||||
|
|||||||
@@ -14,375 +14,388 @@
|
|||||||
|
|
||||||
namespace Effects {
|
namespace Effects {
|
||||||
|
|
||||||
// Helper: transformar point con rotación, scale i traslación
|
// Helper: transformar point con rotación, scale i traslación
|
||||||
// (Copiat de shape_renderer.cpp:12-34)
|
// (Copiat de shape_renderer.cpp:12-34)
|
||||||
static auto transformPoint(const Vec2& point, const Vec2& shape_centre, const Vec2& position, float angle, float scale) -> Vec2 {
|
static auto transformPoint(const Vec2& point, const Vec2& shape_centre, const Vec2& position, float angle, float scale) -> Vec2 {
|
||||||
// 1. Centrar el point respecte al centro de la shape
|
// 1. Centrar el point respecte al centro de la shape
|
||||||
float centered_x = point.x - shape_centre.x;
|
float centered_x = point.x - shape_centre.x;
|
||||||
float centered_y = point.y - shape_centre.y;
|
float centered_y = point.y - shape_centre.y;
|
||||||
|
|
||||||
// 2. Aplicar scale al point centrat
|
// 2. Aplicar scale al point centrat
|
||||||
float scaled_x = centered_x * scale;
|
float scaled_x = centered_x * scale;
|
||||||
float scaled_y = centered_y * scale;
|
float scaled_y = centered_y * scale;
|
||||||
|
|
||||||
// 3. Aplicar rotación
|
// 3. Aplicar rotación
|
||||||
float cos_a = std::cos(angle);
|
float cos_a = std::cos(angle);
|
||||||
float sin_a = std::sin(angle);
|
float sin_a = std::sin(angle);
|
||||||
|
|
||||||
float rotated_x = (scaled_x * cos_a) - (scaled_y * sin_a);
|
float rotated_x = (scaled_x * cos_a) - (scaled_y * sin_a);
|
||||||
float rotated_y = (scaled_x * sin_a) + (scaled_y * cos_a);
|
float rotated_y = (scaled_x * sin_a) + (scaled_y * cos_a);
|
||||||
|
|
||||||
// 4. Aplicar traslación a posición mundial
|
// 4. Aplicar traslación a posición mundial
|
||||||
return {.x = rotated_x + position.x, .y = rotated_y + position.y};
|
return {.x = rotated_x + position.x, .y = rotated_y + position.y};
|
||||||
}
|
|
||||||
|
|
||||||
DebrisManager::DebrisManager(Rendering::Renderer* renderer)
|
|
||||||
: renderer_(renderer) {
|
|
||||||
// Inicialitzar todos los debris como inactius
|
|
||||||
for (auto& debris : debris_pool_) {
|
|
||||||
debris.active = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebrisManager::explode(const std::shared_ptr<Graphics::Shape>& shape,
|
|
||||||
const Vec2& centro,
|
|
||||||
float angle,
|
|
||||||
float scale,
|
|
||||||
float velocitat_base,
|
|
||||||
float brightness,
|
|
||||||
const Vec2& velocitat_objecte,
|
|
||||||
float velocitat_angular,
|
|
||||||
float factor_herencia_visual,
|
|
||||||
const std::string& sound,
|
|
||||||
SDL_Color color) {
|
|
||||||
if (!shape || !shape->isValid()) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reproducir sonido de explosión
|
DebrisManager::DebrisManager(Rendering::Renderer* renderer)
|
||||||
Audio::get()->playSound(sound, Audio::Group::GAME);
|
: renderer_(renderer) {
|
||||||
|
// Inicialitzar todos los debris como inactius
|
||||||
|
for (auto& debris : debris_pool_) {
|
||||||
|
debris.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const Vec2& shape_centre = shape->getCenter();
|
void DebrisManager::explode(const std::shared_ptr<Graphics::Shape>& shape,
|
||||||
|
const Vec2& centro,
|
||||||
|
float angle,
|
||||||
|
float scale,
|
||||||
|
float velocitat_base,
|
||||||
|
float brightness,
|
||||||
|
const Vec2& velocitat_objecte,
|
||||||
|
float velocitat_angular,
|
||||||
|
float factor_herencia_visual,
|
||||||
|
const std::string& sound,
|
||||||
|
SDL_Color color,
|
||||||
|
float lifetime,
|
||||||
|
float friction,
|
||||||
|
int segment_multiplier) {
|
||||||
|
if (!shape || !shape->isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& primitive : shape->getPrimitives()) {
|
// Reproducir sonido de explosión
|
||||||
for (const auto& [local_p1, local_p2] : extractSegments(primitive)) {
|
Audio::get()->playSound(sound, Audio::Group::GAME);
|
||||||
// Transformar points locals → coordenades mundials
|
|
||||||
Vec2 world_p1 = transformPoint(local_p1, 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
|
const Vec2& shape_centre = shape->getCenter();
|
||||||
if (!spawnDebris(world_p1, world_p2, centro, velocitat_base, brightness,
|
|
||||||
velocitat_objecte, velocitat_angular,
|
// Multiplier: cada segment s'emet N vegades amb direccions aleatòries
|
||||||
factor_herencia_visual, color)) {
|
// distintes (la variació ±15° de computeExplosionDirection ho garanteix).
|
||||||
return;
|
const int COPIES = std::max(1, segment_multiplier);
|
||||||
|
|
||||||
|
for (int copy = 0; copy < COPIES; copy++) {
|
||||||
|
for (const auto& primitive : shape->getPrimitives()) {
|
||||||
|
for (const auto& [local_p1, local_p2] : extractSegments(primitive)) {
|
||||||
|
// Transformar points locals → coordenades mundials
|
||||||
|
Vec2 world_p1 = transformPoint(local_p1, 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
|
||||||
|
if (!spawnDebris(world_p1, world_p2, centro, velocitat_base, brightness, velocitat_objecte, velocitat_angular, factor_herencia_visual, color, lifetime, friction)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto DebrisManager::extractSegments(const Graphics::ShapePrimitive& primitive)
|
auto DebrisManager::extractSegments(const Graphics::ShapePrimitive& primitive)
|
||||||
-> std::vector<std::pair<Vec2, Vec2>> {
|
-> std::vector<std::pair<Vec2, Vec2>> {
|
||||||
std::vector<std::pair<Vec2, Vec2>> segments;
|
std::vector<std::pair<Vec2, Vec2>> segments;
|
||||||
|
|
||||||
if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
|
if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
|
||||||
// Polyline: extreure segments consecutius
|
// Polyline: extreure segments consecutius
|
||||||
for (size_t i = 0; i + 1 < primitive.points.size(); i++) {
|
for (size_t i = 0; i + 1 < primitive.points.size(); i++) {
|
||||||
segments.emplace_back(primitive.points[i], primitive.points[i + 1]);
|
segments.emplace_back(primitive.points[i], primitive.points[i + 1]);
|
||||||
|
}
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
// PrimitiveType::LINE: un únic segment (si té els 2 punts)
|
||||||
|
if (primitive.points.size() >= 2) {
|
||||||
|
segments.emplace_back(primitive.points[0], primitive.points[1]);
|
||||||
}
|
}
|
||||||
return segments;
|
return segments;
|
||||||
}
|
}
|
||||||
// PrimitiveType::LINE: un únic segment (si té els 2 punts)
|
|
||||||
if (primitive.points.size() >= 2) {
|
|
||||||
segments.emplace_back(primitive.points[0], primitive.points[1]);
|
|
||||||
}
|
|
||||||
return segments;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto DebrisManager::spawnDebris(const Vec2& world_p1, const Vec2& world_p2,
|
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 {
|
||||||
const Vec2& centro, float velocitat_base, float brightness,
|
Debris* debris = findFreeSlot();
|
||||||
const Vec2& velocitat_objecte, float velocitat_angular,
|
if (debris == nullptr) {
|
||||||
float factor_herencia_visual, SDL_Color color) -> bool {
|
std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n";
|
||||||
Debris* debris = findFreeSlot();
|
return false;
|
||||||
if (debris == nullptr) {
|
|
||||||
std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Geometria
|
|
||||||
debris->p1 = world_p1;
|
|
||||||
debris->p2 = world_p2;
|
|
||||||
|
|
||||||
// Direcció radial (desde el centro hacia el segment)
|
|
||||||
Vec2 direccio = computeExplosionDirection(world_p1, world_p2, centro);
|
|
||||||
|
|
||||||
// Velocidad inicial (base ± variació aleatòria + velocity heretada de l'objecte)
|
|
||||||
float speed =
|
|
||||||
velocitat_base +
|
|
||||||
(((std::rand() / static_cast<float>(RAND_MAX)) * 2.0F - 1.0F) *
|
|
||||||
Defaults::Physics::Debris::VARIACIO_VELOCITAT);
|
|
||||||
debris->velocity.x = (direccio.x * speed) + velocitat_objecte.x;
|
|
||||||
debris->velocity.y = (direccio.y * speed) + velocitat_objecte.y;
|
|
||||||
debris->acceleration = Defaults::Physics::Debris::ACCELERACIO;
|
|
||||||
|
|
||||||
// Rotación de trayectoria (con conversió a tangencial si excedeix cap)
|
|
||||||
applyAngularVelocity(*debris, direccio, velocitat_angular);
|
|
||||||
|
|
||||||
// Rotación visual (proporcional o aleatòria)
|
|
||||||
applyVisualRotation(*debris, velocitat_angular, factor_herencia_visual);
|
|
||||||
|
|
||||||
debris->angle_rotacio = 0.0F;
|
|
||||||
|
|
||||||
// Vida i shrinking
|
|
||||||
debris->temps_vida = 0.0F;
|
|
||||||
debris->temps_max = Defaults::Physics::Debris::TEMPS_VIDA;
|
|
||||||
debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE;
|
|
||||||
|
|
||||||
// Visuals heretades
|
|
||||||
debris->brightness = brightness;
|
|
||||||
debris->color = color;
|
|
||||||
|
|
||||||
debris->active = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebrisManager::applyAngularVelocity(Debris& debris, const Vec2& direccio,
|
|
||||||
float velocitat_angular) {
|
|
||||||
if (std::abs(velocitat_angular) <= 0.01F) {
|
|
||||||
debris.velocitat_rot = 0.0F; // Nave: sin curvas
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FASE 1: Aplicar herència i variació
|
|
||||||
float factor_herencia =
|
|
||||||
Defaults::Physics::Debris::FACTOR_HERENCIA_MIN +
|
|
||||||
((std::rand() / static_cast<float>(RAND_MAX)) *
|
|
||||||
(Defaults::Physics::Debris::FACTOR_HERENCIA_MAX -
|
|
||||||
Defaults::Physics::Debris::FACTOR_HERENCIA_MIN));
|
|
||||||
float velocitat_ang_heretada = velocitat_angular * factor_herencia;
|
|
||||||
float variacio = ((std::rand() / static_cast<float>(RAND_MAX)) * 0.2F) - 0.1F;
|
|
||||||
velocitat_ang_heretada *= (1.0F + variacio);
|
|
||||||
|
|
||||||
// FASE 2: Cap a la velocity màxima; l'excés es converteix en tangencial
|
|
||||||
constexpr float CAP = Defaults::Physics::Debris::VELOCITAT_ROT_MAX;
|
|
||||||
float abs_ang = std::abs(velocitat_ang_heretada);
|
|
||||||
float sign_ang = (velocitat_ang_heretada >= 0.0F) ? 1.0F : -1.0F;
|
|
||||||
|
|
||||||
if (abs_ang <= CAP) {
|
|
||||||
debris.velocitat_rot = velocitat_ang_heretada;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Excés: converteix l'excés de velocitat angular en velocitat tangencial lineal
|
|
||||||
float excess = abs_ang - CAP;
|
|
||||||
constexpr float RADIUS = 20.0F; // Radi típic de la shape (enemigos = 20 px)
|
|
||||||
float v_tangential = excess * RADIUS;
|
|
||||||
|
|
||||||
// Direcció tangencial: perpendicular a la radial (90° CCW): tangent = (-dy, dx)
|
|
||||||
debris.velocity.x += -direccio.y * v_tangential;
|
|
||||||
debris.velocity.y += direccio.x * v_tangential;
|
|
||||||
|
|
||||||
// Velocitat angular limitada al cap (preservant el signe)
|
|
||||||
debris.velocitat_rot = sign_ang * CAP;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebrisManager::applyVisualRotation(Debris& debris, float velocitat_angular,
|
|
||||||
float factor_herencia_visual) {
|
|
||||||
if (factor_herencia_visual > 0.01F && std::abs(velocitat_angular) > 0.01F) {
|
|
||||||
// Heredar rotación visual con factor proporcional + ±5% de variació
|
|
||||||
debris.velocitat_rot_visual = debris.velocitat_rot * factor_herencia_visual;
|
|
||||||
float variacio_visual =
|
|
||||||
((std::rand() / static_cast<float>(RAND_MAX)) * 0.1F) - 0.05F;
|
|
||||||
debris.velocitat_rot_visual *= (1.0F + variacio_visual);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rotación visual aleatòria (factor = 0.0 o sin velocidad angular)
|
|
||||||
debris.velocitat_rot_visual =
|
|
||||||
Defaults::Physics::Debris::ROTACIO_MIN +
|
|
||||||
((std::rand() / static_cast<float>(RAND_MAX)) *
|
|
||||||
(Defaults::Physics::Debris::ROTACIO_MAX -
|
|
||||||
Defaults::Physics::Debris::ROTACIO_MIN));
|
|
||||||
|
|
||||||
// 50% probabilitat de rotación en sentit contrari
|
|
||||||
if (std::rand() % 2 == 0) {
|
|
||||||
debris.velocitat_rot_visual = -debris.velocitat_rot_visual;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebrisManager::update(float delta_time) {
|
|
||||||
for (auto& debris : debris_pool_) {
|
|
||||||
if (!debris.active) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Actualitzar time de vida
|
// Geometria
|
||||||
debris.temps_vida += delta_time;
|
debris->p1 = world_p1;
|
||||||
|
debris->p2 = world_p2;
|
||||||
|
|
||||||
// Desactivar si ha superat time màxim
|
// Direcció radial (desde el centro hacia el segment)
|
||||||
if (debris.temps_vida >= debris.temps_max) {
|
Vec2 direccio = computeExplosionDirection(world_p1, world_p2, centro);
|
||||||
debris.active = false;
|
|
||||||
continue;
|
// Velocidad inicial (base ± variació aleatòria + velocity heretada de l'objecte)
|
||||||
|
float speed =
|
||||||
|
velocitat_base +
|
||||||
|
(((std::rand() / static_cast<float>(RAND_MAX)) * 2.0F - 1.0F) *
|
||||||
|
Defaults::Physics::Debris::VARIACIO_VELOCITAT);
|
||||||
|
debris->velocity.x = (direccio.x * speed) + velocitat_objecte.x;
|
||||||
|
debris->velocity.y = (direccio.y * speed) + velocitat_objecte.y;
|
||||||
|
debris->acceleration = friction;
|
||||||
|
|
||||||
|
// Rotación de trayectoria (con conversió a tangencial si excedeix cap)
|
||||||
|
applyAngularVelocity(*debris, direccio, velocitat_angular);
|
||||||
|
|
||||||
|
// Rotación visual (proporcional o aleatòria)
|
||||||
|
applyVisualRotation(*debris, velocitat_angular, factor_herencia_visual);
|
||||||
|
|
||||||
|
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.
|
||||||
|
debris->temps_vida = 0.0F;
|
||||||
|
debris->min_lifetime = lifetime;
|
||||||
|
debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE;
|
||||||
|
|
||||||
|
// Visuals heretades
|
||||||
|
debris->brightness = brightness;
|
||||||
|
debris->color = color;
|
||||||
|
|
||||||
|
debris->active = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebrisManager::applyAngularVelocity(Debris& debris, const Vec2& direccio, float velocitat_angular) {
|
||||||
|
if (std::abs(velocitat_angular) <= 0.01F) {
|
||||||
|
debris.velocitat_rot = 0.0F; // Nave: sin curvas
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Actualitzar velocity (desacceleració)
|
// FASE 1: Aplicar herència i variació
|
||||||
// Aplicar fricció en la direcció del movement
|
float factor_herencia =
|
||||||
float speed = std::sqrt((debris.velocity.x * debris.velocity.x) +
|
Defaults::Physics::Debris::FACTOR_HERENCIA_MIN +
|
||||||
(debris.velocity.y * debris.velocity.y));
|
((std::rand() / static_cast<float>(RAND_MAX)) *
|
||||||
|
(Defaults::Physics::Debris::FACTOR_HERENCIA_MAX -
|
||||||
|
Defaults::Physics::Debris::FACTOR_HERENCIA_MIN));
|
||||||
|
float velocitat_ang_heretada = velocitat_angular * factor_herencia;
|
||||||
|
float variacio = ((std::rand() / static_cast<float>(RAND_MAX)) * 0.2F) - 0.1F;
|
||||||
|
velocitat_ang_heretada *= (1.0F + variacio);
|
||||||
|
|
||||||
if (speed > 1.0F) {
|
// FASE 2: Cap a la velocity màxima; l'excés es converteix en tangencial
|
||||||
// Calcular direcció normalitzada
|
constexpr float CAP = Defaults::Physics::Debris::VELOCITAT_ROT_MAX;
|
||||||
float dir_x = debris.velocity.x / speed;
|
float abs_ang = std::abs(velocitat_ang_heretada);
|
||||||
float dir_y = debris.velocity.y / speed;
|
float sign_ang = (velocitat_ang_heretada >= 0.0F) ? 1.0F : -1.0F;
|
||||||
|
|
||||||
// Aplicar aceleración negativa (fricció)
|
if (abs_ang <= CAP) {
|
||||||
float nova_speed = speed + (debris.acceleration * delta_time);
|
debris.velocitat_rot = velocitat_ang_heretada;
|
||||||
nova_speed = std::max(nova_speed, 0.0F);
|
return;
|
||||||
|
|
||||||
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)
|
// Excés: converteix l'excés de velocitat angular en velocitat tangencial lineal
|
||||||
if (std::abs(debris.velocitat_rot) > 0.01F) {
|
float excess = abs_ang - CAP;
|
||||||
// Calcular angle de rotación este frame
|
constexpr float RADIUS = 20.0F; // Radi típic de la shape (enemigos = 20 px)
|
||||||
float dangle = debris.velocitat_rot * delta_time;
|
float v_tangential = excess * RADIUS;
|
||||||
|
|
||||||
// Rotar vector de velocity usant matriu de rotación 2D
|
// Direcció tangencial: perpendicular a la radial (90° CCW): tangent = (-dy, dx)
|
||||||
float vel_x_old = debris.velocity.x;
|
debris.velocity.x += -direccio.y * v_tangential;
|
||||||
float vel_y_old = debris.velocity.y;
|
debris.velocity.y += direccio.x * v_tangential;
|
||||||
|
|
||||||
float cos_a = std::cos(dangle);
|
// Velocitat angular limitada al cap (preservant el signe)
|
||||||
float sin_a = std::sin(dangle);
|
debris.velocitat_rot = sign_ang * CAP;
|
||||||
|
}
|
||||||
|
|
||||||
debris.velocity.x = (vel_x_old * cos_a) - (vel_y_old * sin_a);
|
void DebrisManager::applyVisualRotation(Debris& debris, float velocitat_angular, float factor_herencia_visual) {
|
||||||
debris.velocity.y = (vel_x_old * sin_a) + (vel_y_old * cos_a);
|
if (factor_herencia_visual > 0.01F && std::abs(velocitat_angular) > 0.01F) {
|
||||||
|
// Heredar rotación visual con factor proporcional + ±5% de variació
|
||||||
|
debris.velocitat_rot_visual = debris.velocitat_rot * factor_herencia_visual;
|
||||||
|
float variacio_visual =
|
||||||
|
((std::rand() / static_cast<float>(RAND_MAX)) * 0.1F) - 0.05F;
|
||||||
|
debris.velocitat_rot_visual *= (1.0F + variacio_visual);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2c. Aplicar fricció angular (desacceleració gradual)
|
// Rotación visual aleatòria (factor = 0.0 o sin velocidad angular)
|
||||||
if (std::abs(debris.velocitat_rot) > 0.01F) {
|
debris.velocitat_rot_visual =
|
||||||
float sign = (debris.velocitat_rot > 0) ? 1.0F : -1.0F;
|
Defaults::Physics::Debris::ROTACIO_MIN +
|
||||||
float reduccion =
|
((std::rand() / static_cast<float>(RAND_MAX)) *
|
||||||
Defaults::Physics::Debris::FRICCIO_ANGULAR * delta_time;
|
(Defaults::Physics::Debris::ROTACIO_MAX -
|
||||||
debris.velocitat_rot -= sign * reduccion;
|
Defaults::Physics::Debris::ROTACIO_MIN));
|
||||||
|
|
||||||
// Evitar canvi de signe (no pot passar de CW a CCW)
|
// 50% probabilitat de rotación en sentit contrari
|
||||||
if ((debris.velocitat_rot > 0) != (sign > 0)) {
|
if (std::rand() % 2 == 0) {
|
||||||
debris.velocitat_rot = 0.0F;
|
debris.velocitat_rot_visual = -debris.velocitat_rot_visual;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
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
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebrisManager::draw() const {
|
||||||
|
for (const auto& debris : debris_pool_) {
|
||||||
|
if (!debris.active) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dibujar segmento con brightness y color heredados del padre.
|
||||||
|
Rendering::linea(renderer_,
|
||||||
|
static_cast<int>(debris.p1.x),
|
||||||
|
static_cast<int>(debris.p1.y),
|
||||||
|
static_cast<int>(debris.p2.x),
|
||||||
|
static_cast<int>(debris.p2.y),
|
||||||
|
debris.brightness,
|
||||||
|
0.0F,
|
||||||
|
debris.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto DebrisManager::findFreeSlot() -> Debris* {
|
||||||
|
for (auto& debris : debris_pool_) {
|
||||||
|
if (!debris.active) {
|
||||||
|
return &debris;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nullptr; // Pool ple
|
||||||
// 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)
|
|
||||||
float shrink_factor =
|
|
||||||
1.0F - (debris.factor_shrink * debris.temps_vida / debris.temps_max);
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void DebrisManager::draw() const {
|
auto DebrisManager::computeExplosionDirection(const Vec2& p1,
|
||||||
for (const auto& debris : debris_pool_) {
|
const Vec2& p2,
|
||||||
if (!debris.active) {
|
const Vec2& centre_objecte) -> Vec2 {
|
||||||
continue;
|
// 1. Calcular centro del segment
|
||||||
|
float centro_seg_x = (p1.x + p2.x) / 2.0F;
|
||||||
|
float centro_seg_y = (p1.y + p2.y) / 2.0F;
|
||||||
|
|
||||||
|
// 2. Calcular vector des del centro de l'objecte hacia el centro del segment
|
||||||
|
// Això garanteix que la direcció siempre apunte hacia fuera (direcció radial)
|
||||||
|
float dx = centro_seg_x - centre_objecte.x;
|
||||||
|
float dy = centro_seg_y - centre_objecte.y;
|
||||||
|
|
||||||
|
// 3. Normalitzar (obtenir vector unitari)
|
||||||
|
float length = std::sqrt((dx * dx) + (dy * dy));
|
||||||
|
if (length < 0.001F) {
|
||||||
|
// Segment al centro (cas extrem mucho improbable), retornar direcció aleatòria
|
||||||
|
float angle_rand =
|
||||||
|
(std::rand() / static_cast<float>(RAND_MAX)) * 2.0F * Defaults::Math::PI;
|
||||||
|
return {.x = std::cos(angle_rand), .y = std::sin(angle_rand)};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dibujar segmento con brightness y color heredados del padre.
|
dx /= length;
|
||||||
Rendering::linea(renderer_,
|
dy /= length;
|
||||||
static_cast<int>(debris.p1.x),
|
|
||||||
static_cast<int>(debris.p1.y),
|
|
||||||
static_cast<int>(debris.p2.x),
|
|
||||||
static_cast<int>(debris.p2.y),
|
|
||||||
debris.brightness, 0.0F, debris.color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto DebrisManager::findFreeSlot() -> Debris* {
|
// 4. Añadir variació aleatòria pequeña (±15°) per varietat visual
|
||||||
for (auto& debris : debris_pool_) {
|
float angle_variacio =
|
||||||
if (!debris.active) {
|
((std::rand() % 30) - 15) * Defaults::Math::PI / 180.0F;
|
||||||
return &debris;
|
|
||||||
|
float cos_v = std::cos(angle_variacio);
|
||||||
|
float sin_v = std::sin(angle_variacio);
|
||||||
|
|
||||||
|
float final_x = (dx * cos_v) - (dy * sin_v);
|
||||||
|
float final_y = (dx * sin_v) + (dy * cos_v);
|
||||||
|
|
||||||
|
return {.x = final_x, .y = final_y};
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebrisManager::reset() {
|
||||||
|
for (auto& debris : debris_pool_) {
|
||||||
|
debris.active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr; // Pool ple
|
|
||||||
}
|
|
||||||
|
|
||||||
auto DebrisManager::computeExplosionDirection(const Vec2& p1,
|
auto DebrisManager::getActiveCount() const -> int {
|
||||||
const Vec2& p2,
|
int count = 0;
|
||||||
const Vec2& centre_objecte) -> Vec2 {
|
for (const auto& debris : debris_pool_) {
|
||||||
// 1. Calcular centro del segment
|
if (debris.active) {
|
||||||
float centro_seg_x = (p1.x + p2.x) / 2.0F;
|
count++;
|
||||||
float centro_seg_y = (p1.y + p2.y) / 2.0F;
|
}
|
||||||
|
|
||||||
// 2. Calcular vector des del centro de l'objecte hacia el centro del segment
|
|
||||||
// Això garanteix que la direcció siempre apunte hacia fuera (direcció radial)
|
|
||||||
float dx = centro_seg_x - centre_objecte.x;
|
|
||||||
float dy = centro_seg_y - centre_objecte.y;
|
|
||||||
|
|
||||||
// 3. Normalitzar (obtenir vector unitari)
|
|
||||||
float length = std::sqrt((dx * dx) + (dy * dy));
|
|
||||||
if (length < 0.001F) {
|
|
||||||
// Segment al centro (cas extrem mucho improbable), retornar direcció aleatòria
|
|
||||||
float angle_rand =
|
|
||||||
(std::rand() / static_cast<float>(RAND_MAX)) * 2.0F * Defaults::Math::PI;
|
|
||||||
return {.x = std::cos(angle_rand), .y = std::sin(angle_rand)};
|
|
||||||
}
|
|
||||||
|
|
||||||
dx /= length;
|
|
||||||
dy /= length;
|
|
||||||
|
|
||||||
// 4. Añadir variació aleatòria pequeña (±15°) per varietat visual
|
|
||||||
float angle_variacio =
|
|
||||||
((std::rand() % 30) - 15) * Defaults::Math::PI / 180.0F;
|
|
||||||
|
|
||||||
float cos_v = std::cos(angle_variacio);
|
|
||||||
float sin_v = std::sin(angle_variacio);
|
|
||||||
|
|
||||||
float final_x = (dx * cos_v) - (dy * sin_v);
|
|
||||||
float final_y = (dx * sin_v) + (dy * cos_v);
|
|
||||||
|
|
||||||
return {.x = final_x, .y = final_y};
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebrisManager::reset() {
|
|
||||||
for (auto& debris : debris_pool_) {
|
|
||||||
debris.active = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto DebrisManager::getActiveCount() const -> int {
|
|
||||||
int count = 0;
|
|
||||||
for (const auto& debris : debris_pool_) {
|
|
||||||
if (debris.active) {
|
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Effects
|
} // namespace Effects
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/rendering/render_context.hpp"
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
@@ -13,15 +12,16 @@
|
|||||||
|
|
||||||
#include "core/defaults.hpp"
|
#include "core/defaults.hpp"
|
||||||
#include "core/graphics/shape.hpp"
|
#include "core/graphics/shape.hpp"
|
||||||
|
#include "core/rendering/render_context.hpp"
|
||||||
#include "core/types.hpp"
|
#include "core/types.hpp"
|
||||||
#include "debris.hpp"
|
#include "debris.hpp"
|
||||||
|
|
||||||
namespace Effects {
|
namespace Effects {
|
||||||
|
|
||||||
// Gestor de fragments de explosions
|
// Gestor de fragments de explosions
|
||||||
// Manté un pool de objectes Debris i gestiona el seu cicle de vida
|
// Manté un pool de objectes Debris i gestiona el seu cicle de vida
|
||||||
class DebrisManager {
|
class DebrisManager {
|
||||||
public:
|
public:
|
||||||
explicit DebrisManager(Rendering::Renderer* renderer);
|
explicit DebrisManager(Rendering::Renderer* renderer);
|
||||||
|
|
||||||
// Crear explosión a partir de una shape
|
// Crear explosión a partir de una shape
|
||||||
@@ -34,6 +34,9 @@ class DebrisManager {
|
|||||||
// - 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)
|
||||||
|
// - friction: desacceleració del debris (px/s², per defecte ACCELERACIO = -60)
|
||||||
|
// - segment_multiplier: nombre de còpies per segment (per defecte 1 = sense duplicar)
|
||||||
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,
|
||||||
@@ -44,7 +47,10 @@ class DebrisManager {
|
|||||||
float velocitat_angular = 0.0F,
|
float velocitat_angular = 0.0F,
|
||||||
float factor_herencia_visual = 0.0F,
|
float factor_herencia_visual = 0.0F,
|
||||||
const std::string& sound = Defaults::Sound::EXPLOSION,
|
const std::string& sound = Defaults::Sound::EXPLOSION,
|
||||||
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::TEMPS_VIDA,
|
||||||
|
float friction = Defaults::Physics::Debris::ACCELERACIO,
|
||||||
|
int segment_multiplier = 1);
|
||||||
|
|
||||||
// Actualitzar todos los fragments active
|
// Actualitzar todos los fragments active
|
||||||
void update(float delta_time);
|
void update(float delta_time);
|
||||||
@@ -58,14 +64,13 @@ class DebrisManager {
|
|||||||
// Obtenir número de fragments active
|
// Obtenir número de fragments active
|
||||||
[[nodiscard]] auto getActiveCount() const -> int;
|
[[nodiscard]] auto getActiveCount() const -> int;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Rendering::Renderer* renderer_;
|
Rendering::Renderer* renderer_;
|
||||||
|
|
||||||
// Pool de fragments (màxim concurrent)
|
// Pool de fragments (màxim concurrent)
|
||||||
// Un pentágono té 5 línies, 15 enemigos = 75 línies
|
// Pentàgon 5 línies × 15 enemics × multiplier 3 = 225 trossos només d'enemics.
|
||||||
// + ship (3 línies) + balas (5 línies * 3) = 93 línies màxim
|
// + ship (3 línies) + balas (5 línies × 3) = ~243. Arrodonit a 300.
|
||||||
// Arrodonit a 100 per seguretat
|
static constexpr int MAX_DEBRIS = 300;
|
||||||
static constexpr int MAX_DEBRIS = 150;
|
|
||||||
std::array<Debris, MAX_DEBRIS> debris_pool_;
|
std::array<Debris, MAX_DEBRIS> debris_pool_;
|
||||||
|
|
||||||
// Trobar primer slot inactiu
|
// Trobar primer slot inactiu
|
||||||
@@ -81,14 +86,9 @@ class DebrisManager {
|
|||||||
-> 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,
|
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;
|
||||||
const Vec2& centro, float velocitat_base, float brightness,
|
static void applyAngularVelocity(Debris& debris, const Vec2& direccio, float velocitat_angular);
|
||||||
const Vec2& velocitat_objecte, float velocitat_angular,
|
static void applyVisualRotation(Debris& debris, float velocitat_angular, float factor_herencia_visual);
|
||||||
float factor_herencia_visual, SDL_Color color) -> bool;
|
};
|
||||||
static void applyAngularVelocity(Debris& debris, const Vec2& direccio,
|
|
||||||
float velocitat_angular);
|
|
||||||
static void applyVisualRotation(Debris& debris, float velocitat_angular,
|
|
||||||
float factor_herencia_visual);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Effects
|
} // namespace Effects
|
||||||
|
|||||||
@@ -73,7 +73,10 @@ namespace Systems::Collision {
|
|||||||
DROTACIO,
|
DROTACIO,
|
||||||
0.0F, // sin herencia visual
|
0.0F, // sin herencia visual
|
||||||
Defaults::Sound::EXPLOSION,
|
Defaults::Sound::EXPLOSION,
|
||||||
COLOR);
|
COLOR,
|
||||||
|
Defaults::Physics::Debris::ENEMY_LIFETIME,
|
||||||
|
Defaults::Physics::Debris::ENEMY_FRICTION,
|
||||||
|
Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER);
|
||||||
}
|
}
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user