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).
|
// correcte de la intro coreografiada (també quan venim de JUMP_TO_TITLE_MAIN).
|
||||||
ship_animator_->setVisible(false);
|
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();
|
initTitle();
|
||||||
inicialitzarJailgames();
|
inicialitzarJailgames();
|
||||||
|
|
||||||
@@ -294,6 +303,7 @@ void TitleScene::update(float delta_time) {
|
|||||||
estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) {
|
estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) {
|
||||||
ship_animator_->update(delta_time);
|
ship_animator_->update(delta_time);
|
||||||
}
|
}
|
||||||
|
updateFlashes(delta_time);
|
||||||
|
|
||||||
switch (estat_actual_) {
|
switch (estat_actual_) {
|
||||||
case TitleState::STARFIELD_FADE_IN:
|
case TitleState::STARFIELD_FADE_IN:
|
||||||
@@ -524,6 +534,7 @@ void TitleScene::draw() {
|
|||||||
estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) {
|
estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) {
|
||||||
ship_animator_->draw();
|
ship_animator_->draw();
|
||||||
}
|
}
|
||||||
|
drawFlashes();
|
||||||
|
|
||||||
if (estat_actual_ == TitleState::STARFIELD_FADE_IN || estat_actual_ == TitleState::STARFIELD) {
|
if (estat_actual_ == TitleState::STARFIELD_FADE_IN || estat_actual_ == TitleState::STARFIELD) {
|
||||||
return;
|
return;
|
||||||
@@ -629,3 +640,47 @@ auto TitleScene::checkStartGameButtonPressed() -> bool {
|
|||||||
void TitleScene::handleEvent(const SDL_Event& event) {
|
void TitleScene::handleEvent(const SDL_Event& event) {
|
||||||
(void)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::Camera3D> camera_;
|
||||||
std::unique_ptr<Graphics::Starfield> starfield_;
|
std::unique_ptr<Graphics::Starfield> starfield_;
|
||||||
std::unique_ptr<Title::ShipAnimator> ship_animator_;
|
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};
|
TitleState estat_actual_{TitleState::STARFIELD_FADE_IN};
|
||||||
float temps_acumulat_{0.0F};
|
float temps_acumulat_{0.0F};
|
||||||
|
|
||||||
|
|||||||
@@ -142,9 +142,14 @@ namespace Title {
|
|||||||
case ShipState::FLOATING:
|
case ShipState::FLOATING:
|
||||||
updateFloating(ship, delta_time);
|
updateFloating(ship, delta_time);
|
||||||
break;
|
break;
|
||||||
case ShipState::EXITING:
|
case ShipState::EXITING: {
|
||||||
updateExiting(ship, delta_time);
|
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;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "core/graphics/camera3d.hpp"
|
#include "core/graphics/camera3d.hpp"
|
||||||
#include "core/graphics/wireframe3d.hpp"
|
#include "core/graphics/wireframe3d.hpp"
|
||||||
@@ -70,10 +71,16 @@ namespace Title {
|
|||||||
[[nodiscard]] auto isAnimationComplete() const -> bool;
|
[[nodiscard]] auto isAnimationComplete() const -> bool;
|
||||||
[[nodiscard]] auto isVisible() 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:
|
private:
|
||||||
Rendering::Renderer* renderer_;
|
Rendering::Renderer* renderer_;
|
||||||
const Graphics::Camera3D* camera_;
|
const Graphics::Camera3D* camera_;
|
||||||
std::array<TitleShip, 2> ships_;
|
std::array<TitleShip, 2> ships_;
|
||||||
|
ShipDisappearCallback on_ship_disappear_;
|
||||||
|
|
||||||
static void updateEntering(TitleShip& ship, float delta_time);
|
static void updateEntering(TitleShip& ship, float delta_time);
|
||||||
static void updateFloating(TitleShip& ship, float delta_time);
|
static void updateFloating(TitleShip& ship, float delta_time);
|
||||||
|
|||||||
Reference in New Issue
Block a user