feat(hud): parpadeig configurable als prompts inactius
This commit is contained in:
1 parent
642e0653b2
commit
793e87a203
6 files changed
+43
No files matched your search
@@ -73,6 +73,12 @@ hud:
|
|||||||
entry_ms: 800 # durada de l'animació d'entrada dels tres blocs
|
entry_ms: 800 # durada de l'animació d'entrada dels tres blocs
|
||||||
flash_ms: 250 # durada del flash quan un jugador rep un hit
|
flash_ms: 250 # durada del flash quan un jugador rep un hit
|
||||||
shake_ms: 300 # durada del shake que acompanya el flash
|
shake_ms: 300 # durada del shake que acompanya el flash
|
||||||
|
# Parpadeig dels prompts inactius ("Polsa start..." / "Espera..."). Un
|
||||||
|
# únic temporitzador global al Hud sincronitza P1 i P2 i no es reinicia
|
||||||
|
# quan el text canvia entre CAN_JOIN i WAIT. cycle<=0 o ratio>=1 →
|
||||||
|
# sempre visible (disable trivial).
|
||||||
|
blink_cycle_ms: 1000 # durada del cicle complet
|
||||||
|
blink_visible_ratio: 0.8 # proporció del cicle visible (0..1)
|
||||||
|
|
||||||
# Famílies de color del HUD per bloc. Cada bloc té `label` (to apagat
|
# Famílies de color del HUD per bloc. Cada bloc té `label` (to apagat
|
||||||
# per etiquetes), `value` (to viu per a la xifra) i `shadow` (to fosc
|
# per etiquetes), `value` (to viu per a la xifra) i `shadow` (to fosc
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ UiConfig::UiConfig()
|
|||||||
hud_entry_ms_(Defaults::UI::HUD_ENTRY_MS),
|
hud_entry_ms_(Defaults::UI::HUD_ENTRY_MS),
|
||||||
hud_flash_ms_(Defaults::UI::HUD_FLASH_MS),
|
hud_flash_ms_(Defaults::UI::HUD_FLASH_MS),
|
||||||
hud_shake_ms_(Defaults::UI::HUD_SHAKE_MS),
|
hud_shake_ms_(Defaults::UI::HUD_SHAKE_MS),
|
||||||
|
hud_blink_cycle_ms_(Defaults::UI::HUD_BLINK_CYCLE_MS),
|
||||||
|
hud_blink_visible_ratio_(Defaults::UI::HUD_BLINK_VISIBLE_RATIO),
|
||||||
pause_enter_ms_(Defaults::UI::PAUSE_ENTER_MS),
|
pause_enter_ms_(Defaults::UI::PAUSE_ENTER_MS),
|
||||||
pause_exit_ms_(Defaults::UI::PAUSE_EXIT_MS),
|
pause_exit_ms_(Defaults::UI::PAUSE_EXIT_MS),
|
||||||
pause_enter_curve_(curveByName(Defaults::UI::PAUSE_ENTER_CURVE)),
|
pause_enter_curve_(curveByName(Defaults::UI::PAUSE_ENTER_CURVE)),
|
||||||
@@ -267,6 +269,8 @@ void UiConfig::load(const std::string& file_path) {
|
|||||||
readField(h, "entry_ms", hud_entry_ms_);
|
readField(h, "entry_ms", hud_entry_ms_);
|
||||||
readField(h, "flash_ms", hud_flash_ms_);
|
readField(h, "flash_ms", hud_flash_ms_);
|
||||||
readField(h, "shake_ms", hud_shake_ms_);
|
readField(h, "shake_ms", hud_shake_ms_);
|
||||||
|
readField(h, "blink_cycle_ms", hud_blink_cycle_ms_);
|
||||||
|
readField(h, "blink_visible_ratio", hud_blink_visible_ratio_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (yaml.contains("pause")) {
|
if (yaml.contains("pause")) {
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ class UiConfig {
|
|||||||
[[nodiscard]] auto hudEntryMs() const -> int { return hud_entry_ms_; }
|
[[nodiscard]] auto hudEntryMs() const -> int { return hud_entry_ms_; }
|
||||||
[[nodiscard]] auto hudFlashMs() const -> int { return hud_flash_ms_; }
|
[[nodiscard]] auto hudFlashMs() const -> int { return hud_flash_ms_; }
|
||||||
[[nodiscard]] auto hudShakeMs() const -> int { return hud_shake_ms_; }
|
[[nodiscard]] auto hudShakeMs() const -> int { return hud_shake_ms_; }
|
||||||
|
[[nodiscard]] auto hudBlinkCycleMs() const -> int { return hud_blink_cycle_ms_; }
|
||||||
|
[[nodiscard]] auto hudBlinkVisibleRatio() const -> float { return hud_blink_visible_ratio_; }
|
||||||
[[nodiscard]] auto pauseEnterMs() const -> int { return pause_enter_ms_; }
|
[[nodiscard]] auto pauseEnterMs() const -> int { return pause_enter_ms_; }
|
||||||
[[nodiscard]] auto pauseExitMs() const -> int { return pause_exit_ms_; }
|
[[nodiscard]] auto pauseExitMs() const -> int { return pause_exit_ms_; }
|
||||||
|
|
||||||
@@ -238,6 +240,8 @@ class UiConfig {
|
|||||||
int hud_entry_ms_;
|
int hud_entry_ms_;
|
||||||
int hud_flash_ms_;
|
int hud_flash_ms_;
|
||||||
int hud_shake_ms_;
|
int hud_shake_ms_;
|
||||||
|
int hud_blink_cycle_ms_;
|
||||||
|
float hud_blink_visible_ratio_;
|
||||||
int pause_enter_ms_;
|
int pause_enter_ms_;
|
||||||
int pause_exit_ms_;
|
int pause_exit_ms_;
|
||||||
CurveFn pause_enter_curve_;
|
CurveFn pause_enter_curve_;
|
||||||
|
|||||||
@@ -188,6 +188,10 @@ void Hud::update(int delta_ms) {
|
|||||||
if (b.shake_ms_left > 0) { b.shake_ms_left = std::max(0, b.shake_ms_left - delta_ms); }
|
if (b.shake_ms_left > 0) { b.shake_ms_left = std::max(0, b.shake_ms_left - delta_ms); }
|
||||||
if (b.flash_ms_left > 0) { b.flash_ms_left = std::max(0, b.flash_ms_left - delta_ms); }
|
if (b.flash_ms_left > 0) { b.flash_ms_left = std::max(0, b.flash_ms_left - delta_ms); }
|
||||||
}
|
}
|
||||||
|
const int CYCLE = UiConfig::get().hudBlinkCycleMs();
|
||||||
|
if (CYCLE > 0) {
|
||||||
|
blink_elapsed_ms_ = (blink_elapsed_ms_ + delta_ms) % CYCLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hud::setMode(BlockId block, Mode mode) {
|
void Hud::setMode(BlockId block, Mode mode) {
|
||||||
@@ -238,6 +242,16 @@ auto Hud::computeOffset(const Block& b) -> SDL_FPoint {
|
|||||||
return off;
|
return off;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Hud::blinkVisible() const -> bool {
|
||||||
|
const auto& ui = UiConfig::get();
|
||||||
|
const int CYCLE = ui.hudBlinkCycleMs();
|
||||||
|
const float RATIO = ui.hudBlinkVisibleRatio();
|
||||||
|
if (CYCLE <= 0 || RATIO >= 1.0F) { return true; }
|
||||||
|
if (RATIO <= 0.0F) { return false; }
|
||||||
|
const int VISIBLE_MS = static_cast<int>(static_cast<float>(CYCLE) * RATIO);
|
||||||
|
return blink_elapsed_ms_ < VISIBLE_MS;
|
||||||
|
}
|
||||||
|
|
||||||
void Hud::render() const {
|
void Hud::render() const {
|
||||||
// Backdrop de la franja del HUD. Es pinta abans dels tres blocs perquè,
|
// Backdrop de la franja del HUD. Es pinta abans dels tres blocs perquè,
|
||||||
// quan un bloc es mou (entry slide o shake), la zona que deixa al
|
// quan un bloc es mou (entry slide o shake), la zona que deixa al
|
||||||
@@ -295,6 +309,7 @@ void Hud::renderIdleSide(int x, int y, int w, const Block& blk, int player_index
|
|||||||
// el mode fixat per la GameScene cada frame. El render de zeros
|
// el mode fixat per la GameScene cada frame. El render de zeros
|
||||||
// queda com a fallback (mode NONE o slot actiu).
|
// queda com a fallback (mode NONE o slot actiu).
|
||||||
if (!p.active && p.join_prompt != JoinPromptMode::NONE) {
|
if (!p.active && p.join_prompt != JoinPromptMode::NONE) {
|
||||||
|
if (!blinkVisible()) { return; }
|
||||||
const bool CAN_JOIN = (p.join_prompt == JoinPromptMode::CAN_JOIN);
|
const bool CAN_JOIN = (p.join_prompt == JoinPromptMode::CAN_JOIN);
|
||||||
const std::string L1 = Locale::get().text(
|
const std::string L1 = Locale::get().text(
|
||||||
CAN_JOIN ? "hud.join_prompt_can_join_l1" : "hud.join_prompt_wait_l1");
|
CAN_JOIN ? "hud.join_prompt_can_join_l1" : "hud.join_prompt_wait_l1");
|
||||||
|
|||||||
@@ -170,6 +170,10 @@ class Hud {
|
|||||||
// les constants de classe (no toca cap altre membre).
|
// les constants de classe (no toca cap altre membre).
|
||||||
[[nodiscard]] static auto computeOffset(const Block& b) -> SDL_FPoint;
|
[[nodiscard]] static auto computeOffset(const Block& b) -> SDL_FPoint;
|
||||||
|
|
||||||
|
// Estat actual del parpadeig dels prompts inactius. Llegit per
|
||||||
|
// renderIdleSide() per saltar el draw quan toca ocult.
|
||||||
|
[[nodiscard]] auto blinkVisible() const -> bool;
|
||||||
|
|
||||||
EventBus& bus_;
|
EventBus& bus_;
|
||||||
std::vector<EventBus::SubId> subs_;
|
std::vector<EventBus::SubId> subs_;
|
||||||
|
|
||||||
@@ -179,6 +183,10 @@ class Hud {
|
|||||||
std::array<PlayerStats, 2> players_;
|
std::array<PlayerStats, 2> players_;
|
||||||
int pyramid_{1};
|
int pyramid_{1};
|
||||||
int room_{1};
|
int room_{1};
|
||||||
|
// Acumulador continu (ms) per al parpadeig dels prompts inactius.
|
||||||
|
// Es manté entre canvis de mode (CAN_JOIN ↔ WAIT) per evitar salts
|
||||||
|
// visuals i sincronitza els dos slots — vegeu Hud::blinkVisible().
|
||||||
|
int blink_elapsed_ms_{0};
|
||||||
// Vides amb què s'arrenca un slot (`activatePlayer`). Cachejat al
|
// Vides amb què s'arrenca un slot (`activatePlayer`). Cachejat al
|
||||||
// ctor des de `Player::death_config.initial_lives` per no llegir el
|
// ctor des de `Player::death_config.initial_lives` per no llegir el
|
||||||
// singleton estàtic en el camí calent del bus de events.
|
// singleton estàtic en el camí calent del bus de events.
|
||||||
|
|||||||
@@ -246,6 +246,12 @@ namespace Defaults {
|
|||||||
constexpr int HUD_FLASH_MS = 250;
|
constexpr int HUD_FLASH_MS = 250;
|
||||||
constexpr int HUD_SHAKE_MS = 300;
|
constexpr int HUD_SHAKE_MS = 300;
|
||||||
|
|
||||||
|
// Parpadeig dels prompts inactius del HUD ("Polsa start..." /
|
||||||
|
// "Espera..."). Un únic temporitzador global al Hud sincronitza
|
||||||
|
// P1 i P2. cycle<=0 o ratio>=1 → sempre visible (disable trivial).
|
||||||
|
constexpr int HUD_BLINK_CYCLE_MS = 1000;
|
||||||
|
constexpr float HUD_BLINK_VISIBLE_RATIO = 0.8F;
|
||||||
|
|
||||||
// Pausa graduada de la GameScene: el món no para d'un colp, es
|
// Pausa graduada de la GameScene: el món no para d'un colp, es
|
||||||
// frena amb una corba i torna a arrancar amb una altra. Els noms
|
// frena amb una corba i torna a arrancar amb una altra. Els noms
|
||||||
// de corba han de coincidir amb els registrats a
|
// de corba han de coincidir amb els registrats a
|
||||||
|
|||||||
Reference in new issue
Block a user