// escena_logo.cpp - Implementació de l'escena logo // © 2025 Port a C++20 #include "escena_logo.hpp" #include "../../core/system/gestor_escenes.hpp" #include "../../core/system/global_events.hpp" #include "../../core/graphics/shape_loader.hpp" #include "../../core/rendering/shape_renderer.hpp" #include #include #include // Helper: calcular el progrés individual d'una lletra // en funció del progrés global (efecte seqüencial) static float calcular_progress_letra(size_t letra_index, size_t num_letras, float global_progress) { if (num_letras == 0) return 1.0f; constexpr float THRESHOLD = 0.5f; // Següent comença al 50% // Calcular temps per lletra float duration_per_letra = 1.0f / static_cast(num_letras); float step = THRESHOLD * duration_per_letra; float start = static_cast(letra_index) * step; float end = start + duration_per_letra; // Interpolar progrés if (global_progress < start) { return 0.0f; // Encara no ha començat } else if (global_progress >= end) { return 1.0f; // Completament apareguda } else { return (global_progress - start) / (end - start); } } EscenaLogo::EscenaLogo(SDLManager& sdl) : sdl_(sdl), temps_acumulat_(0.0f) { std::cout << "Escena Logo: Inicialitzant...\n"; inicialitzar_lletres(); } void EscenaLogo::executar() { SDL_Event event; Uint64 last_time = SDL_GetTicks(); while (GestorEscenes::actual == GestorEscenes::Escena::LOGO) { // Calcular delta_time real Uint64 current_time = SDL_GetTicks(); float delta_time = (current_time - last_time) / 1000.0f; last_time = current_time; // Limitar delta_time per evitar grans salts if (delta_time > 0.05f) { delta_time = 0.05f; } // Processar events SDL while (SDL_PollEvent(&event)) { // Manejo de finestra if (sdl_.handleWindowEvent(event)) { continue; } // Events globals (F1/F2/F3/ESC/QUIT) if (GlobalEvents::handle(event, sdl_)) { continue; } // Processar events de l'escena (qualsevol tecla/clic salta al joc) processar_events(event); } // Actualitzar lògica actualitzar(delta_time); // Actualitzar colors oscil·lats (efecte verd global) sdl_.updateColors(delta_time); // Dibuixar dibuixar(); } std::cout << "Escena Logo: Finalitzant...\n"; } void EscenaLogo::inicialitzar_lletres() { using namespace Graphics; // Llista de fitxers .shp (A repetida per a les dues A's) std::vector fitxers = { "letra_j.shp", "letra_a.shp", "letra_i.shp", "letra_l.shp", "letra_g.shp", "letra_a.shp", "letra_m.shp", "letra_e.shp", "letra_s.shp" }; // Pas 1: Carregar totes les formes i calcular amplades float ancho_total = 0.0f; for (const auto& fitxer : fitxers) { auto forma = ShapeLoader::load(fitxer); if (!forma || !forma->es_valida()) { std::cerr << "[EscenaLogo] Error carregant " << fitxer << std::endl; continue; } // Calcular bounding box de la forma (trobar ancho) float min_x = FLT_MAX; float max_x = -FLT_MAX; for (const auto& prim : forma->get_primitives()) { for (const auto& punt : prim.points) { min_x = std::min(min_x, punt.x); max_x = std::max(max_x, punt.x); } } float ancho_sin_escalar = max_x - min_x; // IMPORTANT: Escalar ancho i offset amb ESCALA_FINAL // per que les posicions finals coincideixin amb la mida real de les lletres float ancho = ancho_sin_escalar * ESCALA_FINAL; float offset_centre = (forma->get_centre().x - min_x) * ESCALA_FINAL; lletres_.push_back({ forma, {0.0f, 0.0f}, // Posició es calcularà després ancho, offset_centre }); ancho_total += ancho; } // Pas 2: Afegir espaiat entre lletres ancho_total += ESPAI_ENTRE_LLETRES * (lletres_.size() - 1); // Pas 3: Calcular posició inicial (centrat horitzontal) constexpr float PANTALLA_ANCHO = 640.0f; constexpr float PANTALLA_ALTO = 480.0f; float x_inicial = (PANTALLA_ANCHO - ancho_total) / 2.0f; float y_centre = PANTALLA_ALTO / 2.0f; // Pas 4: Assignar posicions a cada lletra float x_actual = x_inicial; for (auto& lletra : lletres_) { // Posicionar el centre de la forma (shape_centre) en pantalla // Usar offset_centre en lloc de ancho/2 perquè shape_centre // pot no estar exactament al mig del bounding box lletra.posicio.x = x_actual + lletra.offset_centre; lletra.posicio.y = y_centre; // Avançar per a següent lletra x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES; } std::cout << "[EscenaLogo] " << lletres_.size() << " lletres carregades, ancho total: " << ancho_total << " px\n"; } void EscenaLogo::actualitzar(float delta_time) { temps_acumulat_ += delta_time; // Després de DURACIO_TOTAL segons, saltar al joc if (temps_acumulat_ >= DURACIO_TOTAL) { GestorEscenes::actual = GestorEscenes::Escena::JOC; } } void EscenaLogo::dibuixar() { // Fons negre sdl_.neteja(0, 0, 0); // Progrés global normalitzat (0.0 → 1.0) float global_progress = std::min(temps_acumulat_ / DURACIO_ZOOM, 1.0f); // Centre de la pantalla (punt inicial de totes les lletres) constexpr Punt CENTRE_PANTALLA = {640.0f / 2.0f, 480.0f / 2.0f}; // Dibuixar cada lletra amb animació seqüencial for (size_t i = 0; i < lletres_.size(); i++) { const auto& lletra = lletres_[i]; // Calcular progrés individual d'aquesta lletra (0.0 → 1.0) float letra_progress = calcular_progress_letra(i, lletres_.size(), global_progress); // Si la lletra encara no ha començat, saltar-la if (letra_progress <= 0.0f) { continue; } // Interpolar posició: des del centre cap a posició final Punt pos_actual; pos_actual.x = CENTRE_PANTALLA.x + (lletra.posicio.x - CENTRE_PANTALLA.x) * letra_progress; pos_actual.y = CENTRE_PANTALLA.y + (lletra.posicio.y - CENTRE_PANTALLA.y) * letra_progress; // Aplicar ease-out quadràtic per suavitat float t = letra_progress; float ease_factor = 1.0f - (1.0f - t) * (1.0f - t); // Interpolar escala amb ease-out: des de ESCALA_INICIAL cap a ESCALA_FINAL float escala_actual = ESCALA_INICIAL + (ESCALA_FINAL - ESCALA_INICIAL) * ease_factor; // Renderitzar la lletra Rendering::render_shape( sdl_.obte_renderer(), lletra.forma, pos_actual, // Posició interpolada 0.0f, // Sense rotació escala_actual, // Escala interpolada amb ease-out true, // Dibuixar 1.0f // Progress = 1.0 (lletra completa, sense animació de primitives) ); } sdl_.presenta(); } void EscenaLogo::processar_events(const SDL_Event& event) { // Qualsevol tecla o clic de ratolí salta al joc if (event.type == SDL_EVENT_KEY_DOWN || event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { GestorEscenes::actual = GestorEscenes::Escena::JOC; } }