varies millores en el hud de debug

This commit is contained in:
2026-03-11 23:27:02 +01:00
parent dfbd8a430b
commit cbe6dc9744
4 changed files with 78 additions and 119 deletions

View File

@@ -108,6 +108,10 @@ class Engine {
ScalingMode getCurrentScalingMode() const { return current_scaling_mode_; } ScalingMode getCurrentScalingMode() const { return current_scaling_mode_; }
int getCurrentScreenWidth() const { return current_screen_width_; } int getCurrentScreenWidth() const { return current_screen_width_; }
int getCurrentScreenHeight() const { return current_screen_height_; } int getCurrentScreenHeight() const { return current_screen_height_; }
std::string getCurrentTextureName() const {
if (texture_names_.empty()) return "";
return texture_names_[current_texture_index_];
}
int getBaseScreenWidth() const { return base_screen_width_; } int getBaseScreenWidth() const { return base_screen_width_; }
int getBaseScreenHeight() const { return base_screen_height_; } int getBaseScreenHeight() const { return base_screen_height_; }
int getMaxAutoScenario() const { return max_auto_scenario_; } int getMaxAutoScenario() const { return max_auto_scenario_; }

View File

@@ -313,6 +313,17 @@ void TextRenderer::printAbsolute(int physical_x, int physical_y, const std::stri
printAbsolute(physical_x, physical_y, text.c_str(), color); printAbsolute(physical_x, physical_y, text.c_str(), color);
} }
void TextRenderer::printAbsoluteShadowed(int physical_x, int physical_y, const char* text) {
// Sombra: negro semitransparente desplazado 1px
printAbsolute(physical_x + 1, physical_y + 1, text, {0, 0, 0, 180});
// Texto: blanco opaco
printAbsolute(physical_x, physical_y, text, {255, 255, 255, 255});
}
void TextRenderer::printAbsoluteShadowed(int physical_x, int physical_y, const std::string& text) {
printAbsoluteShadowed(physical_x, physical_y, text.c_str());
}
int TextRenderer::getTextWidth(const char* text) { int TextRenderer::getTextWidth(const char* text) {
if (!isInitialized() || text == nullptr) { if (!isInitialized() || text == nullptr) {
return 0; return 0;

View File

@@ -31,6 +31,10 @@ public:
void printAbsolute(int physical_x, int physical_y, const char* text, SDL_Color color); void printAbsolute(int physical_x, int physical_y, const char* text, SDL_Color color);
void printAbsolute(int physical_x, int physical_y, const std::string& text, SDL_Color color); void printAbsolute(int physical_x, int physical_y, const std::string& text, SDL_Color color);
// Renderiza texto con sombra negra (+1px offset) para máxima legibilidad sobre cualquier fondo
void printAbsoluteShadowed(int physical_x, int physical_y, const char* text);
void printAbsoluteShadowed(int physical_x, int physical_y, const std::string& text);
// Obtiene el ancho de un texto renderizado (en píxeles lógicos para compatibilidad) // Obtiene el ancho de un texto renderizado (en píxeles lógicos para compatibilidad)
int getTextWidth(const char* text); int getTextWidth(const char* text);

View File

@@ -1,6 +1,7 @@
#include "ui_manager.hpp" #include "ui_manager.hpp"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <algorithm>
#include <string> #include <string>
#include "ball.hpp" // for Ball #include "ball.hpp" // for Ball
@@ -84,7 +85,7 @@ void UIManager::initialize(SDL_Renderer* renderer, ThemeManager* theme_manager,
text_renderer_notifier_ = new TextRenderer(); text_renderer_notifier_ = new TextRenderer();
// Inicializar renderers con tamaño dinámico // Inicializar renderers con tamaño dinámico
text_renderer_debug_->init(renderer, "data/fonts/FunnelSans-Regular.ttf", current_font_size_, true); text_renderer_debug_->init(renderer, "data/fonts/FunnelSans-Regular.ttf", std::max(9, current_font_size_ - 2), true);
text_renderer_notifier_->init(renderer, "data/fonts/FunnelSans-Regular.ttf", current_font_size_, true); text_renderer_notifier_->init(renderer, "data/fonts/FunnelSans-Regular.ttf", current_font_size_, true);
// Crear y configurar sistema de notificaciones // Crear y configurar sistema de notificaciones
@@ -109,7 +110,7 @@ void UIManager::update(Uint64 current_time, float delta_time) {
fps_current_ = fps_frame_count_; fps_current_ = fps_frame_count_;
fps_frame_count_ = 0; fps_frame_count_ = 0;
fps_last_time_ = current_time; fps_last_time_ = current_time;
fps_text_ = "fps: " + std::to_string(fps_current_); fps_text_ = "FPS: " + std::to_string(fps_current_);
} }
// Actualizar sistema de notificaciones // Actualizar sistema de notificaciones
@@ -180,7 +181,7 @@ void UIManager::updatePhysicalWindowSize(int width, int height) {
// Reinicializar text renderers con nuevo tamaño // Reinicializar text renderers con nuevo tamaño
if (text_renderer_debug_) { if (text_renderer_debug_) {
text_renderer_debug_->reinitialize(current_font_size_); text_renderer_debug_->reinitialize(std::max(9, current_font_size_ - 2));
} }
if (text_renderer_notifier_) { if (text_renderer_notifier_) {
text_renderer_notifier_->reinitialize(current_font_size_); text_renderer_notifier_->reinitialize(current_font_size_);
@@ -204,42 +205,23 @@ void UIManager::renderDebugHUD(const Engine* engine,
AppMode current_app_mode, AppMode current_app_mode,
const Shape* active_shape, const Shape* active_shape,
float shape_convergence) { float shape_convergence) {
// Obtener altura de línea para espaciado dinámico
int line_height = text_renderer_debug_->getTextHeight(); int line_height = text_renderer_debug_->getTextHeight();
int margin = 8; // Margen constante en píxeles físicos int margin = 8;
// Obtener viewport FÍSICO (píxeles reales, no lógicos)
// CRÍTICO: En F3, SDL_GetRenderViewport() devuelve coordenadas LÓGICAS,
// pero printAbsolute() trabaja en píxeles FÍSICOS. Usar helper para obtener
// viewport en coordenadas físicas.
SDL_Rect physical_viewport = getPhysicalViewport(renderer_); SDL_Rect physical_viewport = getPhysicalViewport(renderer_);
// =========================== // --- Construir strings ---
// COLUMNA LEFT (Sistema)
// ===========================
int left_y = margin;
// AppMode (antes estaba centrado, ahora va a la izquierda)
std::string appmode_text; std::string appmode_text;
SDL_Color appmode_color = {255, 255, 255, 255}; // Blanco por defecto
if (current_app_mode == AppMode::LOGO) { if (current_app_mode == AppMode::LOGO) {
appmode_text = "AppMode: LOGO"; appmode_text = "AppMode: LOGO";
appmode_color = {255, 128, 0, 255}; // Naranja
} else if (current_app_mode == AppMode::DEMO) { } else if (current_app_mode == AppMode::DEMO) {
appmode_text = "AppMode: DEMO"; appmode_text = "AppMode: DEMO";
appmode_color = {255, 165, 0, 255}; // Naranja
} else if (current_app_mode == AppMode::DEMO_LITE) { } else if (current_app_mode == AppMode::DEMO_LITE) {
appmode_text = "AppMode: DEMO LITE"; appmode_text = "AppMode: DEMO LITE";
appmode_color = {255, 200, 0, 255}; // Amarillo-naranja
} else { } else {
appmode_text = "AppMode: SANDBOX"; appmode_text = "AppMode: SANDBOX";
appmode_color = {0, 255, 128, 255}; // Verde claro
} }
text_renderer_debug_->printAbsolute(margin, left_y, appmode_text.c_str(), appmode_color);
left_y += line_height;
// SimulationMode
std::string simmode_text; std::string simmode_text;
if (current_mode == SimulationMode::PHYSICS) { if (current_mode == SimulationMode::PHYSICS) {
simmode_text = "SimMode: PHYSICS"; simmode_text = "SimMode: PHYSICS";
@@ -252,40 +234,36 @@ void UIManager::renderDebugHUD(const Engine* engine,
} else if (current_mode == SimulationMode::BOIDS) { } else if (current_mode == SimulationMode::BOIDS) {
simmode_text = "SimMode: BOIDS"; simmode_text = "SimMode: BOIDS";
} }
text_renderer_debug_->printAbsolute(margin, left_y, simmode_text.c_str(), {0, 255, 255, 255}); // Cian
left_y += line_height;
// Número de pelotas (escenario actual) std::string sprite_name = engine->getCurrentTextureName();
std::transform(sprite_name.begin(), sprite_name.end(), sprite_name.begin(), ::toupper);
std::string sprite_text = "Sprite: " + sprite_name;
size_t ball_count = scene_manager->getBallCount(); size_t ball_count = scene_manager->getBallCount();
std::string balls_text; std::string balls_text;
if (ball_count >= 1000) { if (ball_count >= 1000) {
// Formatear con separador de miles (ejemplo: 5,000 o 50,000)
std::string count_str = std::to_string(ball_count); std::string count_str = std::to_string(ball_count);
std::string formatted; std::string formatted;
int digits = count_str.length(); int digits = static_cast<int>(count_str.length());
for (int i = 0; i < digits; i++) { for (int i = 0; i < digits; i++) {
if (i > 0 && (digits - i) % 3 == 0) { if (i > 0 && (digits - i) % 3 == 0) formatted += ',';
formatted += ',';
}
formatted += count_str[i]; formatted += count_str[i];
} }
balls_text = "Balls: " + formatted; balls_text = "Balls: " + formatted;
} else { } else {
balls_text = "Balls: " + std::to_string(ball_count); balls_text = "Balls: " + std::to_string(ball_count);
} }
text_renderer_debug_->printAbsolute(margin, left_y, balls_text.c_str(), {128, 255, 128, 255}); // Verde claro
left_y += line_height;
// Máximo de pelotas en modos automáticos (resultado del benchmark)
int max_auto_idx = engine->getMaxAutoScenario(); int max_auto_idx = engine->getMaxAutoScenario();
int max_auto_balls = BALL_COUNT_SCENARIOS[max_auto_idx]; int max_auto_balls = BALL_COUNT_SCENARIOS[max_auto_idx];
if (engine->isCustomAutoAvailable() && engine->getCustomScenarioBalls() > max_auto_balls) if (engine->isCustomAutoAvailable() && engine->getCustomScenarioBalls() > max_auto_balls) {
max_auto_balls = engine->getCustomScenarioBalls(); max_auto_balls = engine->getCustomScenarioBalls();
}
std::string max_auto_text; std::string max_auto_text;
if (max_auto_balls >= 1000) { if (max_auto_balls >= 1000) {
std::string count_str = std::to_string(max_auto_balls); std::string count_str = std::to_string(max_auto_balls);
std::string formatted; std::string formatted;
int digits = count_str.length(); int digits = static_cast<int>(count_str.length());
for (int i = 0; i < digits; i++) { for (int i = 0; i < digits; i++) {
if (i > 0 && (digits - i) % 3 == 0) formatted += ','; if (i > 0 && (digits - i) % 3 == 0) formatted += ',';
formatted += count_str[i]; formatted += count_str[i];
@@ -294,14 +272,7 @@ void UIManager::renderDebugHUD(const Engine* engine,
} else { } else {
max_auto_text = "Auto max: " + std::to_string(max_auto_balls); max_auto_text = "Auto max: " + std::to_string(max_auto_balls);
} }
text_renderer_debug_->printAbsolute(margin, left_y, max_auto_text.c_str(), {128, 255, 128, 255}); // Verde claro
left_y += line_height;
// V-Sync
text_renderer_debug_->printAbsolute(margin, left_y, vsync_text_.c_str(), {0, 255, 255, 255}); // Cian
left_y += line_height;
// Modo de escalado (INTEGER/LETTERBOX/STRETCH o WINDOWED si no está en fullscreen)
std::string scaling_text; std::string scaling_text;
if (engine->getFullscreenEnabled() || engine->getRealFullscreenEnabled()) { if (engine->getFullscreenEnabled() || engine->getRealFullscreenEnabled()) {
ScalingMode scaling = engine->getCurrentScalingMode(); ScalingMode scaling = engine->getCurrentScalingMode();
@@ -315,20 +286,10 @@ void UIManager::renderDebugHUD(const Engine* engine,
} else { } else {
scaling_text = "Scaling: WINDOWED"; scaling_text = "Scaling: WINDOWED";
} }
text_renderer_debug_->printAbsolute(margin, left_y, scaling_text.c_str(), {255, 255, 0, 255}); // Amarillo
left_y += line_height;
// Resolución física (píxeles reales de la ventana)
std::string phys_res_text = "Physical: " + std::to_string(physical_window_width_) + "x" + std::to_string(physical_window_height_); std::string phys_res_text = "Physical: " + std::to_string(physical_window_width_) + "x" + std::to_string(physical_window_height_);
text_renderer_debug_->printAbsolute(margin, left_y, phys_res_text.c_str(), {255, 128, 255, 255}); // Magenta claro
left_y += line_height;
// Resolución lógica (resolución interna del renderizador)
std::string logic_res_text = "Logical: " + std::to_string(engine->getCurrentScreenWidth()) + "x" + std::to_string(engine->getCurrentScreenHeight()); std::string logic_res_text = "Logical: " + std::to_string(engine->getCurrentScreenWidth()) + "x" + std::to_string(engine->getCurrentScreenHeight());
text_renderer_debug_->printAbsolute(margin, left_y, logic_res_text.c_str(), {255, 128, 255, 255}); // Magenta claro
left_y += line_height;
// Display refresh rate (obtener de SDL)
std::string refresh_text; std::string refresh_text;
int num_displays = 0; int num_displays = 0;
SDL_DisplayID* displays = SDL_GetDisplays(&num_displays); SDL_DisplayID* displays = SDL_GetDisplays(&num_displays);
@@ -343,83 +304,62 @@ void UIManager::renderDebugHUD(const Engine* engine,
} else { } else {
refresh_text = "Refresh: N/A"; refresh_text = "Refresh: N/A";
} }
text_renderer_debug_->printAbsolute(margin, left_y, refresh_text.c_str(), {255, 255, 128, 255}); // Amarillo claro
left_y += line_height;
// Tema actual (delegado a ThemeManager)
std::string theme_text = std::string("Theme: ") + theme_manager_->getCurrentThemeNameEN(); std::string theme_text = std::string("Theme: ") + theme_manager_->getCurrentThemeNameEN();
text_renderer_debug_->printAbsolute(margin, left_y, theme_text.c_str(), {128, 255, 255, 255}); // Cian claro
left_y += line_height;
// =========================== Uint64 ticks_ms = SDL_GetTicks();
// COLUMNA RIGHT (Primera pelota) Uint64 total_secs = ticks_ms / 1000;
// =========================== int hh = static_cast<int>(total_secs / 3600);
int right_y = margin; int mm = static_cast<int>((total_secs % 3600) / 60);
int ss = static_cast<int>(total_secs % 60);
char elapsed_buf[32];
SDL_snprintf(elapsed_buf, sizeof(elapsed_buf), "Elapsed: %02d:%02d:%02d", hh, mm, ss);
std::string elapsed_text(elapsed_buf);
// FPS counter (esquina superior derecha) // --- Construir vector de líneas en orden ---
int fps_text_width = text_renderer_debug_->getTextWidthPhysical(fps_text_.c_str()); std::vector<std::string> lines;
int fps_x = physical_viewport.w - fps_text_width - margin; lines.push_back(fps_text_);
text_renderer_debug_->printAbsolute(fps_x, right_y, fps_text_.c_str(), {255, 255, 0, 255}); // Amarillo lines.push_back(appmode_text);
right_y += line_height; lines.push_back(simmode_text);
lines.push_back(sprite_text);
lines.push_back(balls_text);
lines.push_back(max_auto_text);
lines.push_back(vsync_text_);
lines.push_back(scaling_text);
lines.push_back(phys_res_text);
lines.push_back(logic_res_text);
lines.push_back(refresh_text);
lines.push_back(theme_text);
lines.push_back(elapsed_text);
// Info de la primera pelota (si existe)
const Ball* first_ball = scene_manager->getFirstBall(); const Ball* first_ball = scene_manager->getFirstBall();
if (first_ball != nullptr) { if (first_ball != nullptr) {
// Posición X, Y lines.push_back("VelX: " + std::to_string(static_cast<int>(first_ball->getVelocityX())));
lines.push_back("VelY: " + std::to_string(static_cast<int>(first_ball->getVelocityY())));
SDL_FRect pos = first_ball->getPosition(); SDL_FRect pos = first_ball->getPosition();
std::string pos_text = "Pos: (" + std::to_string(static_cast<int>(pos.x)) + ", " + std::to_string(static_cast<int>(pos.y)) + ")"; lines.push_back("Pos: (" + std::to_string(static_cast<int>(pos.x)) + ", " + std::to_string(static_cast<int>(pos.y)) + ")");
int pos_width = text_renderer_debug_->getTextWidthPhysical(pos_text.c_str()); lines.push_back("Gravity: " + std::to_string(static_cast<int>(first_ball->getGravityForce())));
text_renderer_debug_->printAbsolute(physical_viewport.w - pos_width - margin, right_y, pos_text.c_str(), {255, 128, 128, 255}); // Rojo claro lines.push_back(first_ball->isOnSurface() ? "Surface: YES" : "Surface: NO");
right_y += line_height; lines.push_back("Loss: " + std::to_string(first_ball->getLossCoefficient()).substr(0, 4));
lines.push_back("Dir: " + gravityDirectionToString(static_cast<int>(scene_manager->getCurrentGravity())));
// Velocidad X
int vx_int = static_cast<int>(first_ball->getVelocityX());
std::string vx_text = "VelX: " + std::to_string(vx_int);
int vx_width = text_renderer_debug_->getTextWidthPhysical(vx_text.c_str());
text_renderer_debug_->printAbsolute(physical_viewport.w - vx_width - margin, right_y, vx_text.c_str(), {128, 255, 128, 255}); // Verde claro
right_y += line_height;
// Velocidad Y
int vy_int = static_cast<int>(first_ball->getVelocityY());
std::string vy_text = "VelY: " + std::to_string(vy_int);
int vy_width = text_renderer_debug_->getTextWidthPhysical(vy_text.c_str());
text_renderer_debug_->printAbsolute(physical_viewport.w - vy_width - margin, right_y, vy_text.c_str(), {128, 255, 128, 255}); // Verde claro
right_y += line_height;
// Fuerza de gravedad
int grav_int = static_cast<int>(first_ball->getGravityForce());
std::string grav_text = "Gravity: " + std::to_string(grav_int);
int grav_width = text_renderer_debug_->getTextWidthPhysical(grav_text.c_str());
text_renderer_debug_->printAbsolute(physical_viewport.w - grav_width - margin, right_y, grav_text.c_str(), {255, 255, 128, 255}); // Amarillo claro
right_y += line_height;
// Estado superficie
std::string surface_text = first_ball->isOnSurface() ? "Surface: YES" : "Surface: NO";
int surface_width = text_renderer_debug_->getTextWidthPhysical(surface_text.c_str());
text_renderer_debug_->printAbsolute(physical_viewport.w - surface_width - margin, right_y, surface_text.c_str(), {255, 200, 128, 255}); // Naranja claro
right_y += line_height;
// Coeficiente de rebote (loss)
float loss_val = first_ball->getLossCoefficient();
std::string loss_text = "Loss: " + std::to_string(loss_val).substr(0, 4);
int loss_width = text_renderer_debug_->getTextWidthPhysical(loss_text.c_str());
text_renderer_debug_->printAbsolute(physical_viewport.w - loss_width - margin, right_y, loss_text.c_str(), {255, 128, 255, 255}); // Magenta
right_y += line_height;
// Dirección de gravedad
std::string gravity_dir_text = "Dir: " + gravityDirectionToString(static_cast<int>(scene_manager->getCurrentGravity()));
int dir_width = text_renderer_debug_->getTextWidthPhysical(gravity_dir_text.c_str());
text_renderer_debug_->printAbsolute(physical_viewport.w - dir_width - margin, right_y, gravity_dir_text.c_str(), {128, 255, 255, 255}); // Cian claro
right_y += line_height;
} }
// Convergencia en modo LOGO (solo cuando está activo) - Parte inferior derecha
if (current_app_mode == AppMode::LOGO && current_mode == SimulationMode::SHAPE) { if (current_app_mode == AppMode::LOGO && current_mode == SimulationMode::SHAPE) {
int convergence_percent = static_cast<int>(shape_convergence * 100.0f); int convergence_percent = static_cast<int>(shape_convergence * 100.0f);
std::string convergence_text = "Convergence: " + std::to_string(convergence_percent) + "%"; lines.push_back("Convergence: " + std::to_string(convergence_percent) + "%");
int conv_width = text_renderer_debug_->getTextWidthPhysical(convergence_text.c_str()); }
text_renderer_debug_->printAbsolute(physical_viewport.w - conv_width - margin, right_y, convergence_text.c_str(), {255, 128, 0, 255}); // Naranja
right_y += line_height; // --- Render con desbordamiento a segunda columna ---
int max_lines = (physical_viewport.h - 2 * margin) / line_height;
if (max_lines < 1) max_lines = 1;
int col_width = physical_viewport.w / 2;
for (int i = 0; i < static_cast<int>(lines.size()); i++) {
int col = i / max_lines;
int row = i % max_lines;
int x = margin + col * col_width;
int y = margin + row * line_height;
text_renderer_debug_->printAbsoluteShadowed(x, y, lines[i].c_str());
} }
} }