tweak(hud): marcador en tres blocs ancorats (P1 esquerra, P2 dreta, nivell centrat) amb color per jugador
This commit is contained in:
@@ -28,12 +28,6 @@ namespace Defaults::Hud {
|
||||
// Nivell / sistema → verd.
|
||||
constexpr SDL_Color LEVEL_BRIGHT = {.r = 77, .g = 255, .b = 102, .a = 255}; // #4DFF66
|
||||
constexpr SDL_Color LEVEL_DIM = {.r = 29, .g = 107, .b = 44, .a = 255}; // #1D6B2C
|
||||
|
||||
// Colors legacy (es retiren en migrar el render del marcador).
|
||||
constexpr SDL_Color SCORE_P1 = {.r = 255, .g = 255, .b = 255, .a = 255}; // blanc
|
||||
constexpr SDL_Color SCORE_P2 = {.r = 255, .g = 130, .b = 200, .a = 255}; // rosa magenta
|
||||
constexpr SDL_Color LIVES = {.r = 255, .g = 180, .b = 60, .a = 255}; // ambre / or
|
||||
constexpr SDL_Color LEVEL = {.r = 155, .g = 255, .b = 175, .a = 255}; // verd sistema
|
||||
} // namespace Colors
|
||||
|
||||
// Vides representades com a icones de la nau (reutilitza la shape de la nau
|
||||
|
||||
@@ -814,7 +814,7 @@ void GameScene::drawInitHudState() {
|
||||
}
|
||||
|
||||
if (score_progress > 0.0F) {
|
||||
Systems::InitHud::drawScoreboardAnimated(text_, buildScoreboardSegments(), score_progress);
|
||||
Systems::InitHud::drawScoreboardAnimated(text_, buildScoreboardData(), score_progress);
|
||||
}
|
||||
|
||||
if (ship1_progress > 0.0F && match_config_.player1_active && ships_[0].isActive()) {
|
||||
@@ -933,41 +933,29 @@ void GameScene::drawScoreboard() {
|
||||
text_.renderCentered(Locale::get().text("demo.banner"), CENTER, SCALE, SPACING);
|
||||
return;
|
||||
}
|
||||
Systems::InitHud::drawScoreboardSegmentsAt(text_, buildScoreboardSegments(), CENTER, SCALE, SPACING);
|
||||
Systems::InitHud::drawScoreboardAt(text_, buildScoreboardData(), CENTER.y, SCALE, SPACING);
|
||||
}
|
||||
|
||||
auto GameScene::buildScoreboardSegments() const -> Systems::InitHud::ScoreboardSegments {
|
||||
Systems::InitHud::ScoreboardSegments out;
|
||||
auto GameScene::buildScoreboardData() const -> Systems::InitHud::ScoreboardData {
|
||||
Systems::InitHud::ScoreboardData out;
|
||||
|
||||
// Puntuació P1 (6 dígits) - zeros si inactiu
|
||||
if (match_config_.player1_active) {
|
||||
std::string s = std::to_string(score_per_player_[0]);
|
||||
out.score_p1 = std::string(6 - std::min(6, static_cast<int>(s.length())), '0') + s;
|
||||
out.lives_p1 = (lives_per_player_[0] < 10)
|
||||
? "0" + std::to_string(lives_per_player_[0])
|
||||
: std::to_string(lives_per_player_[0]);
|
||||
} else {
|
||||
out.score_p1 = "000000";
|
||||
out.lives_p1 = "00";
|
||||
}
|
||||
// Puntuació a 6 dígits amb zeros a l'esquerra (inactiu → tot zeros, 0 vides).
|
||||
const auto FORMAT_SCORE = [](int score) {
|
||||
const std::string S = std::to_string(score);
|
||||
return std::string(6 - std::min(6, static_cast<int>(S.length())), '0') + S;
|
||||
};
|
||||
|
||||
// Nivell (2 dígits) amb label localitzat
|
||||
out.score_p1 = match_config_.player1_active ? FORMAT_SCORE(score_per_player_[0]) : "000000";
|
||||
out.lives_p1 = match_config_.player1_active ? lives_per_player_[0] : 0;
|
||||
out.score_p2 = match_config_.player2_active ? FORMAT_SCORE(score_per_player_[1]) : "000000";
|
||||
out.lives_p2 = match_config_.player2_active ? lives_per_player_[1] : 0;
|
||||
|
||||
// Nivell: etiqueta localitzada + número a 2 dígits (separats per pintar-los
|
||||
// amb tonalitats distintes).
|
||||
const uint8_t STAGE_NUM = stage_manager_->getCurrentStage();
|
||||
const std::string STAGE_STR = (STAGE_NUM < 10) ? "0" + std::to_string(STAGE_NUM)
|
||||
: std::to_string(STAGE_NUM);
|
||||
out.level = Locale::get().text("hud.level") + STAGE_STR;
|
||||
|
||||
// Puntuació P2 (6 dígits) - zeros si inactiu
|
||||
if (match_config_.player2_active) {
|
||||
std::string s = std::to_string(score_per_player_[1]);
|
||||
out.score_p2 = std::string(6 - std::min(6, static_cast<int>(s.length())), '0') + s;
|
||||
out.lives_p2 = (lives_per_player_[1] < 10)
|
||||
? "0" + std::to_string(lives_per_player_[1])
|
||||
: std::to_string(lives_per_player_[1]);
|
||||
} else {
|
||||
out.score_p2 = "000000";
|
||||
out.lives_p2 = "00";
|
||||
}
|
||||
out.level_label = Locale::get().text("hud.level");
|
||||
out.level_value = (STAGE_NUM < 10) ? "0" + std::to_string(STAGE_NUM)
|
||||
: std::to_string(STAGE_NUM);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -140,9 +140,9 @@ class GameScene final : public Scene {
|
||||
void drawPlayingState();
|
||||
void drawLevelCompletedState();
|
||||
|
||||
// [NEW] Helper del marcador: construeix els 5 segments (score_p1, vides_p1,
|
||||
// level, score_p2, vides_p2) per a render colorit per segment.
|
||||
[[nodiscard]] auto buildScoreboardSegments() const -> Systems::InitHud::ScoreboardSegments;
|
||||
// Helper del marcador: construeix les dades (puntuacions, vides i nivell)
|
||||
// per al render en blocs ancorats per jugador.
|
||||
[[nodiscard]] auto buildScoreboardData() const -> Systems::InitHud::ScoreboardData;
|
||||
|
||||
// Sub-pasos de update() (descompuestos en Fase 9d para reducir
|
||||
// complejidad cognitiva; cada uno es responsable de una sección).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,15 +21,17 @@
|
||||
|
||||
namespace Systems::InitHud {
|
||||
|
||||
// Segments del marcador. Cada segment es renderitza amb el seu propi color
|
||||
// (vegeu Defaults::Hud::Colors). El layout final concatena en aquest ordre
|
||||
// amb separadors d'1, 2, 2, 1 espais respectivament (igual que el legacy).
|
||||
struct ScoreboardSegments {
|
||||
std::string score_p1;
|
||||
std::string lives_p1;
|
||||
std::string level; // ex: "NIVELL 01"
|
||||
std::string score_p2;
|
||||
std::string lives_p2;
|
||||
// Dades del marcador. El render reparteix tres blocs ancorats: bloc P1 a
|
||||
// l'esquerra i bloc P2 a la dreta (mateix ordre intern "punts vides", no
|
||||
// mirrored), i el nivell centrat. Cada bloc de jugador es pinta amb el seu
|
||||
// color (vegeu Defaults::Hud::Colors); el nivell, en verd de sistema.
|
||||
struct ScoreboardData {
|
||||
std::string score_p1; // 6 dígits, zeros a l'esquerra
|
||||
std::string score_p2; // 6 dígits, zeros a l'esquerra
|
||||
int lives_p1{0}; // vides P1 (icones de nau al render)
|
||||
int lives_p2{0}; // vides P2
|
||||
std::string level_label; // ex: "NIVELL "
|
||||
std::string level_value; // ex: "01"
|
||||
};
|
||||
|
||||
// Convierte un progreso global 0..1 al sub-progreso de un elemento que solo
|
||||
@@ -51,19 +53,19 @@ namespace Systems::InitHud {
|
||||
// 66..100% → línea inferior crece desde los lados hacia el centro.
|
||||
void drawBordersAnimated(Rendering::Renderer* renderer, float progress);
|
||||
|
||||
// Dibuixa els 5 segments del scoreboard centrats al voltant de `center`,
|
||||
// cadascun amb el seu color (Defaults::Hud::Colors). Separadors de 1/2/2/1
|
||||
// espais entre segments per preservar el layout legacy.
|
||||
void drawScoreboardSegmentsAt(const Graphics::VectorText& text,
|
||||
const ScoreboardSegments& segments,
|
||||
const Vec2& center,
|
||||
// Dibuixa el marcador en tres blocs ancorats a la fila d'alçada `center_y`:
|
||||
// bloc P1 a l'esquerra, bloc P2 a la dreta i nivell centrat. Cada bloc amb
|
||||
// el seu color (Defaults::Hud::Colors).
|
||||
void drawScoreboardAt(const Graphics::VectorText& text,
|
||||
const ScoreboardData& data,
|
||||
float center_y,
|
||||
float scale,
|
||||
float spacing);
|
||||
|
||||
// Dibuixa el scoreboard centrat, pujant des de fora de la pantalla fins a
|
||||
// la seva posició final amb easing. Delega a drawScoreboardSegmentsAt.
|
||||
// Dibuixa el marcador pujant des de fora de la pantalla fins a la seva
|
||||
// posició final amb easing. Delega a drawScoreboardAt.
|
||||
void drawScoreboardAnimated(const Graphics::VectorText& text,
|
||||
const ScoreboardSegments& segments,
|
||||
const ScoreboardData& data,
|
||||
float progress);
|
||||
|
||||
} // namespace Systems::InitHud
|
||||
|
||||
Reference in New Issue
Block a user