afegit debris_manager

This commit is contained in:
2025-12-02 08:30:32 +01:00
parent 67681e0f37
commit 73f222fcb7
8 changed files with 410 additions and 12 deletions

View File

@@ -51,6 +51,7 @@ set(APP_SOURCES
source/game/entities/nau.cpp source/game/entities/nau.cpp
source/game/entities/bala.cpp source/game/entities/bala.cpp
source/game/entities/enemic.cpp source/game/entities/enemic.cpp
source/game/effects/debris_manager.cpp
) )
# Configuración de SDL3 # Configuración de SDL3

View File

@@ -50,7 +50,8 @@ APP_SOURCES := \
source/game/escenes/escena_joc.cpp \ source/game/escenes/escena_joc.cpp \
source/game/entities/nau.cpp \ source/game/entities/nau.cpp \
source/game/entities/bala.cpp \ source/game/entities/bala.cpp \
source/game/entities/enemic.cpp source/game/entities/enemic.cpp \
source/game/effects/debris_manager.cpp
# ============================================================================== # ==============================================================================
# INCLUDES # INCLUDES

View File

@@ -0,0 +1,33 @@
// debris.hpp - Fragment de línia volant (explosió de formes)
// © 2025 Port a C++20 amb SDL3
#pragma once
#include "core/types.hpp"
namespace Effects {
// Debris: un segment de línia que vola perpendicular a sí mateix
// Representa un fragment d'una forma destruïda (nau, enemic, bala)
struct Debris {
// Geometria del segment (2 punts en coordenades mundials)
Punt p1; // Punt inicial del segment
Punt p2; // Punt final del segment
// Física
Punt velocitat; // Velocitat en px/s (components x, y)
float acceleracio; // Acceleració negativa (fricció) en px/s²
// Rotació
float angle_rotacio; // Angle de rotació acumulat (radians)
float velocitat_rot; // Velocitat de rotació en rad/s
// Estat de vida
float temps_vida; // Temps transcorregut (segons)
float temps_max; // Temps de vida màxim (segons)
bool actiu; // Està actiu?
// Shrinking (reducció de distància entre punts)
float factor_shrink; // Factor de reducció per segon (0.0-1.0)
};
} // namespace Effects

View File

