tweak(hud): vides com a slots fixos (NUM_SLOTS = MAX_VIDES-1) que s'encenen/atenuen

This commit is contained in:
2026-05-29 20:43:26 +02:00
parent 462e91d967
commit 17e9206d26
2 changed files with 45 additions and 48 deletions
+3 -15
View File
@@ -30,21 +30,9 @@ namespace Defaults::Hud {
constexpr SDL_Color LEVEL_DIM = {.r = 29, .g = 107, .b = 44, .a = 255}; // #1D6B2C
} // namespace Colors
// Vides representades com a icones de la nau (reutilitza la shape de la nau
// escalada, no un número). Mides derivades de l'alçada del marcador
// (Defaults::Zones::SCOREBOARD_BOTTOM_H) per encaixar-hi sempre.
namespace Lives {
constexpr float ICON_HEIGHT_RATIO = 0.45F; // alçada de la icona com a fracció de l'alçada del marcador
constexpr float ICON_SPACING_FACTOR = 1.35F; // separació centre-a-centre = alçada_icona × factor
constexpr int MAX_ICONS = 5; // límit d'icones dibuixades (acota l'ample del bloc)
} // namespace Lives
// Disposició del marcador: bloc P1 ancorat a l'esquerra, bloc P2 a la dreta
// (mateix ordre intern "punts vides", no mirrored) i nivell centrat. Els
// blocs s'alineen amb les verticals del PLAYAREA.
namespace Layout {
constexpr float BLOCK_INNER_GAP = 24.0F; // separació punts↔vides dins d'un bloc (px lògics)
} // namespace Layout
// Les vides es dibuixen com a slots fixos de naus en miniatura (NUM_SLOTS =
// MAX_VIDES 1). Mida i pas dels slots es deriven de la mètrica del glif del
// dígit a init_hud_animator, no de constants soltes.
// Animación de entrada del HUD (init_hud_animator).
namespace InitAnim {
+42 -33
View File
@@ -81,49 +81,57 @@ namespace Systems::InitHud {
namespace {
// Alçada de la icona de vida (px lògics) derivada de l'alçada del marcador.
auto lifeIconHeight() -> float {
return Defaults::Hud::Lives::ICON_HEIGHT_RATIO * Defaults::Zones::SCOREBOARD_BOTTOM_H;
// Nombre de slots de vides: una nau menys que el màxim (la nau en joc
// no es dibuixa; els slots són repuestos). Deriva de MAX_VIDES.
constexpr int NUM_SLOTS = Defaults::Game::MAX_VIDES - 1;
// Pas d'un dígit (amplada + tracking, escalat): és la diferència entre
// l'ample de dos caràcters i el d'un. Marca el ritme de tot el bloc.
auto digitPitch(float scale, float spacing) -> float {
return Graphics::VectorText::getTextWidth("00", scale, spacing) -
Graphics::VectorText::getTextWidth("0", scale, spacing);
}
// Nombre d'icones de vida a dibuixar (acotat a MAX_ICONS, mai negatiu).
auto lifeIconCount(int lives) -> int {
return std::clamp(lives, 0, Defaults::Hud::Lives::MAX_ICONS);
// Mida d'un slot = alçada de la majúscula del dígit (mètrica del glif).
auto slotSize(float scale) -> float {
return Graphics::VectorText::getTextHeight(scale);
}
// Ample del bloc de vides (0 si no hi ha vides). N icones = (N-1) passos
// de separació + l'amplada d'una icona.
auto livesBlockWidth(int lives) -> float {
const int N = lifeIconCount(lives);
if (N <= 0) {
// Ample del bloc de slots: constant, independent de les vides. NUM_SLOTS
// slots al pas del dígit (l'últim slot ocupa la seva pròpia mida).
auto slotsBlockWidth(float scale, float spacing) -> float {
if (NUM_SLOTS <= 0) {
return 0.0F;
}
const float ICON_H = lifeIconHeight();
const float STEP = ICON_H * Defaults::Hud::Lives::ICON_SPACING_FACTOR;
return (static_cast<float>(N - 1) * STEP) + ICON_H;
return (static_cast<float>(NUM_SLOTS - 1) * digitPitch(scale, spacing)) + slotSize(scale);
}
// Dibuixa les vides com a icones de la nau (apuntant amunt, color del
// jugador). El glow el posa el shader. x_left = vora esquerra del bloc.
void drawLives(Rendering::Renderer* renderer,
// Dibuixa els slots de vides com a naus en miniatura en posicions FIXES.
// Slot amb vida disponible (repuesto) → color encès; slot buit → atenuat.
// Repuestos = vides 1 (la nau en joc no compta com a slot).
void drawSlots(Rendering::Renderer* renderer,
const std::shared_ptr<Graphics::Shape>& shape,
int lives,
SDL_Color color,
SDL_Color bright,
SDL_Color dim,
float x_left,
float center_y) {
const int N = lifeIconCount(lives);
if (N <= 0 || !shape) {
float center_y,
float scale,
float spacing) {
if (NUM_SLOTS <= 0 || !shape) {
return;
}
const float ICON_H = lifeIconHeight();
const float STEP = ICON_H * Defaults::Hud::Lives::ICON_SPACING_FACTOR;
// Escala que ajusta el cercle circumscrit de la shape a l'alçada
// objectiu (mida predictible independent del .shp).
const float SIZE = slotSize(scale);
const float PITCH = digitPitch(scale, spacing);
// Escala que ajusta el cercle circumscrit de la shape a la mida del
// slot (mida predictible independent del .shp).
const float RADIUS = shape->getBoundingRadius();
const float SCALE = (RADIUS > 0.001F) ? (ICON_H / (2.0F * RADIUS)) : 1.0F;
for (int i = 0; i < N; i++) {
const Vec2 POS = {.x = x_left + (ICON_H / 2.0F) + (static_cast<float>(i) * STEP), .y = center_y};
Rendering::renderShape(renderer, shape, POS, 0.0F, SCALE, 1.0F, 1.0F, color);
const float ICON_SCALE = (RADIUS > 0.001F) ? (SIZE / (2.0F * RADIUS)) : 1.0F;
const int FILLED = std::clamp(lives - 1, 0, NUM_SLOTS);
for (int i = 0; i < NUM_SLOTS; i++) {
const SDL_Color COLOR = (i < FILLED) ? bright : dim;
const Vec2 POS = {.x = x_left + (SIZE / 2.0F) + (static_cast<float>(i) * PITCH), .y = center_y};
Rendering::renderShape(renderer, shape, POS, 0.0F, ICON_SCALE, 1.0F, 1.0F, COLOR);
}
}
@@ -176,13 +184,14 @@ namespace Systems::InitHud {
bool right_align) {
const float TOP_Y = center_y - (Graphics::VectorText::getTextHeight(scale) / 2.0F);
const float W_SCORE = Graphics::VectorText::getTextWidth(score, scale, spacing);
const float W_LIVES = livesBlockWidth(lives);
const float BLOCK_W = W_SCORE + Defaults::Hud::Layout::BLOCK_INNER_GAP + W_LIVES;
const float GAP = digitPitch(scale, spacing); // separació punts↔slots = un pas de dígit
const float W_LIVES = slotsBlockWidth(scale, spacing);
const float BLOCK_W = W_SCORE + GAP + W_LIVES;
float x = right_align ? (anchor_x - BLOCK_W) : anchor_x;
drawScore(text, score, bright, dim, x, TOP_Y, scale, spacing);
x += W_SCORE + Defaults::Hud::Layout::BLOCK_INNER_GAP;
drawLives(renderer, shape, lives, bright, x, center_y);
x += W_SCORE + GAP;
drawSlots(renderer, shape, lives, bright, dim, x, center_y, scale, spacing);
}
// Pinta el nivell centrat: etiqueta "NIVELL" en verd atenuat i el número