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.
|
// Nivell / sistema → verd.
|
||||||
constexpr SDL_Color LEVEL_BRIGHT = {.r = 77, .g = 255, .b = 102, .a = 255}; // #4DFF66
|
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
|
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
|
} // namespace Colors
|
||||||
|
|
||||||
// Vides representades com a icones de la nau (reutilitza la shape de la nau
|
// 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) {
|
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()) {
|
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);
|
text_.renderCentered(Locale::get().text("demo.banner"), CENTER, SCALE, SPACING);
|
||||||
return;
|
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 {
|
auto GameScene::buildScoreboardData() const -> Systems::InitHud::ScoreboardData {
|
||||||
Systems::InitHud::ScoreboardSegments out;
|
Systems::InitHud::ScoreboardData out;
|
||||||
|
|
||||||
// Puntuació P1 (6 dígits) - zeros si inactiu
|
// Puntuació a 6 dígits amb zeros a l'esquerra (inactiu → tot zeros, 0 vides).
|
||||||
if (match_config_.player1_active) {
|
const auto FORMAT_SCORE = [](int score) {
|
||||||
std::string s = std::to_string(score_per_player_[0]);
|
const std::string S = std::to_string(score);
|
||||||
out.score_p1 = std::string(6 - std::min(6, static_cast<int>(s.length())), '0') + s;
|
return 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";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 uint8_t STAGE_NUM = stage_manager_->getCurrentStage();
|
||||||
const std::string STAGE_STR = (STAGE_NUM < 10) ? "0" + std::to_string(STAGE_NUM)
|
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);
|
: 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";
|
|
||||||
}
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,9 +140,9 @@ class GameScene final : public Scene {
|
|||||||
void drawPlayingState();
|
void drawPlayingState();
|
||||||
void drawLevelCompletedState();
|
void drawLevelCompletedState();
|
||||||
|
|
||||||
// [NEW] Helper del marcador: construeix els 5 segments (score_p1, vides_p1,
|
// Helper del marcador: construeix les dades (puntuacions, vides i nivell)
|
||||||
// level, score_p2, vides_p2) per a render colorit per segment.
|
// per al render en blocs ancorats per jugador.
|
||||||
[[nodiscard]] auto buildScoreboardSegments() const -> Systems::InitHud::ScoreboardSegments;
|
[[nodiscard]] auto buildScoreboardData() const -> Systems::InitHud::ScoreboardData;
|
||||||
|
|
||||||
// Sub-pasos de update() (descompuestos en Fase 9d para reducir
|
// Sub-pasos de update() (descompuestos en Fase 9d para reducir
|
||||||
// complejidad cognitiva; cada uno es responsable de una sección).
|
// complejidad cognitiva; cada uno es responsable de una sección).
|
||||||
|
|||||||
@@ -78,53 +78,85 @@ namespace Systems::InitHud {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawScoreboardSegmentsAt(const Graphics::VectorText& text,
|
namespace {
|
||||||
const ScoreboardSegments& segments,
|
|
||||||
const Vec2& center,
|
// 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 scale,
|
||||||
float spacing) {
|
float spacing) {
|
||||||
// Separadors entre segments (preservant el layout legacy: " ", " ", " ", " ").
|
const float W_LABEL = Graphics::VectorText::getTextWidth(label, scale, spacing);
|
||||||
const float W_SEP1 = Graphics::VectorText::getTextWidth(" ", scale, spacing);
|
const float W_VALUE = Graphics::VectorText::getTextWidth(value, scale, spacing);
|
||||||
const float W_SEP2 = Graphics::VectorText::getTextWidth(" ", 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);
|
||||||
|
}
|
||||||
|
|
||||||
const float W_SP1 = Graphics::VectorText::getTextWidth(segments.score_p1, scale, spacing);
|
} // namespace
|
||||||
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;
|
void drawScoreboardAt(const Graphics::VectorText& text,
|
||||||
|
const ScoreboardData& data,
|
||||||
|
float center_y,
|
||||||
|
float scale,
|
||||||
|
float 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 HEIGHT = Graphics::VectorText::getTextHeight(scale);
|
drawPlayerBlock(text, data.score_p1, data.lives_p1, Defaults::Hud::Colors::P1_BRIGHT, LEFT, TOP_Y, scale, spacing, false);
|
||||||
const float TOP_Y = center.y - (HEIGHT / 2.0F);
|
drawPlayerBlock(text, data.score_p2, data.lives_p2, Defaults::Hud::Colors::P2_BRIGHT, RIGHT, TOP_Y, scale, spacing, true);
|
||||||
float x = center.x - (TOTAL / 2.0F);
|
drawLevel(text, data.level_label, data.level_value, TOP_Y, scale, spacing);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawScoreboardAnimated(const Graphics::VectorText& text,
|
void drawScoreboardAnimated(const Graphics::VectorText& text,
|
||||||
const ScoreboardSegments& segments,
|
const ScoreboardData& data,
|
||||||
float progress) {
|
float progress) {
|
||||||
const float EASED = Easing::easeOutQuad(progress);
|
const float EASED = Easing::easeOutQuad(progress);
|
||||||
|
|
||||||
constexpr float SCALE = Defaults::Hud::SCOREBOARD_TEXT_SCALE;
|
constexpr float SCALE = Defaults::Hud::SCOREBOARD_TEXT_SCALE;
|
||||||
constexpr float SPACING = Defaults::Hud::SCOREBOARD_TEXT_SPACING;
|
constexpr float SPACING = Defaults::Hud::SCOREBOARD_TEXT_SPACING;
|
||||||
const SDL_FRect& scoreboard_zone = Defaults::Zones::SCOREBOARD;
|
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);
|
const float Y_FINAL = scoreboard_zone.y + (scoreboard_zone.h / 2.0F);
|
||||||
// Posición inicial: fuera de la pantalla por debajo.
|
// Posición inicial: fuera de la pantalla por debajo.
|
||||||
const auto Y_INI = static_cast<float>(Defaults::Game::HEIGHT);
|
const auto Y_INI = static_cast<float>(Defaults::Game::HEIGHT);
|
||||||
const float Y_ANIM = Y_INI + ((Y_FINAL - Y_INI) * EASED);
|
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
|
} // namespace Systems::InitHud
|
||||||
|
|||||||
@@ -21,15 +21,17 @@
|
|||||||
|
|
||||||
namespace Systems::InitHud {
|
namespace Systems::InitHud {
|
||||||
|
|
||||||
// Segments del marcador. Cada segment es renderitza amb el seu propi color
|
// Dades del marcador. El render reparteix tres blocs ancorats: bloc P1 a
|
||||||
// (vegeu Defaults::Hud::Colors). El layout final concatena en aquest ordre
|
// l'esquerra i bloc P2 a la dreta (mateix ordre intern "punts vides", no
|
||||||
// amb separadors d'1, 2, 2, 1 espais respectivament (igual que el legacy).
|
// mirrored), i el nivell centrat. Cada bloc de jugador es pinta amb el seu
|
||||||
struct ScoreboardSegments {
|
// color (vegeu Defaults::Hud::Colors); el nivell, en verd de sistema.
|
||||||
std::string score_p1;
|
struct ScoreboardData {
|
||||||
std::string lives_p1;
|
std::string score_p1; // 6 dígits, zeros a l'esquerra
|
||||||
std::string level; // ex: "NIVELL 01"
|
std::string score_p2; // 6 dígits, zeros a l'esquerra
|
||||||
std::string score_p2;
|
int lives_p1{0}; // vides P1 (icones de nau al render)
|
||||||
std::string lives_p2;
|
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
|
// 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.
|
// 66..100% → línea inferior crece desde los lados hacia el centro.
|
||||||
void drawBordersAnimated(Rendering::Renderer* renderer, float progress);
|
void drawBordersAnimated(Rendering::Renderer* renderer, float progress);
|
||||||
|
|
||||||
// Dibuixa els 5 segments del scoreboard centrats al voltant de `center`,
|
// Dibuixa el marcador en tres blocs ancorats a la fila d'alçada `center_y`:
|
||||||
// cadascun amb el seu color (Defaults::Hud::Colors). Separadors de 1/2/2/1
|
// bloc P1 a l'esquerra, bloc P2 a la dreta i nivell centrat. Cada bloc amb
|
||||||
// espais entre segments per preservar el layout legacy.
|
// el seu color (Defaults::Hud::Colors).
|
||||||
void drawScoreboardSegmentsAt(const Graphics::VectorText& text,
|
void drawScoreboardAt(const Graphics::VectorText& text,
|
||||||
const ScoreboardSegments& segments,
|
const ScoreboardData& data,
|
||||||
const Vec2& center,
|
float center_y,
|
||||||
float scale,
|
float scale,
|
||||||
float spacing);
|
float spacing);
|
||||||
|
|
||||||
// Dibuixa el scoreboard centrat, pujant des de fora de la pantalla fins a
|
// Dibuixa el marcador pujant des de fora de la pantalla fins a la seva
|
||||||
// la seva posició final amb easing. Delega a drawScoreboardSegmentsAt.
|
// posició final amb easing. Delega a drawScoreboardAt.
|
||||||
void drawScoreboardAnimated(const Graphics::VectorText& text,
|
void drawScoreboardAnimated(const Graphics::VectorText& text,
|
||||||
const ScoreboardSegments& segments,
|
const ScoreboardData& data,
|
||||||
float progress);
|
float progress);
|
||||||
|
|
||||||
} // namespace Systems::InitHud
|
} // namespace Systems::InitHud
|
||||||
|
|||||||
Reference in New Issue
Block a user