@@ -0,0 +1,275 @@
// debris_manager.cpp - Implementació del gestor de fragments
// © 2025 Port a C++20 amb SDL3
#include "debris_manager.hpp"
#include "core/defaults.hpp"
#include "core/rendering/line_renderer.hpp"
#include <cmath>
#include <cstdlib>
#include <iostream>
namespace Effects {
// Helper: transformar punt amb rotació, escala i trasllació
// (Copiat de shape_renderer.cpp:12-34)
static Punt transform_point(const Punt &point, const Punt &shape_centre,
const Punt &posicio, float angle, float escala) {
// 1. Centrar el punt respecte al centre de la forma
float centered_x = point.x - shape_centre.x;
float centered_y = point.y - shape_centre.y;
// 2. Aplicar escala al punt centrat
float scaled_x = centered_x * escala;
float scaled_y = centered_y * escala;
// 3. Aplicar rotació
float cos_a = std::cos(angle);
float sin_a = std::sin(angle);
float rotated_x = scaled_x * cos_a - scaled_y * sin_a;
float rotated_y = scaled_x * sin_a + scaled_y * cos_a;
// 4. Aplicar trasllació a posició mundial
return {rotated_x + posicio.x, rotated_y + posicio.y};
}
DebrisManager::DebrisManager(SDL_Renderer *renderer) : renderer_(renderer) {
// Inicialitzar tots els debris com inactius
for (auto &debris : debris_pool_) {
debris.actiu = false;
}
}
void DebrisManager::explotar(const std::shared_ptr<Graphics::Shape> &shape,
const Punt &centre, float angle, float escala,
float velocitat_base) {
if (!shape || !shape->es_valida()) {
return;
}
// Obtenir centre de la forma per a transformacions
const Punt &shape_centre = shape->get_centre();
// Iterar sobre totes les primitives de la forma
for (const auto &primitive : shape->get_primitives()) {
// Processar cada segment de línia
std::vector<std::pair<Punt, Punt>> segments;
if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
// Polyline: extreure segments consecutius
for (size_t i = 0; i < primitive.points.size() - 1; i++) {
segments.push_back({primitive.points[i], primitive.points[i + 1]});
}
} else { // PrimitiveType::LINE
// Line: un únic segment
if (primitive.points.size() >= 2) {
segments.push_back({primitive.points[0], primitive.points[1]});
}
}
// Crear debris per a cada segment
for (const auto &[local_p1, local_p2] : segments) {
// 1. Transformar punts locals → coordenades mundials
Punt world_p1 =
transform_point(local_p1, shape_centre, centre, angle, escala);
Punt world_p2 =
transform_point(local_p2, shape_centre, centre, angle, escala);
// 2. Trobar slot lliure
Debris *debris = trobar_slot_lliure();
if (!debris) {
std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n";
return; // Pool ple
}
// 3. Inicialitzar geometria
debris->p1 = world_p1;
debris->p2 = world_p2;
// 4. Calcular direcció perpendicular
Punt direccio = calcular_direccio_perpendicular(world_p1, world_p2);
// 5. Velocitat inicial (base ± variació aleatòria)
float speed =
velocitat_base +
((std::rand() / static_cast<float>(RAND_MAX)) * 2.0f - 1.0f) *
Defaults::Physics::Debris::VARIACIO_VELOCITAT;
debris->velocitat.x = direccio.x * speed;
debris->velocitat.y = direccio.y * speed;
debris->acceleracio = Defaults::Physics::Debris::ACCELERACIO;
// 6. Rotació lenta aleatòria
debris->velocitat_rot =
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ó en sentit contrari
if (std::rand() % 2 == 0) {
debris->velocitat_rot = -debris->velocitat_rot;
}
debris->angle_rotacio = 0.0f;
// 7. Configurar vida i shrinking
debris->temps_vida = 0.0f;
debris->temps_max = Defaults::Physics::Debris::TEMPS_VIDA;
debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE;
// 8. Activar
debris->actiu = true;
}
}
}
void DebrisManager::actualitzar(float delta_time) {
for (auto &debris : debris_pool_) {
if (!debris.actiu)
continue;
// 1. Actualitzar temps de vida
debris.temps_vida += delta_time;
// Desactivar si ha superat temps màxim
if (debris.temps_vida >= debris.temps_max) {
debris.actiu = false;
continue;
}
// 2. Actualitzar velocitat (desacceleració)
// Aplicar fricció en la direcció del moviment
float speed = std::sqrt(debris.velocitat.x * debris.velocitat.x +
debris.velocitat.y * debris.velocitat.y);
if (speed > 1.0f) {
// Calcular direcció normalitzada
float dir_x = debris.velocitat.x / speed;
float dir_y = debris.velocitat.y / speed;
// Aplicar acceleració negativa (fricció)
float nova_speed = speed + debris.acceleracio * delta_time;
if (nova_speed < 0.0f)
nova_speed = 0.0f;
debris.velocitat.x = dir_x * nova_speed;
debris.velocitat.y = dir_y * nova_speed;
} else {
// Velocitat molt baixa, aturar
debris.velocitat.x = 0.0f;
debris.velocitat.y = 0.0f;
}
// 3. Calcular centre del segment
Punt centre = {(debris.p1.x + debris.p2.x) / 2.0f,
(debris.p1.y + debris.p2.y) / 2.0f};
// 4. Actualitzar posició del centre
centre.x += debris.velocitat.x * delta_time;
centre.y += debris.velocitat.y * delta_time;
// 5. Actualitzar rotació
debris.angle_rotacio += debris.velocitat_rot * delta_time;
// 6. Aplicar shrinking (reducció de distància entre punts)
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 distància original entre punts
float dx = debris.p2.x - debris.p1.x;
float dy = debris.p2.y - debris.p1.y;
// 7. Reconstruir segment amb nova mida i rotació
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 = centre.x - half_length * std::cos(new_angle);
debris.p1.y = centre.y - half_length * std::sin(new_angle);
debris.p2.x = centre.x + half_length * std::cos(new_angle);
debris.p2.y = centre.y + half_length * std::sin(new_angle);
}
}
void DebrisManager::dibuixar() const {
for (const auto &debris : debris_pool_) {
if (!debris.actiu)
continue;
// Dibuixar segment de línia
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), true);
}
}
Debris *DebrisManager::trobar_slot_lliure() {
for (auto &debris : debris_pool_) {
if (!debris.actiu) {
return &debris;
}
}
return nullptr; // Pool ple
}
Punt DebrisManager::calcular_direccio_perpendicular(const Punt &p1,
const Punt &p2) const {
// 1. Calcular vector de la línia (p1 → p2)
float dx = p2.x - p1.x;
float dy = p2.y - p1.y;
// 2. Normalitzar (obtenir vector unitari)
float length = std::sqrt(dx * dx + dy * dy);
if (length < 0.001f) {
// Línia degenerada, retornar direcció aleatòria
float angle_rand =
(std::rand() / static_cast<float>(RAND_MAX)) * 2.0f * Defaults::Math::PI;
return {std::cos(angle_rand), std::sin(angle_rand)};
}
dx /= length;
dy /= length;
// 3. Rotar 90° (perpendicular)
// Rotació 90° sentit antihorari: (x,y) → (-y, x)
float perp_x = -dy;
float perp_y = dx;
// 4. Afegir variació aleatòria petita (±15°)
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 = perp_x * cos_v - perp_y * sin_v;
float final_y = perp_x * sin_v + perp_y * cos_v;
// 5. Afegir ± direcció aleatòria (50% probabilitat d'invertir)
if (std::rand() % 2 == 0) {
final_x = -final_x;
final_y = -final_y;
}
return {final_x, final_y};
}
void DebrisManager::reiniciar() {
for (auto &debris : debris_pool_) {
debris.actiu = false;
}
}
int DebrisManager::get_num_actius() const {
int count = 0;
for (const auto &debris : debris_pool_) {
if (debris.actiu)
count++;
}
return count;
}
} // namespace Effects

