feat(title): destell hiperespacial al VP quan la nau desapareix (sparkle 4-puntes còncau)

This commit is contained in:
2026-05-22 23:46:56 +02:00
parent 70ca19eb87
commit 3b1e469a4f
5 changed files with 91 additions and 1 deletions
+9
View File
@@ -0,0 +1,9 @@
# title_flash.shp - Sparkle 4-puntes amb costats còncaus (Atari-style)
# 4 puntes als cardinals (radi 30) i valls còncaus als 45° (corba Bezier
# quadràtica amb control point ±8). 5 punts per arc subdividint la corba.
name: title_flash
scale: 1.0
center: 0, 0
polyline: 0,-30 3.76,-21.76 8.64,-14.64 14.64,-8.64 21.76,-3.76 30,0 21.76,3.76 14.64,8.64 8.64,14.64 3.76,21.76 0,30 -3.76,21.76 -8.64,14.64 -14.64,8.64 -21.76,3.76 -30,0 -21.76,-3.76 -14.64,-8.64 -8.64,-14.64 -3.76,-21.76 0,-30
+55
View File
@@ -73,6 +73,15 @@ TitleScene::TitleScene(SDLManager& sdl, SceneContext& context)
// correcte de la intro coreografiada (també quan venim de JUMP_TO_TITLE_MAIN).
ship_animator_->setVisible(false);
// Flash que tapa el "pop" final de la nau al VP. Es spawneja al centre
// de pantalla (= projecció del VP) quan ship_animator avisa.
flash_shape_ = Graphics::ShapeLoader::load("title_flash.shp");
ship_animator_->setOnShipDisappear([this](int /*player_id*/) {
triggerFlash(Vec2{
.x = static_cast<float>(Defaults::Window::WIDTH) / 2.0F,
.y = static_cast<float>(Defaults::Window::HEIGHT) / 2.0F});
});
initTitle();
inicialitzarJailgames();
@@ -294,6 +303,7 @@ void TitleScene::update(float delta_time) {
estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) {
ship_animator_->update(delta_time);
}
updateFlashes(delta_time);
switch (estat_actual_) {
case TitleState::STARFIELD_FADE_IN:
@@ -524,6 +534,7 @@ void TitleScene::draw() {
estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) {
ship_animator_->draw();
}
drawFlashes();
if (estat_actual_ == TitleState::STARFIELD_FADE_IN || estat_actual_ == TitleState::STARFIELD) {
return;
@@ -629,3 +640,47 @@ auto TitleScene::checkStartGameButtonPressed() -> bool {
void TitleScene::handleEvent(const SDL_Event& event) {
(void)event;
}
namespace {
constexpr float FLASH_DURATION = 0.40F;
constexpr float FLASH_MAX_SCALE = 2.5F;
constexpr SDL_Color FLASH_COLOR = {.r = 255, .g = 255, .b = 255, .a = 255};
} // namespace
void TitleScene::triggerFlash(Vec2 pos) {
for (auto& f : flashes_) {
if (!f.active) {
f.active = true;
f.position = pos;
f.timer = 0.0F;
return;
}
}
}
void TitleScene::updateFlashes(float delta_time) {
for (auto& f : flashes_) {
if (!f.active) {
continue;
}
f.timer += delta_time;
if (f.timer >= FLASH_DURATION) {
f.active = false;
}
}
}
void TitleScene::drawFlashes() {
if (!flash_shape_) {
return;
}
for (const auto& f : flashes_) {
if (!f.active) {
continue;
}
// Escala 0 → max al midpoint → 0. Sinus simètric.
const float T_NORM = f.timer / FLASH_DURATION;
const float SCALE = FLASH_MAX_SCALE * std::sin(T_NORM * Defaults::Math::PI);
Rendering::renderShape(sdl_.getRenderer(), flash_shape_, f.position, 0.0F, SCALE, 1.0F, 1.0F, FLASH_COLOR);
}
}
+14
View File
@@ -63,6 +63,20 @@ class TitleScene final : public Scene {
std::unique_ptr<Graphics::Camera3D> camera_;
std::unique_ptr<Graphics::Starfield> starfield_;
std::unique_ptr<Title::ShipAnimator> ship_animator_;
// Destell que tapa el "pop" final de cada nau quan arriba al VP.
// Pool fix de 2 (una per nau). Anima escala 0→max→0.
struct Flash {
Vec2 position{};
float timer{0.0F};
bool active{false};
};
std::array<Flash, 2> flashes_{};
std::shared_ptr<Graphics::Shape> flash_shape_;
void triggerFlash(Vec2 pos);
void updateFlashes(float delta_time);
void drawFlashes();
TitleState estat_actual_{TitleState::STARFIELD_FADE_IN};
float temps_acumulat_{0.0F};
+6 -1
View File
@@ -142,9 +142,14 @@ namespace Title {
case ShipState::FLOATING:
updateFloating(ship, delta_time);
break;
case ShipState::EXITING:
case ShipState::EXITING: {
updateExiting(ship, delta_time);
// Transició a invisible: la nau acaba d'arribar al VP.
if (!ship.visible && on_ship_disappear_) {
on_ship_disappear_(ship.player_id);
}
break;
}
}
}
}
+7
View File
@@ -10,6 +10,7 @@
#include <array>
#include <cstdint>
#include <functional>
#include "core/graphics/camera3d.hpp"
#include "core/graphics/wireframe3d.hpp"
@@ -70,10 +71,16 @@ namespace Title {
[[nodiscard]] auto isAnimationComplete() const -> bool;
[[nodiscard]] auto isVisible() const -> bool;
// Callback disparat quan una nau acaba l'EXITING (es torna invisible
// al VP). Útil per a un destell que tapi el pop final.
using ShipDisappearCallback = std::function<void(int player_id)>;
void setOnShipDisappear(ShipDisappearCallback cb) { on_ship_disappear_ = std::move(cb); }
private:
Rendering::Renderer* renderer_;
const Graphics::Camera3D* camera_;
std::array<TitleShip, 2> ships_;
ShipDisappearCallback on_ship_disappear_;
static void updateEntering(TitleShip& ship, float delta_time);
static void updateFloating(TitleShip& ship, float delta_time);