animacions a renderinfo

This commit is contained in:
2026-04-05 01:03:48 +02:00
parent 91f88ded09
commit 4238ae1bc4
9 changed files with 181 additions and 32 deletions

View File

@@ -51,6 +51,7 @@ set(APP_SOURCES
source/game/sprite.cpp source/game/sprite.cpp
# Utils # Utils
source/utils/easing.cpp
source/utils/utils.cpp source/utils/utils.cpp
# Main # Main

View File

@@ -10,6 +10,7 @@
#include "core/rendering/screen.hpp" #include "core/rendering/screen.hpp"
#include "core/rendering/text.hpp" #include "core/rendering/text.hpp"
#include "game/options.hpp" #include "game/options.hpp"
#include "utils/easing.hpp"
namespace Menu { namespace Menu {
@@ -121,6 +122,11 @@ namespace Menu {
} }
return std::string("OFF"); }, [](int dir) { Overlay::cycleRenderInfo(dir); }, nullptr}); return std::string("OFF"); }, [](int dir) { Overlay::cycleRenderInfo(dir); }, nullptr});
p.items.push_back({"HORA", ItemKind::Toggle,
[] { return onOff(Options::render_info.show_time); },
[](int) { Options::render_info.show_time = !Options::render_info.show_time; },
nullptr});
return p; return p;
} }
@@ -340,9 +346,7 @@ namespace Menu {
const Page& page = stack_.back(); const Page& page = stack_.back();
const int target_h = boxHeight(page); const int target_h = boxHeight(page);
// Ease-out quadratic: f(t) = 1 - (1-t)^2 float eased = Easing::outQuad(open_anim_);
float t = open_anim_;
float eased = 1.0F - (1.0F - t) * (1.0F - t);
// Caixa creix verticalment des del centre // Caixa creix verticalment des del centre
int box_h = static_cast<int>(target_h * eased); int box_h = static_cast<int>(target_h * eased);

View File

@@ -9,6 +9,7 @@
#include "core/rendering/text.hpp" #include "core/rendering/text.hpp"
#include "core/system/director.hpp" #include "core/system/director.hpp"
#include "game/options.hpp" #include "game/options.hpp"
#include "utils/easing.hpp"
namespace Overlay { namespace Overlay {
@@ -50,7 +51,19 @@ namespace Overlay {
static Uint32 last_ticks_ = 0; static Uint32 last_ticks_ = 0;
// --- Render info --- // --- Render info ---
static std::string render_info_text_; static Options::RenderInfoPosition info_visible_pos_ = Options::RenderInfoPosition::OFF;
static float info_anim_ = 0.0F; // 0 = fora de pantalla, 1 = posició final
static constexpr float INFO_SLIDE_SPEED = 5.0F;
// Segments del render info — cadascú amb la seva pròpia visibilitat animada
static constexpr int INFO_SEGMENT_COUNT = 4;
static constexpr float SEG_SPEED = 6.0F; // ~165 ms per aparèixer/desaparèixer
struct InfoSegment {
std::string text;
float anim{0.0F};
bool visible{false};
};
static InfoSegment info_segments_[INFO_SEGMENT_COUNT];
// --- Doble ESC per a eixir --- // --- Doble ESC per a eixir ---
static bool esc_waiting_ = false; static bool esc_waiting_ = false;
@@ -129,17 +142,79 @@ namespace Overlay {
font_->draw(pixel_data, box_x + NOTIF_PADDING_H, box_y + NOTIF_PADDING_V, notif.message.c_str(), NOTIF_TEXT_COLOR); font_->draw(pixel_data, box_x + NOTIF_PADDING_H, box_y + NOTIF_PADDING_V, notif.message.c_str(), NOTIF_TEXT_COLOR);
} }
// Render info (FPS, driver, shader) — centrat, posició configurable // Render info (FPS, driver, shader) — animat amb slide vertical
if (Options::render_info.position != Options::RenderInfoPosition::OFF && !render_info_text_.empty()) { // State machine: visible_pos s'actualitza cap a desired quan anim arriba a 0
int info_w = font_->width(render_info_text_.c_str()); {
int info_x = (SCREEN_W - info_w) / 2; const auto desired = Options::render_info.position;
int info_y = (Options::render_info.position == Options::RenderInfoPosition::TOP) if (desired == info_visible_pos_) {
? 1 // Mateix lloc: entra fins a 1
: SCREEN_H - font_->charHeight() - 1; if (info_anim_ < 1.0F) {
// Ombra (1px desplaçat) info_anim_ += INFO_SLIDE_SPEED * dt;
font_->draw(pixel_data, info_x + 1, info_y + 1, render_info_text_.c_str(), Options::render_info.shadow_color); if (info_anim_ > 1.0F) info_anim_ = 1.0F;
// Text }
font_->draw(pixel_data, info_x, info_y, render_info_text_.c_str(), Options::render_info.text_color); } else {
// Canvi: si visible_pos està OFF, commuta directament
if (info_visible_pos_ == Options::RenderInfoPosition::OFF) {
info_visible_pos_ = desired;
info_anim_ = 0.0F;
} else {
// Ix del lloc actual
info_anim_ -= INFO_SLIDE_SPEED * dt;
if (info_anim_ <= 0.0F) {
info_anim_ = 0.0F;
info_visible_pos_ = desired;
}
}
}
// Actualitza animacions individuals dels segments
for (auto& seg : info_segments_) {
float target = seg.visible ? 1.0F : 0.0F;
if (seg.anim < target) {
seg.anim += SEG_SPEED * dt;
if (seg.anim > target) seg.anim = target;
} else if (seg.anim > target) {
seg.anim -= SEG_SPEED * dt;
if (seg.anim < target) seg.anim = target;
}
}
// Render si hi ha alguna cosa visible
if (info_visible_pos_ != Options::RenderInfoPosition::OFF && info_anim_ > 0.0F) {
// Calcula amplada total interpolant cada segment per la seva anim
float total_w = 0.0F;
for (auto& seg : info_segments_) {
if (seg.anim > 0.0F && !seg.text.empty()) {
total_w += font_->width(seg.text.c_str()) * Easing::outQuad(seg.anim);
}
}
if (total_w > 0.0F) {
// Slide vertical (ease-out quadratic) — igual que abans
float eased_y = Easing::outQuad(info_anim_);
int ch = font_->charHeight();
int final_y;
int start_y;
if (info_visible_pos_ == Options::RenderInfoPosition::TOP) {
final_y = 1;
start_y = -ch - 1;
} else {
final_y = SCREEN_H - ch - 1;
start_y = SCREEN_H;
}
int info_y = start_y + static_cast<int>((final_y - start_y) * eased_y);
// Dibuixa cada segment en la seva posició x acumulada
float cur_x = (SCREEN_W - total_w) / 2.0F;
for (auto& seg : info_segments_) {
if (seg.anim > 0.01F && !seg.text.empty()) {
int xi = static_cast<int>(cur_x);
font_->draw(pixel_data, xi + 1, info_y + 1, seg.text.c_str(), Options::render_info.shadow_color);
font_->draw(pixel_data, xi, info_y, seg.text.c_str(), Options::render_info.text_color);
cur_x += font_->width(seg.text.c_str()) * Easing::outQuad(seg.anim);
}
}
}
}
} }
// Elimina les acabades // Elimina les acabades
@@ -192,8 +267,16 @@ namespace Overlay {
Options::render_info.position = static_cast<Options::RenderInfoPosition>(pos); Options::render_info.position = static_cast<Options::RenderInfoPosition>(pos);
} }
void setRenderInfoText(const char* text) { void setRenderInfoSegments(const char* s0, const char* s1, const char* s2, const char* s3) {
render_info_text_ = text; const char* segs[INFO_SEGMENT_COUNT] = {s0, s1, s2, s3};
for (int i = 0; i < INFO_SEGMENT_COUNT; i++) {
if (segs[i] != nullptr && *segs[i] != '\0') {
info_segments_[i].text = segs[i];
info_segments_[i].visible = true;
} else {
info_segments_[i].visible = false;
}
}
} }
auto isEscConsumed() -> bool { auto isEscConsumed() -> bool {

View File

@@ -15,7 +15,9 @@ namespace Overlay {
// Activa/desactiva la info de renderitzat (FPS, driver, shader, preset) // Activa/desactiva la info de renderitzat (FPS, driver, shader, preset)
void toggleRenderInfo(); void toggleRenderInfo();
void cycleRenderInfo(int dir); // dir=+1 avant, -1 endarrere void cycleRenderInfo(int dir); // dir=+1 avant, -1 endarrere
void setRenderInfoText(const char* text); // Configura els segments del render info. Cada segment (nullptr o string buit
// per amagar) apareix/desapareix amb animació; el conjunt es centra dinàmicament.
void setRenderInfoSegments(const char* s0, const char* s1, const char* s2, const char* s3);
// Gestió d'eixida amb doble ESC // Gestió d'eixida amb doble ESC
// Retorna true si l'ESC ha sigut consumit (no s'ha de passar al joc) // Retorna true si l'ESC ha sigut consumit (no s'ha de passar al joc)

View File

@@ -329,25 +329,34 @@ auto Screen::getActiveShaderName() const -> const char* {
void Screen::updateRenderInfo() { void Screen::updateRenderInfo() {
static const Uint32 start_ticks = SDL_GetTicks(); static const Uint32 start_ticks = SDL_GetTicks();
std::string driver = gpu_driver_.empty() ? "sdl" : toLower(gpu_driver_); std::string driver = gpu_driver_.empty() ? "sdl" : toLower(gpu_driver_);
std::string info = std::to_string(fps_.last_value) + " fps - " + driver;
// Segment 0: FPS + driver (sempre visible)
std::string fps_driver = std::to_string(fps_.last_value) + " fps - " + driver;
// Segment 1: shader + preset (només si shaders actius)
std::string shader_seg;
if (Options::video.shader_enabled) { if (Options::video.shader_enabled) {
std::string shader_name = toLower(getActiveShaderName()); shader_seg = " - " + toLower(getActiveShaderName()) + " " + toLower(getCurrentPresetName());
std::string preset_name = toLower(getCurrentPresetName());
info += " - " + shader_name + " " + preset_name;
if (Options::video.supersampling) info += " (ss)";
} }
// Temps de joc: m:ss.cc // Segment 2: supersampling indicator
Uint32 elapsed = SDL_GetTicks() - start_ticks; const char* ss_seg = (Options::video.shader_enabled && Options::video.supersampling) ? " (ss)" : nullptr;
int minutes = elapsed / 60000;
int seconds = (elapsed / 1000) % 60;
int centis = (elapsed / 10) % 100;
char time_buf[32];
snprintf(time_buf, sizeof(time_buf), " - %d:%02d.%02d", minutes, seconds, centis);
info += time_buf;
Overlay::setRenderInfoText(info.c_str()); // Segment 3: hora (només si show_time)
char time_buf[32] = {0};
if (Options::render_info.show_time) {
Uint32 elapsed = SDL_GetTicks() - start_ticks;
int minutes = elapsed / 60000;
int seconds = (elapsed / 1000) % 60;
int centis = (elapsed / 10) % 100;
snprintf(time_buf, sizeof(time_buf), " - %d:%02d.%02d", minutes, seconds, centis);
}
Overlay::setRenderInfoSegments(
fps_driver.c_str(),
shader_seg.empty() ? nullptr : shader_seg.c_str(),
ss_seg,
time_buf[0] ? time_buf : nullptr);
} }
void Screen::adjustWindowSize() { void Screen::adjustWindowSize() {

View File

@@ -94,6 +94,8 @@ namespace Options {
else else
render_info.position = RenderInfoPosition::OFF; render_info.position = RenderInfoPosition::OFF;
} }
if (node.contains("show_time"))
render_info.show_time = node["show_time"].get_value<bool>();
if (node.contains("text_color")) if (node.contains("text_color"))
render_info.text_color = static_cast<Uint32>(node["text_color"].get_value<uint64_t>()); render_info.text_color = static_cast<Uint32>(node["text_color"].get_value<uint64_t>());
if (node.contains("shadow_color")) if (node.contains("shadow_color"))
@@ -238,6 +240,7 @@ namespace Options {
pos = "bottom"; pos = "bottom";
file << " position: " << pos << " # off/top/bottom\n"; file << " position: " << pos << " # off/top/bottom\n";
} }
file << " show_time: " << (render_info.show_time ? "true" : "false") << "\n";
file << " text_color: " << render_info.text_color << "\n"; file << " text_color: " << render_info.text_color << "\n";
file << " shadow_color: " << render_info.shadow_color << "\n"; file << " shadow_color: " << render_info.shadow_color << "\n";
file << "\n"; file << "\n";

View File

@@ -56,6 +56,7 @@ namespace Options {
// Opcions del render info // Opcions del render info
struct RenderInfo { struct RenderInfo {
RenderInfoPosition position{RenderInfoPosition::OFF}; RenderInfoPosition position{RenderInfoPosition::OFF};
bool show_time{true};
Uint32 text_color{0xFF00D7FF}; // Groc daurat (ABGR) Uint32 text_color{0xFF00D7FF}; // Groc daurat (ABGR)
Uint32 shadow_color{0xFF005A6B}; // Ombra daurada fosca (ABGR) Uint32 shadow_color{0xFF005A6B}; // Ombra daurada fosca (ABGR)
}; };

24
source/utils/easing.cpp Normal file
View File

@@ -0,0 +1,24 @@
#include "utils/easing.hpp"
namespace Easing {
auto linear(float t) -> float { return t; }
auto outQuad(float t) -> float { return 1.0F - (1.0F - t) * (1.0F - t); }
auto inQuad(float t) -> float { return t * t; }
auto inOutQuad(float t) -> float {
return t < 0.5F ? 2.0F * t * t : 1.0F - (-2.0F * t + 2.0F) * (-2.0F * t + 2.0F) / 2.0F;
}
auto outCubic(float t) -> float {
const float inv = 1.0F - t;
return 1.0F - inv * inv * inv;
}
auto inCubic(float t) -> float { return t * t * t; }
auto lerp(float a, float b, float t) -> float { return a + (b - a) * t; }
auto lerpInt(int a, int b, float t) -> int {
return a + static_cast<int>((b - a) * t);
}
} // namespace Easing

22
source/utils/easing.hpp Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
// Funcions de suavitzat per animacions. Totes accepten t en el rang [0, 1]
// i retornen un valor en [0, 1] (amb overshoot possible en algunes variants).
namespace Easing {
auto linear(float t) -> float;
// Quadratic
auto outQuad(float t) -> float;
auto inQuad(float t) -> float;
auto inOutQuad(float t) -> float;
// Cubic
auto outCubic(float t) -> float;
auto inCubic(float t) -> float;
// Interpolacions (aplicar després d'un easing al paràmetre t)
auto lerp(float a, float b, float t) -> float;
auto lerpInt(int a, int b, float t) -> int;
} // namespace Easing