View File

@@ -0,0 +1,59 @@
// debris_manager.hpp - Gestor de fragments d'explosions
// © 2025 Port a C++20 amb SDL3
#pragma once
#include "debris.hpp"
#include "core/graphics/shape.hpp"
#include "core/types.hpp"
#include <SDL3/SDL.h>
#include <array>
#include <memory>
namespace Effects {
// Gestor de fragments d'explosions
// Manté un pool d'objectes Debris i gestiona el seu cicle de vida
class DebrisManager {
public:
explicit DebrisManager(SDL_Renderer *renderer);
// Crear explosió a partir d'una forma
// - shape: forma vectorial a explotar
// - centre: posició del centre de l'objecte
// - angle: orientació de l'objecte (radians)
// - escala: escala de l'objecte (1.0 = normal)
// - velocitat_base: velocitat inicial dels fragments (px/s)
void explotar(const std::shared_ptr<Graphics::Shape> &shape,
const Punt &centre, float angle, float escala,
float velocitat_base);
// Actualitzar tots els fragments actius
void actualitzar(float delta_time);
// Dibuixar tots els fragments actius
void dibuixar() const;
// Reiniciar tots els fragments (neteja)
void reiniciar();
// Obtenir número de fragments actius
int get_num_actius() const;
private:
SDL_Renderer *renderer_;
// Pool de fragments (màxim concurrent)
// Un pentàgon té 5 línies, 15 enemics = 75 línies
// + nau (3 línies) + bales (5 línies * 3) = 93 línies màxim
// Arrodonit a 100 per seguretat
static constexpr int MAX_DEBRIS = 100;
std::array<Debris, MAX_DEBRIS> debris_pool_;
// Trobar primer slot inactiu
Debris *trobar_slot_lliure();
// Calcular direcció perpendicular a un segment
Punt calcular_direccio_perpendicular(const Punt &p1, const Punt &p2) const;
};
} // namespace Effects

View File

@@ -20,6 +20,7 @@ public:
// Getters (API pública sense canvis) // Getters (API pública sense canvis)
bool esta_actiu() const { return esta_; } bool esta_actiu() const { return esta_; }
const Punt &get_centre() const { return centre_; } const Punt &get_centre() const { return centre_; }
const std::shared_ptr<Graphics::Shape> &get_forma() const { return forma_; }
void destruir() { esta_ = false; } void destruir() { esta_ = false; }
private: private:

