tweak(hud): marcador en tres blocs ancorats (P1 esquerra, P2 dreta, nivell centrat) amb color per jugador

This commit is contained in:
2026-05-29 20:09:28 +02:00
parent e4f8f586d6
commit 317e2a3fd9
5 changed files with 104 additions and 88 deletions
+62 -30
View File
@@ -78,53 +78,85 @@ namespace Systems::InitHud {
}
}
void drawScoreboardSegmentsAt(const Graphics::VectorText& text,
const ScoreboardSegments& segments,
const Vec2& center,
namespace {
// Vides com a text (2 dígits) — provisional fins a migrar-les a icones.
auto livesText(int lives) -> std::string {
const std::string S = std::to_string(lives);
return (lives < 10) ? "0" + S : S;
}
// Pinta el bloc d'un jugador "punts vides" amb el seu color. Si
// right_align, el bloc acaba a anchor_x (ancorat a la dreta); si no,
// comença a anchor_x (ancorat a l'esquerra).
void drawPlayerBlock(const Graphics::VectorText& text,
const std::string& score,
int lives,
SDL_Color color,
float anchor_x,
float top_y,
float scale,
float spacing,
bool right_align) {
const std::string LIVES_STR = livesText(lives);
const float W_SCORE = Graphics::VectorText::getTextWidth(score, scale, spacing);
const float W_LIVES = Graphics::VectorText::getTextWidth(LIVES_STR, scale, spacing);
const float BLOCK_W = W_SCORE + Defaults::Hud::Layout::BLOCK_INNER_GAP + W_LIVES;
float x = right_align ? (anchor_x - BLOCK_W) : anchor_x;
text.render(score, {.x = x, .y = top_y}, scale, spacing, 1.0F, color);
x += W_SCORE + Defaults::Hud::Layout::BLOCK_INNER_GAP;
text.render(LIVES_STR, {.x = x, .y = top_y}, scale, spacing, 1.0F, color);
}
// Pinta el nivell ("NIVELL" + número) centrat a la pantalla.
void drawLevel(const Graphics::VectorText& text,
const std::string& label,
const std::string& value,
float top_y,
float scale,
float spacing) {
const float W_LABEL = Graphics::VectorText::getTextWidth(label, scale, spacing);
const float W_VALUE = Graphics::VectorText::getTextWidth(value, scale, spacing);
const float CX = Defaults::Game::WIDTH / 2.0F;
float x = CX - ((W_LABEL + W_VALUE) / 2.0F);
text.render(label, {.x = x, .y = top_y}, scale, spacing, 1.0F, Defaults::Hud::Colors::LEVEL_BRIGHT);
x += W_LABEL;
text.render(value, {.x = x, .y = top_y}, scale, spacing, 1.0F, Defaults::Hud::Colors::LEVEL_BRIGHT);
}
} // namespace
void drawScoreboardAt(const Graphics::VectorText& text,
const ScoreboardData& data,
float center_y,
float scale,
float spacing) {
// Separadors entre segments (preservant el layout legacy: " ", " ", " ", " ").
const float W_SEP1 = Graphics::VectorText::getTextWidth(" ", scale, spacing);
const float W_SEP2 = Graphics::VectorText::getTextWidth(" ", scale, spacing);
// Els blocs s'ancoren a les verticals del PLAYAREA (sota el marc).
const SDL_FRect& play = Defaults::Zones::PLAYAREA;
const float LEFT = play.x;
const float RIGHT = play.x + play.w;
const float TOP_Y = center_y - (Graphics::VectorText::getTextHeight(scale) / 2.0F);
const float W_SP1 = Graphics::VectorText::getTextWidth(segments.score_p1, scale, spacing);
const float W_LP1 = Graphics::VectorText::getTextWidth(segments.lives_p1, scale, spacing);
const float W_LV = Graphics::VectorText::getTextWidth(segments.level, scale, spacing);
const float W_SP2 = Graphics::VectorText::getTextWidth(segments.score_p2, scale, spacing);
const float W_LP2 = Graphics::VectorText::getTextWidth(segments.lives_p2, scale, spacing);
const float TOTAL = W_SP1 + W_SEP1 + W_LP1 + W_SEP2 + W_LV + W_SEP2 + W_SP2 + W_SEP1 + W_LP2;
const float HEIGHT = Graphics::VectorText::getTextHeight(scale);
const float TOP_Y = center.y - (HEIGHT / 2.0F);
float x = center.x - (TOTAL / 2.0F);
text.render(segments.score_p1, {.x = x, .y = TOP_Y}, scale, spacing, 1.0F, Defaults::Hud::Colors::SCORE_P1);
x += W_SP1 + W_SEP1;
text.render(segments.lives_p1, {.x = x, .y = TOP_Y}, scale, spacing, 1.0F, Defaults::Hud::Colors::LIVES);
x += W_LP1 + W_SEP2;
text.render(segments.level, {.x = x, .y = TOP_Y}, scale, spacing, 1.0F, Defaults::Hud::Colors::LEVEL);
x += W_LV + W_SEP2;
text.render(segments.score_p2, {.x = x, .y = TOP_Y}, scale, spacing, 1.0F, Defaults::Hud::Colors::SCORE_P2);
x += W_SP2 + W_SEP1;
text.render(segments.lives_p2, {.x = x, .y = TOP_Y}, scale, spacing, 1.0F, Defaults::Hud::Colors::LIVES);
drawPlayerBlock(text, data.score_p1, data.lives_p1, Defaults::Hud::Colors::P1_BRIGHT, LEFT, TOP_Y, scale, spacing, false);
drawPlayerBlock(text, data.score_p2, data.lives_p2, Defaults::Hud::Colors::P2_BRIGHT, RIGHT, TOP_Y, scale, spacing, true);
drawLevel(text, data.level_label, data.level_value, TOP_Y, scale, spacing);
}
void drawScoreboardAnimated(const Graphics::VectorText& text,
const ScoreboardSegments& segments,
const ScoreboardData& data,
float progress) {
const float EASED = Easing::easeOutQuad(progress);
constexpr float SCALE = Defaults::Hud::SCOREBOARD_TEXT_SCALE;
constexpr float SPACING = Defaults::Hud::SCOREBOARD_TEXT_SPACING;
const SDL_FRect& scoreboard_zone = Defaults::Zones::SCOREBOARD;
const float CENTRE_X = scoreboard_zone.w / 2.0F;
const float Y_FINAL = scoreboard_zone.y + (scoreboard_zone.h / 2.0F);
// Posición inicial: fuera de la pantalla por debajo.
const auto Y_INI = static_cast<float>(Defaults::Game::HEIGHT);
const float Y_ANIM = Y_INI + ((Y_FINAL - Y_INI) * EASED);
drawScoreboardSegmentsAt(text, segments, {.x = CENTRE_X, .y = Y_ANIM}, SCALE, SPACING);
drawScoreboardAt(text, data, Y_ANIM, SCALE, SPACING);
}
} // namespace Systems::InitHud