diff --git a/source/core/defaults/title.hpp b/source/core/defaults/title.hpp index 959d9cc..02c1af8 100644 --- a/source/core/defaults/title.hpp +++ b/source/core/defaults/title.hpp @@ -128,10 +128,17 @@ namespace Defaults::Title { // Coreografia de la seqüència d'entrada al state MAIN. // Tots els elements (logo, footer, naus, press start) entren ordenadament // segons aquests thresholds. Vegeu title_scene.cpp/updateMainState. + // + // Per al logo i el footer, l'efecte simula un moviment 3D des de l'usuari + // cap al VP: el text arrenca gran i a la posició projectada extrema (com + // si estigués prop de la càmera, fora de pantalla) i acaba a la seva + // posició final amb escala normal (com si hagués aterrat al VP). Pivot: + // centre de pantalla (= projecció del VP 3D). namespace Sequence { - // Offsets fora-pantalla per a l'animació d'entrada (additius a la posició final). - constexpr float LOGO_OFFSCREEN_OFFSET_Y = -240.0F; // logo entra des de dalt - constexpr float FOOTER_OFFSCREEN_OFFSET_Y = +160.0F; // jailgames/copyright entren des de baix + // Factor d'escala inicial. >1 = sprite gran a l'inici (prop de l'usuari). + // La posició inicial es deriva: pivot=centre, delta multiplicat per aquest factor. + constexpr float LOGO_INTRO_SCALE_START = 2.5F; + constexpr float FOOTER_INTRO_SCALE_START = 2.5F; // Durades de les animacions d'entrada (segons). constexpr float LOGO_ENTRY_DURATION = 1.2F; diff --git a/source/game/scenes/title_scene.cpp b/source/game/scenes/title_scene.cpp index 5e7190c..4d0fd03 100644 --- a/source/game/scenes/title_scene.cpp +++ b/source/game/scenes/title_scene.cpp @@ -250,14 +250,21 @@ void TitleScene::inicialitzarJailgames() { void TitleScene::dibuixarPeuTitol(float spacing) const { namespace S = Defaults::Title::Sequence; - const float JAILGAMES_Y_INTRO = (1.0F - Easing::easeOutQuad(intro_jailgames_progress_)) * - S::FOOTER_OFFSCREEN_OFFSET_Y; - const float COPYRIGHT_Y_INTRO = (1.0F - Easing::easeOutQuad(intro_copyright_progress_)) * - S::FOOTER_OFFSCREEN_OFFSET_Y; + // Pivot al centre de pantalla (= projecció VP). Cada element s'expandeix + // des d'aquí mentre s_factor passa de SCALE_START (gran, prop de l'usuari) + // a 1.0 (a la mida i posició finals, "lluny" al VP). + const float SCREEN_CENTRE_X = Defaults::Game::WIDTH / 2.0F; + const float SCREEN_CENTRE_Y = Defaults::Game::HEIGHT / 2.0F; + const float JAILGAMES_S = std::lerp(S::FOOTER_INTRO_SCALE_START, 1.0F, Easing::easeOutQuad(intro_jailgames_progress_)); + const float COPYRIGHT_S = std::lerp(S::FOOTER_INTRO_SCALE_START, 1.0F, Easing::easeOutQuad(intro_copyright_progress_)); + const float JAILGAMES_RENDER_SCALE = Defaults::Title::Layout::JAILGAMES_SCALE * JAILGAMES_S; for (const auto& lletra : lletres_jailgames_) { - const Vec2 POS{.x = lletra.position.x, .y = lletra.position.y + JAILGAMES_Y_INTRO}; - Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, Defaults::Title::Layout::JAILGAMES_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::JAILGAMES_LOGO); + const Vec2 POS{ + .x = SCREEN_CENTRE_X + (JAILGAMES_S * (lletra.position.x - SCREEN_CENTRE_X)), + .y = SCREEN_CENTRE_Y + (JAILGAMES_S * (lletra.position.y - SCREEN_CENTRE_Y)), + }; + Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, JAILGAMES_RENDER_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::JAILGAMES_LOGO); } std::string copyright = Project::COPYRIGHT; for (char& c : copyright) { @@ -265,9 +272,11 @@ void TitleScene::dibuixarPeuTitol(float spacing) const { c = static_cast(c - 32); } } - const float Y_COPY = (Defaults::Game::HEIGHT * Defaults::Title::Layout::COPYRIGHT1_POS) + COPYRIGHT_Y_INTRO; - const float CENTRE_X = Defaults::Game::WIDTH / 2.0F; - text_.renderCentered(copyright, {.x = CENTRE_X, .y = Y_COPY}, Defaults::Title::Layout::COPYRIGHT_SCALE, spacing, 1.0F, Defaults::Title::Colors::COPYRIGHT); + const float Y_COPY_FINAL = Defaults::Game::HEIGHT * Defaults::Title::Layout::COPYRIGHT1_POS; + const float COPY_X = SCREEN_CENTRE_X; // ja al centre + const float COPY_Y = SCREEN_CENTRE_Y + (COPYRIGHT_S * (Y_COPY_FINAL - SCREEN_CENTRE_Y)); + const float COPY_RENDER_SCALE = Defaults::Title::Layout::COPYRIGHT_SCALE * COPYRIGHT_S; + text_.renderCentered(copyright, {.x = COPY_X, .y = COPY_Y}, COPY_RENDER_SCALE, spacing, 1.0F, Defaults::Title::Colors::COPYRIGHT); } auto TitleScene::isFinished() const -> bool { @@ -524,11 +533,13 @@ void TitleScene::draw() { return; } - // Offset additiu de la intro del logo (cau des de dalt). Quan progress=1, - // el logo està al seu lloc i aquest offset val 0 — l'oscil·lació suau - // (updateLogoAnimation) continua treballant sobre la posició base. - const float LOGO_Y_INTRO = (1.0F - Easing::easeOutQuad(intro_logo_progress_)) * - Defaults::Title::Sequence::LOGO_OFFSCREEN_OFFSET_Y; + // Factor d'escala+posició per simular un moviment 3D des de l'usuari (prop, + // sprite gran i posició projectada extrema) cap al VP (lluny, sprite a la + // seva mida i posició finals). Pivot: centre de pantalla (= projecció VP). + const float LOGO_S = std::lerp(Defaults::Title::Sequence::LOGO_INTRO_SCALE_START, 1.0F, Easing::easeOutQuad(intro_logo_progress_)); + const float SCREEN_CENTRE_X = Defaults::Game::WIDTH / 2.0F; + const float SCREEN_CENTRE_Y = Defaults::Game::HEIGHT / 2.0F; + const float LOGO_RENDER_SCALE = Defaults::Title::Layout::LOGO_SCALE * LOGO_S; if (animacio_activa_) { float temps_shadow = std::max(0.0F, temps_animacio_ - SHADOW_DELAY); @@ -537,28 +548,38 @@ void TitleScene::draw() { const float SHADOW_OY = (ORBIT_AMPLITUDE_Y * std::sin((TWO_PI * ORBIT_FREQUENCY_Y * temps_shadow) + ORBIT_PHASE_OFFSET)) + SHADOW_OFFSET_Y; for (std::size_t i = 0; i < lletres_orni_.size(); ++i) { + const float BASE_X = posicions_originals_orni_[i].x + std::round(SHADOW_OX); + const float BASE_Y = posicions_originals_orni_[i].y + std::round(SHADOW_OY); const Vec2 POS_SHADOW{ - .x = posicions_originals_orni_[i].x + std::round(SHADOW_OX), - .y = posicions_originals_orni_[i].y + std::round(SHADOW_OY) + LOGO_Y_INTRO, + .x = SCREEN_CENTRE_X + (LOGO_S * (BASE_X - SCREEN_CENTRE_X)), + .y = SCREEN_CENTRE_Y + (LOGO_S * (BASE_Y - SCREEN_CENTRE_Y)), }; - Rendering::renderShape(sdl_.getRenderer(), lletres_orni_[i].shape, POS_SHADOW, 0.0F, Defaults::Title::Layout::LOGO_SCALE, 1.0F, SHADOW_BRIGHTNESS, Defaults::Title::Colors::LOGO_SHADOW); + Rendering::renderShape(sdl_.getRenderer(), lletres_orni_[i].shape, POS_SHADOW, 0.0F, LOGO_RENDER_SCALE, 1.0F, SHADOW_BRIGHTNESS, Defaults::Title::Colors::LOGO_SHADOW); } for (std::size_t i = 0; i < lletres_attack_.size(); ++i) { + const float BASE_X = posicions_originals_attack_[i].x + std::round(SHADOW_OX); + const float BASE_Y = posicions_originals_attack_[i].y + std::round(SHADOW_OY); const Vec2 POS_SHADOW{ - .x = posicions_originals_attack_[i].x + std::round(SHADOW_OX), - .y = posicions_originals_attack_[i].y + std::round(SHADOW_OY) + LOGO_Y_INTRO, + .x = SCREEN_CENTRE_X + (LOGO_S * (BASE_X - SCREEN_CENTRE_X)), + .y = SCREEN_CENTRE_Y + (LOGO_S * (BASE_Y - SCREEN_CENTRE_Y)), }; - Rendering::renderShape(sdl_.getRenderer(), lletres_attack_[i].shape, POS_SHADOW, 0.0F, Defaults::Title::Layout::LOGO_SCALE, 1.0F, SHADOW_BRIGHTNESS, Defaults::Title::Colors::LOGO_SHADOW); + Rendering::renderShape(sdl_.getRenderer(), lletres_attack_[i].shape, POS_SHADOW, 0.0F, LOGO_RENDER_SCALE, 1.0F, SHADOW_BRIGHTNESS, Defaults::Title::Colors::LOGO_SHADOW); } } for (const auto& lletra : lletres_orni_) { - const Vec2 POS{.x = lletra.position.x, .y = lletra.position.y + LOGO_Y_INTRO}; - Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, Defaults::Title::Layout::LOGO_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN); + const Vec2 POS{ + .x = SCREEN_CENTRE_X + (LOGO_S * (lletra.position.x - SCREEN_CENTRE_X)), + .y = SCREEN_CENTRE_Y + (LOGO_S * (lletra.position.y - SCREEN_CENTRE_Y)), + }; + Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, LOGO_RENDER_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN); } for (const auto& lletra : lletres_attack_) { - const Vec2 POS{.x = lletra.position.x, .y = lletra.position.y + LOGO_Y_INTRO}; - Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, Defaults::Title::Layout::LOGO_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN); + const Vec2 POS{ + .x = SCREEN_CENTRE_X + (LOGO_S * (lletra.position.x - SCREEN_CENTRE_X)), + .y = SCREEN_CENTRE_Y + (LOGO_S * (lletra.position.y - SCREEN_CENTRE_Y)), + }; + Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, LOGO_RENDER_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN); } const float SPACING = Defaults::Title::Layout::TEXT_SPACING;