feat(title): intro amb path Z (zoom+pivot al VP) en lloc d'offset Y

El logo i el footer ara entren simulant un moviment 3D des de l'usuari
cap al VP: arrenquen grans i a la posició projectada extrema (factor
d'escala SCALE_START > 1, pivot al centre de pantalla) i convergeixen
a la seva mida i posició finals. Substitueix l'offset Y lineal anterior.
This commit is contained in:
2026-05-22 14:03:28 +02:00
parent 2ca2062011
commit 839f73e1ef
2 changed files with 55 additions and 27 deletions
+45 -24
View File
@@ -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<char>(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;