feat(title): destell hiperespacial al VP quan la nau desapareix (sparkle 4-puntes còncau)
This commit is contained in:
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user