fix: Debug HUD usa viewport físico en F3 (coordenadas reales)

Problema:
- En modo F3 (letterbox/integer scale), el debug HUD se pintaba
  fuera del área de juego (en las barras negras laterales)
- SDL_GetRenderViewport() devuelve coordenadas LÓGICAS cuando hay
  presentación lógica activa
- printAbsolute() trabaja en píxeles FÍSICOS
- Mismatch de coordenadas causaba alineación derecha incorrecta

Solución:
- Nuevo helper getPhysicalViewport() que:
  1. Guarda estado de presentación lógica
  2. Deshabilita presentación lógica temporalmente
  3. Obtiene viewport en coordenadas físicas
  4. Restaura presentación lógica
  5. Retorna viewport físico

- UIManager::renderDebugHUD() ahora usa physical_viewport.w
  para cálculo de alineación derecha (9 referencias actualizadas)

Resultado:
- Debug HUD alineado correctamente en F3 letterbox
- Debug HUD alineado correctamente en F4 integer scale
- Modo ventana sigue funcionando correctamente

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-17 08:08:04 +02:00
parent 399650f8da
commit c4075f68db

View File

@@ -13,6 +13,31 @@
#include "notifier.h" // for Notifier
#include "help_overlay.h" // for HelpOverlay
// ============================================================================
// HELPER: Obtener viewport en coordenadas físicas (no lógicas)
// ============================================================================
// SDL_GetRenderViewport() devuelve coordenadas LÓGICAS cuando hay presentación
// lógica activa. Para obtener coordenadas FÍSICAS, necesitamos deshabilitar
// temporalmente la presentación lógica.
static SDL_Rect getPhysicalViewport(SDL_Renderer* renderer) {
// Guardar estado actual de presentación lógica
int logical_w = 0, logical_h = 0;
SDL_RendererLogicalPresentation presentation_mode;
SDL_GetRenderLogicalPresentation(renderer, &logical_w, &logical_h, &presentation_mode);
// Deshabilitar presentación lógica temporalmente
SDL_SetRenderLogicalPresentation(renderer, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);
// Obtener viewport en coordenadas físicas (píxeles reales)
SDL_Rect physical_viewport;
SDL_GetRenderViewport(renderer, &physical_viewport);
// Restaurar presentación lógica
SDL_SetRenderLogicalPresentation(renderer, logical_w, logical_h, presentation_mode);
return physical_viewport;
}
UIManager::UIManager()
: text_renderer_(nullptr)
, text_renderer_debug_(nullptr)
@@ -205,10 +230,11 @@ void UIManager::renderDebugHUD(const Engine* engine,
int line_height = text_renderer_debug_->getTextHeight();
int margin = 8; // Margen constante en píxeles físicos
// Obtener viewport actual (en modo letterbox F3 tiene dimensiones más pequeñas)
// CRÍTICO: Usar dimensiones del VIEWPORT para alineación, no de la ventana física
SDL_Rect viewport;
SDL_GetRenderViewport(renderer_, &viewport);
// 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_);
// ===========================
// COLUMNA LEFT (Sistema)
@@ -312,7 +338,7 @@ void UIManager::renderDebugHUD(const Engine* engine,
// FPS counter (esquina superior derecha)
int fps_text_width = text_renderer_debug_->getTextWidthPhysical(fps_text_.c_str());
int fps_x = viewport.w - fps_text_width - margin;
int fps_x = physical_viewport.w - fps_text_width - margin;
text_renderer_debug_->printAbsolute(fps_x, right_y, fps_text_.c_str(), {255, 255, 0, 255}); // Amarillo
right_y += line_height;
@@ -323,47 +349,47 @@ void UIManager::renderDebugHUD(const Engine* engine,
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)) + ")";
int pos_width = text_renderer_debug_->getTextWidthPhysical(pos_text.c_str());
text_renderer_debug_->printAbsolute(viewport.w - pos_width - margin, right_y, pos_text.c_str(), {255, 128, 128, 255}); // Rojo claro
text_renderer_debug_->printAbsolute(physical_viewport.w - pos_width - margin, right_y, pos_text.c_str(), {255, 128, 128, 255}); // Rojo claro
right_y += line_height;
// 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(viewport.w - vx_width - margin, right_y, vx_text.c_str(), {128, 255, 128, 255}); // Verde claro
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(viewport.w - vy_width - margin, right_y, vy_text.c_str(), {128, 255, 128, 255}); // Verde claro
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(viewport.w - grav_width - margin, right_y, grav_text.c_str(), {255, 255, 128, 255}); // Amarillo claro
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(viewport.w - surface_width - margin, right_y, surface_text.c_str(), {255, 200, 128, 255}); // Naranja claro
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(viewport.w - loss_width - margin, right_y, loss_text.c_str(), {255, 128, 255, 255}); // Magenta
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(viewport.w - dir_width - margin, right_y, gravity_dir_text.c_str(), {128, 255, 255, 255}); // Cian claro
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;
}
@@ -372,7 +398,7 @@ void UIManager::renderDebugHUD(const Engine* engine,
int convergence_percent = static_cast<int>(shape_convergence * 100.0f);
std::string convergence_text = "Convergence: " + std::to_string(convergence_percent) + "%";
int conv_width = text_renderer_debug_->getTextWidthPhysical(convergence_text.c_str());
text_renderer_debug_->printAbsolute(viewport.w - conv_width - margin, right_y, convergence_text.c_str(), {255, 128, 0, 255}); // Naranja
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;
}
}