View File

@@ -10,10 +10,11 @@
#include <cstdlib> #include <cstdlib>
#include <ctime> #include <ctime>
#include <iostream> #include <iostream>
#include <vector>
EscenaJoc::EscenaJoc(SDLManager &sdl) EscenaJoc::EscenaJoc(SDLManager &sdl)
: sdl_(sdl), nau_(sdl.obte_renderer()), itocado_(0), : sdl_(sdl), debris_manager_(sdl.obte_renderer()),
text_(sdl.obte_renderer()) { nau_(sdl.obte_renderer()), itocado_(0), text_(sdl.obte_renderer()) {
// Inicialitzar bales amb renderer // Inicialitzar bales amb renderer
for (auto &bala : bales_) { for (auto &bala : bales_) {
bala = Bala(sdl.obte_renderer()); bala = Bala(sdl.obte_renderer());
@@ -116,6 +117,9 @@ void EscenaJoc::actualitzar(float delta_time) {
for (auto &bala : bales_) { for (auto &bala : bales_) {
bala.actualitzar(delta_time); bala.actualitzar(delta_time);
} }
// Actualitzar fragments d'explosions
debris_manager_.actualitzar(delta_time);
} }
void EscenaJoc::dibuixar() { void EscenaJoc::dibuixar() {
@@ -135,6 +139,9 @@ void EscenaJoc::dibuixar() {
bala.dibuixar(); bala.dibuixar();
} }
// Dibuixar fragments d'explosions (després d'altres objectes)
debris_manager_.dibuixar();
// Dibuixar marcador // Dibuixar marcador
dibuixar_marcador(); dibuixar_marcador();
} }
@@ -146,19 +153,36 @@ void EscenaJoc::processar_input(const SDL_Event &event) {
if (event.type == SDL_EVENT_KEY_DOWN) { if (event.type == SDL_EVENT_KEY_DOWN) {
switch (event.key.key) { switch (event.key.key) {
case SDLK_SPACE: case SDLK_SPACE: {
// Disparar (Fase 9) // TEMPORAL: Explotar enemic aleatori (per testing)
// Basat en el codi Pascal original: crear bala en posició de la nau // TODO: Restaurar dispars quan el sistema d'explosions estiga validat
// El joc original només permetia 1 bala activa alhora
// Buscar primera bala inactiva // Buscar tots els enemics actius
for (auto &bala : bales_) { std::vector<int> enemics_actius;
if (!bala.esta_activa()) { for (int i = 0; i < Constants::MAX_ORNIS; i++) {
bala.disparar(nau_.get_centre(), nau_.get_angle()); if (orni_[i].esta_actiu()) {
break; enemics_actius.push_back(i);
} }
} }
if (!enemics_actius.empty()) {
// Seleccionar enemic aleatori
int idx = enemics_actius[std::rand() % enemics_actius.size()];
// Crear explosió
debris_manager_.explotar(orni_[idx].get_forma(),
orni_[idx].get_centre(),
0.0f, // angle (enemics no roten físicament)
1.0f, // escala
Defaults::Physics::Debris::VELOCITAT_BASE);
// Desactivar enemic
orni_[idx].destruir();
std::cout << "[TEST] Enemic " << idx << " explotat!\n";
}
break; break;
}
default: default:
break; break;

View File

@@ -7,6 +7,7 @@
#include "../../core/graphics/vector_text.hpp" #include "../../core/graphics/vector_text.hpp"
#include "../../core/rendering/sdl_manager.hpp" #include "../../core/rendering/sdl_manager.hpp"
#include "../effects/debris_manager.hpp"
#include "../../core/types.hpp" #include "../../core/types.hpp"
#include "../constants.hpp" #include "../constants.hpp"
#include "../entities/bala.hpp" #include "../entities/bala.hpp"
@@ -31,6 +32,9 @@ public:
private: private:
SDLManager &sdl_; SDLManager &sdl_;
// Efectes visuals
Effects::DebrisManager debris_manager_;
// Estat del joc // Estat del joc
Nau nau_; Nau nau_;
std::array<Enemic, Constants::MAX_ORNIS> orni_; std::array<Enemic, Constants::MAX_ORNIS> orni_;