Fix: HUD debug usa coordenadas físicas absolutas (como notificaciones)

Migra el sistema de renderizado de HUD debug desde printPhysical()
(coordenadas lógicas escaladas) a printAbsolute() (píxeles físicos absolutos).

## Cambios

**engine.cpp (líneas 830-951):**
- Eliminadas líneas de cálculo de factores de escala (text_scale_x/y)
- Todas las coordenadas ahora en píxeles físicos absolutos
- FPS: `physical_window_width_ - text_width - margin` (esquina derecha física)
- 10 llamadas printPhysical() → printAbsolute() con SDL_Color
- 4 llamadas getTextWidth() → getTextWidthPhysical()

## Resultado

 HUD de tamaño fijo independiente de resolución lógica
 FPS siempre pegado a esquina derecha física
 Espaciado constante entre líneas
 Funciona en modo ventana y F4 (stretch fullscreen)
⚠️  PENDIENTE: Ajustar offset para modo F3 con letterbox

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-10 09:39:17 +02:00
parent aa57ac7012
commit d2f170d313

View File

@@ -827,10 +827,6 @@ void Engine::render() {
SDL_RenderGeometry(renderer_, texture_->getSDLTexture(), batch_vertices_.data(), static_cast<int>(batch_vertices_.size()), batch_indices_.data(), static_cast<int>(batch_indices_.size())); SDL_RenderGeometry(renderer_, texture_->getSDLTexture(), batch_vertices_.data(), static_cast<int>(batch_vertices_.size()), batch_indices_.data(), static_cast<int>(batch_indices_.size()));
} }
// Calcular factores de escala lógica → física para texto absoluto
float text_scale_x = static_cast<float>(physical_window_width_) / static_cast<float>(current_screen_width_);
float text_scale_y = static_cast<float>(physical_window_height_) / static_cast<float>(current_screen_height_);
// SISTEMA DE TEXTO ANTIGUO DESHABILITADO // SISTEMA DE TEXTO ANTIGUO DESHABILITADO
// Reemplazado completamente por el sistema de notificaciones (Notifier) // Reemplazado completamente por el sistema de notificaciones (Notifier)
// El doble renderizado causaba que aparecieran textos duplicados detrás de las notificaciones // El doble renderizado causaba que aparecieran textos duplicados detrás de las notificaciones
@@ -865,16 +861,16 @@ void Engine::render() {
if (show_debug_) { if (show_debug_) {
// Obtener altura de línea para espaciado dinámico (usando fuente debug) // Obtener altura de línea para espaciado dinámico (usando fuente debug)
int line_height = text_renderer_debug_.getTextHeight(); int line_height = text_renderer_debug_.getTextHeight();
int margin = 8; // Margen constante int margin = 8; // Margen constante en píxeles físicos
int current_y = margin; // Y inicial int current_y = margin; // Y inicial en píxeles físicos
// Mostrar contador de FPS en esquina superior derecha // Mostrar contador de FPS en esquina superior derecha
int fps_text_width = text_renderer_debug_.getTextWidth(fps_text_.c_str()); int fps_text_width = text_renderer_debug_.getTextWidthPhysical(fps_text_.c_str());
int fps_x = current_screen_width_ - fps_text_width - margin; int fps_x = physical_window_width_ - fps_text_width - margin;
text_renderer_debug_.printPhysical(fps_x, current_y, fps_text_.c_str(), 255, 255, 0, text_scale_x, text_scale_y); // Amarillo text_renderer_debug_.printAbsolute(fps_x, current_y, fps_text_.c_str(), {255, 255, 0, 255}); // Amarillo
// Mostrar estado V-Sync en esquina superior izquierda // Mostrar estado V-Sync en esquina superior izquierda
text_renderer_debug_.printPhysical(margin, current_y, vsync_text_.c_str(), 0, 255, 255, text_scale_x, text_scale_y); // Cian text_renderer_debug_.printAbsolute(margin, current_y, vsync_text_.c_str(), {0, 255, 255, 255}); // Cian
current_y += line_height; current_y += line_height;
// Debug: Mostrar valores de la primera pelota (si existe) // Debug: Mostrar valores de la primera pelota (si existe)
@@ -882,35 +878,35 @@ void Engine::render() {
// Línea 1: Gravedad // Línea 1: Gravedad
int grav_int = static_cast<int>(balls_[0]->getGravityForce()); int grav_int = static_cast<int>(balls_[0]->getGravityForce());
std::string grav_text = "Gravedad: " + std::to_string(grav_int); std::string grav_text = "Gravedad: " + std::to_string(grav_int);
text_renderer_debug_.printPhysical(margin, current_y, grav_text.c_str(), 255, 0, 255, text_scale_x, text_scale_y); // Magenta text_renderer_debug_.printAbsolute(margin, current_y, grav_text.c_str(), {255, 0, 255, 255}); // Magenta
current_y += line_height; current_y += line_height;
// Línea 2: Velocidad Y // Línea 2: Velocidad Y
int vy_int = static_cast<int>(balls_[0]->getVelocityY()); int vy_int = static_cast<int>(balls_[0]->getVelocityY());
std::string vy_text = "Velocidad Y: " + std::to_string(vy_int); std::string vy_text = "Velocidad Y: " + std::to_string(vy_int);
text_renderer_debug_.printPhysical(margin, current_y, vy_text.c_str(), 255, 0, 255, text_scale_x, text_scale_y); // Magenta text_renderer_debug_.printAbsolute(margin, current_y, vy_text.c_str(), {255, 0, 255, 255}); // Magenta
current_y += line_height; current_y += line_height;
// Línea 3: Estado superficie // Línea 3: Estado superficie
std::string surface_text = balls_[0]->isOnSurface() ? "Superficie: Sí" : "Superficie: No"; std::string surface_text = balls_[0]->isOnSurface() ? "Superficie: Sí" : "Superficie: No";
text_renderer_debug_.printPhysical(margin, current_y, surface_text.c_str(), 255, 0, 255, text_scale_x, text_scale_y); // Magenta text_renderer_debug_.printAbsolute(margin, current_y, surface_text.c_str(), {255, 0, 255, 255}); // Magenta
current_y += line_height; current_y += line_height;
// Línea 4: Coeficiente de rebote (loss) // Línea 4: Coeficiente de rebote (loss)
float loss_val = balls_[0]->getLossCoefficient(); float loss_val = balls_[0]->getLossCoefficient();
std::string loss_text = "Rebote: " + std::to_string(loss_val).substr(0, 4); std::string loss_text = "Rebote: " + std::to_string(loss_val).substr(0, 4);
text_renderer_debug_.printPhysical(margin, current_y, loss_text.c_str(), 255, 0, 255, text_scale_x, text_scale_y); // Magenta text_renderer_debug_.printAbsolute(margin, current_y, loss_text.c_str(), {255, 0, 255, 255}); // Magenta
current_y += line_height; current_y += line_height;
// Línea 5: Dirección de gravedad // Línea 5: Dirección de gravedad
std::string gravity_dir_text = "Dirección: " + gravityDirectionToString(current_gravity_); std::string gravity_dir_text = "Dirección: " + gravityDirectionToString(current_gravity_);
text_renderer_debug_.printPhysical(margin, current_y, gravity_dir_text.c_str(), 255, 255, 0, text_scale_x, text_scale_y); // Amarillo text_renderer_debug_.printAbsolute(margin, current_y, gravity_dir_text.c_str(), {255, 255, 0, 255}); // Amarillo
current_y += line_height; current_y += line_height;
} }
// Debug: Mostrar tema actual (delegado a ThemeManager) // Debug: Mostrar tema actual (delegado a ThemeManager)
std::string theme_text = std::string("Tema: ") + theme_manager_->getCurrentThemeNameEN(); std::string theme_text = std::string("Tema: ") + theme_manager_->getCurrentThemeNameEN();
text_renderer_debug_.printPhysical(margin, current_y, theme_text.c_str(), 255, 255, 128, text_scale_x, text_scale_y); // Amarillo claro text_renderer_debug_.printAbsolute(margin, current_y, theme_text.c_str(), {255, 255, 128, 255}); // Amarillo claro
current_y += line_height; current_y += line_height;
// Debug: Mostrar modo de simulación actual // Debug: Mostrar modo de simulación actual
@@ -922,14 +918,14 @@ void Engine::render() {
} else { } else {
mode_text = "Modo: Forma"; mode_text = "Modo: Forma";
} }
text_renderer_debug_.printPhysical(margin, current_y, mode_text.c_str(), 0, 255, 128, text_scale_x, text_scale_y); // Verde claro text_renderer_debug_.printAbsolute(margin, current_y, mode_text.c_str(), {0, 255, 128, 255}); // Verde claro
current_y += line_height; current_y += line_height;
// Debug: Mostrar convergencia en modo LOGO (solo cuando está activo) // Debug: Mostrar convergencia en modo LOGO (solo cuando está activo)
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 = "Convergencia: " + std::to_string(convergence_percent) + "%"; std::string convergence_text = "Convergencia: " + std::to_string(convergence_percent) + "%";
text_renderer_debug_.printPhysical(margin, current_y, convergence_text.c_str(), 255, 128, 0, text_scale_x, text_scale_y); // Naranja text_renderer_debug_.printAbsolute(margin, current_y, convergence_text.c_str(), {255, 128, 0, 255}); // Naranja
current_y += line_height; current_y += line_height;
} }
@@ -938,19 +934,19 @@ void Engine::render() {
int fixed_y = margin + (line_height * 2); // Tercera fila fija int fixed_y = margin + (line_height * 2); // Tercera fila fija
if (current_app_mode_ == AppMode::LOGO) { if (current_app_mode_ == AppMode::LOGO) {
const char* logo_text = "Modo Logo"; const char* logo_text = "Modo Logo";
int logo_text_width = text_renderer_debug_.getTextWidth(logo_text); int logo_text_width = text_renderer_debug_.getTextWidthPhysical(logo_text);
int logo_x = (current_screen_width_ - logo_text_width) / 2; int logo_x = (physical_window_width_ - logo_text_width) / 2;
text_renderer_debug_.printPhysical(logo_x, fixed_y, logo_text, 255, 128, 0, text_scale_x, text_scale_y); // Naranja text_renderer_debug_.printAbsolute(logo_x, fixed_y, logo_text, {255, 128, 0, 255}); // Naranja
} else if (current_app_mode_ == AppMode::DEMO) { } else if (current_app_mode_ == AppMode::DEMO) {
const char* demo_text = "Modo Demo"; const char* demo_text = "Modo Demo";
int demo_text_width = text_renderer_debug_.getTextWidth(demo_text); int demo_text_width = text_renderer_debug_.getTextWidthPhysical(demo_text);
int demo_x = (current_screen_width_ - demo_text_width) / 2; int demo_x = (physical_window_width_ - demo_text_width) / 2;
text_renderer_debug_.printPhysical(demo_x, fixed_y, demo_text, 255, 165, 0, text_scale_x, text_scale_y); // Naranja text_renderer_debug_.printAbsolute(demo_x, fixed_y, demo_text, {255, 165, 0, 255}); // Naranja
} else if (current_app_mode_ == AppMode::DEMO_LITE) { } else if (current_app_mode_ == AppMode::DEMO_LITE) {
const char* lite_text = "Modo Demo Lite"; const char* lite_text = "Modo Demo Lite";
int lite_text_width = text_renderer_debug_.getTextWidth(lite_text); int lite_text_width = text_renderer_debug_.getTextWidthPhysical(lite_text);
int lite_x = (current_screen_width_ - lite_text_width) / 2; int lite_x = (physical_window_width_ - lite_text_width) / 2;
text_renderer_debug_.printPhysical(lite_x, fixed_y, lite_text, 255, 200, 0, text_scale_x, text_scale_y); // Amarillo-naranja text_renderer_debug_.printAbsolute(lite_x, fixed_y, lite_text, {255, 200, 0, 255}); // Amarillo-naranja
} }
} }