9 Commits

Author SHA1 Message Date
a929346463 fix: SHAPE mode - regenerar figuras al cambiar tamaño con F4
Al entrar o salir del modo fullscreen real (F4), el área de juego cambia
de tamaño. Si estábamos en modo SHAPE, las bolas aparecían centradas pero
sin formar la figura.

PROBLEMA RAÍZ:
- changeScenario() recrea las bolas en nuevas posiciones
- NO se regeneraban los targets de la figura (puntos 3D)
- NO se reactivaba la atracción física hacia los targets
- Resultado: bolas centradas sin formar figura

SOLUCIÓN:
Después de changeScenario() en ambas transiciones de F4:
1. Llamar a generateShape() (Engine, no ShapeManager)
2. Reactivar enableShapeAttraction(true) en todas las bolas

Esto sigue el mismo patrón usado en Engine::changeScenario() (línea 515).

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 09:34:20 +02:00
c4075f68db 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>
2025-10-17 08:08:04 +02:00
399650f8da fix: Notifier usa viewport físico en F3 (coordenadas reales)
Problema:
- En modo F3 (letterbox/integer scale), las notificaciones se pintaban
  fuera del área de juego (en las barras negras)
- SDL_GetRenderViewport() devuelve coordenadas LÓGICAS cuando hay
  presentación lógica activa
- printAbsolute() trabaja en píxeles FÍSICOS
- Mismatch de coordenadas causaba centrado incorrecto

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

- Notifier::render() ahora usa physical_viewport.w para centrado

Resultado:
- Notificaciones centradas correctamente en F3 letterbox
- Notificaciones centradas 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>
2025-10-17 08:08:01 +02:00
9b8afa1219 fix: HUD de debug alineado correcto en viewport (F3 letterbox)
Problema:
- Columna derecha del HUD (FPS, info de pelota) se alineaba usando dimensión física
- En modo letterbox (F3 INTEGER/LETTERBOX) aparecía en barras negras o fuera de pantalla
- Mismo issue que tenían Notifier y Help Overlay

Causa:
- ui_manager.cpp:renderDebugHUD() usaba `physical_window_width_` para alinear a la derecha
- En F3 letterbox: viewport visible < ventana física
- Ejemplo: ventana 1920px, viewport 1280px con offset 320px
- Cálculo: fps_x = 1920 - width - margin
- printAbsolute() aplicaba offset: 1920 - width + 320 = fuera de pantalla
- Resultado: texto del HUD invisible o en barras negras

Solución:
- Obtener viewport con SDL_GetRenderViewport() al inicio de renderDebugHUD()
- Reemplazar TODAS las referencias a `physical_window_width_` con `viewport.w`
- Coordenadas relativas al viewport, printAbsolute() aplica offset automáticamente

Código modificado:
- ui_manager.cpp:208-211 - Obtención de viewport
- ui_manager.cpp:315, 326, 333, 340, 347, 353, 360, 366, 375 - Alineación con viewport.w

Líneas afectadas (9 totales):
- FPS counter
- Posición X/Y primera pelota
- Velocidad X/Y
- Fuerza de gravedad
- Estado superficie
- Coeficiente de rebote (loss)
- Dirección de gravedad
- Convergencia (LOGO mode)

Resultado:
 HUD de debug alineado correctamente al borde derecho del viewport
 Columna derecha visible dentro del área de juego
 No aparece en barras negras en F3
 Funciona correctamente en ventana, F3 y F4

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 07:47:50 +02:00
5b674c8ea6 fix: Notifier centrado correcto en viewport (F3 letterbox)
Problema:
- Notificaciones se centraban usando dimensión física de ventana
- En modo letterbox (F3 INTEGER/LETTERBOX) aparecían en barras negras
- Mismo issue que tenía Help Overlay

Causa:
- notifier.cpp:165 usaba `window_width_` para calcular centrado
- En F3 letterbox: viewport visible < ventana física
- Ejemplo: ventana 1920px, viewport 1280px con offset 320px
- Resultado: notificación descentrada fuera del área visible

Solución:
- Obtener viewport con SDL_GetRenderViewport() antes de calcular posición
- Usar `viewport.w` en lugar de `window_width_` para centrado
- Coordenadas relativas al viewport, printAbsolute() aplica offset automáticamente

Código modificado:
- notifier.cpp:162-170 - Centrado usando viewport dimensions

Resultado:
 Notificaciones centradas en área visible (viewport)
 No aparecen en barras negras en F3
 Funciona correctamente en ventana, F3 y F4

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 07:47:10 +02:00
7fac103c51 fix: Help Overlay - ambas columnas respetan padding inferior
Problema:
- Solo columna 0 verificaba si cabía más texto antes de escribir
- Columna 1 (derecha) escribía fuera del overlay si no cabía
- En ventanas de 600px altura, columna 1 se desbordaba

Solución:
- Eliminada restricción `&& current_column == 0` del check de padding
- Ahora AMBAS columnas verifican si caben antes de escribir
- Si columna 1 está llena: omitir texto restante (continue)
- Si columna 0 está llena: cambiar a columna 1

Comportamiento preferido por el usuario:
"prefiero que 'falte texto' a que el texto se escriba por fuera del overlay"

Resultado:
 Columna 0 cambia a columna 1 cuando se llena
 Columna 1 omite texto que no cabe
 Overlay nunca muestra texto fuera de sus límites
 Funciona correctamente en ventanas pequeñas (600px)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 07:33:17 +02:00
bcceb94c9e fix: Help Overlay centrado correcto en modo F3 letterbox
Problemas resueltos:
- En modo F3 (letterbox), el overlay se centraba en pantalla física
  en lugar de en el viewport visible, quedando desplazado
- Al salir de F3 a ventana, el overlay seguía roto
- Padding inferior no se respetaba correctamente

Cambios implementados:
1. render() ahora usa SDL_GetRenderViewport() para obtener área visible
2. Centrado calculado dentro del viewport (con offset de barras negras)
3. toggleFullscreen() restaura tamaño de ventana al salir de F3
4. Padding check movido ANTES de escribir línea (>= en lugar de >)
5. Debug logging añadido para diagnóstico de dimensiones

Resultado:
 Overlay centrado correctamente en F3 letterbox
 Overlay se regenera correctamente al salir de F3
 Padding inferior respetado en columna 0

Pendiente:
- Columna 2 (índice 1) todavía no respeta padding inferior
- Verificar que F4 (real fullscreen) siga funcionando correctamente

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 07:30:31 +02:00
1b3d32ba84 fix: Help Overlay - fullscreen resize roto y padding inferior
Correcciones críticas para overlay en fullscreen y padding:

1. Fullscreen/resize roto CORREGIDO:
   - Problema: orden incorrecto de actualizaciones causaba mezcla de
     dimensiones antiguas (800x600) con font nuevo (24px)
   - Solución: nuevo método updateAll() que actualiza font Y dimensiones
     de forma atómica
   - Flujo correcto: dimensiones físicas → font → recalcular box
   - Antes: overlay gigante y descuadrado al cambiar fullscreen
   - Ahora: overlay se reposiciona y escala correctamente

2. Padding inferior inexistente CORREGIDO:
   - Problema: calculateTextDimensions() usaba num_lines/2 asumiendo
     división perfecta entre columnas
   - Problema 2: rebuildCachedTexture() no verificaba límite inferior
     en columna 1
   - Solución: contar líneas REALES en cada columna y usar el máximo
   - Fórmula correcta: line_height*2 + max_column_lines*line_height + padding*2
   - Ahora: padding inferior respetado siempre

3. Implementación técnica:
   - HelpOverlay::updateAll(font, width, height) nuevo método unificado
   - UIManager llama updateAll() en lugar de reinitializeFontSize() +
     updatePhysicalWindowSize() separadamente
   - Elimina race condition entre actualización de font y dimensiones

Resultado:
- F3/F4 (fullscreen) funciona correctamente
- Resize ventana (F1/F2) funciona correctamente
- Padding inferior respetado en ambas columnas
- Sin overlays gigantes o descuadrados

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 21:32:32 +02:00
7c0a60f140 fix: Help Overlay - corregir solapamiento de columnas y layout
Tres correcciones importantes para el Help Overlay:

1. Solapamiento de columnas corregido:
   - Añadidos column1_width_ y column2_width_ para anchos reales
   - calculateTextDimensions() ahora incluye encabezados en cálculo
   - rebuildCachedTexture() usa anchos reales de columnas
   - Columna 2 empieza en padding + column1_width_ + padding
   - Elimina cálculo erróneo column_width = (box_width_ - padding*3)/2

2. Layout en alta resolución corregido:
   - Eliminado ancho mínimo forzado del 90% de dimensión menor
   - box_width_ ahora usa directamente text_width (justo lo necesario)
   - Antes: 1920x1080 → min 972px aunque contenido necesite 600px
   - Ahora: box ajustado al contenido sin espacio vacío extra

3. Fullscreen/resize corregido:
   - reinitializeFontSize() ya NO llama a calculateBoxDimensions()
   - Evita recalcular con physical_width_ y physical_height_ antiguos
   - Confía en updatePhysicalWindowSize() que se llama después
   - Antes: textura cacheada creada con dimensiones incorrectas
   - Ahora: textura siempre creada con dimensiones correctas

Resultado:
- Columnas no se montan entre sí
- Box ajustado al contenido sin espacio vacío derecha
- Cambios fullscreen/ventana funcionan correctamente
- Overlay se recalcula apropiadamente en todos los casos

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 21:22:12 +02:00
6 changed files with 377 additions and 52 deletions

128
RULES.md Normal file
View File

@@ -0,0 +1,128 @@
Documento de especificaciones de ViBe3 Physics
# Codigo
* Se preferira el uso de #pragma once a #ifndef
* Se preferira el uso de C++ frente a C
* Se preferirá el uso de verisiones mas moderdas de C++ frente a las mas viejas, es decir, C++20 frente a C++17, por ejemplo
* Se preferirá el uso de smart pointers frente a new/delete y sobretodo antes que malloc/free
* Los archivos de cabecera que definan clases, colocaran primero la parte publica y luego la privada. Agruparan los metodos por categorias. Todas las variables, constantes, estructuras, enumeraciones, metodos, llevaran el comentario a la derecha
* Se respetarán las reglas definidas en los ficheros .clang-tidy y .clang-format que hay en la raíz o en las subcarpetas
# Funcionamiento
* El programa tiene modos de funcionamiento (AppMode). El funcionamiento de cada uno de ellos se describirá mas adelante. Son estados exclusivos que van automatizando cambios en el SimulationMode, Theme y Scene y serian:
* SANDBOX
* DEMO
* DEMO LITE
* LOGO
* LOGO LITE
* El progama tiene otros modos de funcionamiento (SimulationMode). El funcionamiento de cada uno de ellos se describirá mas adelante. Son estados exclusivos:
* PHYISICS
* FIGURE
* BOIDS
* El programa tiene un gestor de temas (Theme) que cambia los colores de lo que se ve en pantalla. Hay temas estáticos y dinamicos. El cambio de tema se realiza mediante LERP y no afecta en nada ni al AppMode ni al SimulationMode, es decir, no modifica sus estados.
* El programa tiene escenarios (Scene). Cada escena tiene un numero de pelotas. Cuando se cambia el escenario, se elimina el vector de pelotas y se crea uno nuevo. En funcion del SimulationMode actual se inicializan las pelotas de manera distinta:
* PHYSICS: Se crean todas las pelotas cerca de la parte superior de la pantalla distribuidas en el 75% central del eje X (es como está ahora)
* FIGURE: Se crean todas las pelotas en el punto central de la pantalla
* BOIDS: Se crean todas las pelotas en posiciones al azar de la pantalla con velocidades y direcciones aleatorias
* El cambio de SimulationMode ha de preservar la inercia (velocidad, aceleracion, direccion) de cada pelota. El cambio se produce tanto de forma manual (pulsacion de una tecla por el usuario) como de manera automatica (cualquier AppMode que no sea SANDBOX)
* PHYSICS a FIGURE:
* Pulsando la tecla de la figura correspondiente
* Pulsando la tecla F (ultima figura seleccionada)
* PHYSICS a BOIDS:
* Pulsando la tecla B
* FIGURE a PHYSICS:
* Pulsando los cursores: Gravedad ON en la direccion del cursor
* Pulsando la tecla G: Gravedad OFF
* Pulsando la tecla F: Ultima gravedad seleccionada (direccion o OFF)
* FIGURE a BOIDS:
* Pulsando la tecla B
* BOIDS a PHYSICS:
* Pulsando la tecla G: Gravedad OFF
* Pulsando los cursores: Gravedad ON en la direccion del cursor
* BOIDS a FIGURE:
* Pulsando la tecla de la figura
* Pulsando la tecla F (ultima figura)
# AppMode
* SANDBOX
* No hay ningun automatismo. El usuario va pulsando teclas para ejecutar acciones.
* Si pulsa una de estas teclas, cambia de modo:
* D: DEMO
* L: DEMO LITE
* K: LOGO
* DEMO
* En el modo DEMO el programa va cambiando el SimulationMode de manera automatica (como está ahora es correcto)
* Se inicializa con un Theme al azar, Scene al azar, SimulationMode al azar. Restringido FIGURE->PNG_SHAPE
* Va cambiando de Theme
* Va cambiando de Scene
* Cambia la escala de la Figure
* Cambia el Sprite de las pelotas
* NO PUEDE cambiar a la figura PNG_SHAPE
* Eventualmente puede cambiar de manera automatica a LOGO LITE, sin restricciones
* El usuario puede cambiar el SimulationMode, el Theme o el Scene. Esto no hace que se salga del modo DEMO
* El usuario puede cambiar de AppMode pulsando:
* D: SANDBOX
* L: DEMO LITE
* K: LOGO
* DEMO LITE
* En el modo DEMO el programa va cambiando el SimulationMode de manera automatica (como está ahora es correcto)
* Se inicializa con un Theme al azar, Scene al azar, SimulationMode al azar. Restringido FIGURE->PNG_SHAPE
* Este modo es exactamente igual a DEMO pero NO PUEDE:
* Cambiar de Scene
* Cambiar de Theme
* Cambiar el Sprite de las pelotas
* Eventualmente puede cambiar de manera automatica a LOGO LITE, sin restricciones
* NO PUEDE cambiar a la figura PNG_SHAPE
* El usuario puede cambiar el SimulationMode, el Theme o el Scene. Esto no hace que se salga del modo DEMO LITE
* El usuario puede cambiar de AppMode pulsando:
* D: DEMO
* L: SANDBOX
* K: LOGO
* LOGO
* Se inicializa con la Scene de 5.000 pelotas, con el tamaño de Sprite->Small, con SimulationMode en FIGURE->PNG_SHAPE, con un tema al azar de los permitidos
* No cambia de Scene
* No cambia el tamaño de Sprite
* No cambia la escala de FIGURE
* Los temas permitidos son MONOCROMO, LAVANDA, CARMESI, ESMERALDA o cualquiera de los temas dinamicos
* En este modo SOLO aparece la figura PNG_SHAPE
* Solo cambiara a los temas permitidos
* Cambia el SimulationMode de PHYSICS a FIGURE (como hace ahora) pero no a BOIDS. BOIDS prohibido
* El usuario puede cambiar el SimulationMode, el Theme o el Scene. Esto no hace que se salga del modo LOGO. Incluso puede poner un Theme no permitido o otro Scene.
* El automatismo no cambia nunca de Theme así que se mantiene el del usuario.
* El automatismo no cambia nunca de Scene asi que se mantiene el del usuario.
* El usuario puede cambiar de AppMode pulsando:
* D: DEMO
* L: DEMO LITE
* K: SANDBOX
* B: SANDBOX->BOIDS
* LOGO LITE
* Este modo es exactamente igual al modo LOGO pero con unas pequeñas diferencias:
* Solo se accede a el de manera automatica, el usuario no puede invocarlo. No hay tecla
* Como se accede de manera automatica solo se puede llegar a él desde DEMO o DEMO LITE. Hay que guardar el estado en el que se encontraba AppMode, EngindeMode, Scene, Theme, Sprite, Scale... etc
* Este modo tiene una muy alta probabilidad de terminar, volviendo al estado anterior desde donde se invocó.
* El usuario puede cambiar de AppMode pulsando:
* D: Si el modo anterior era DEMO -> SANDBOX, else -> DEMO)
* L: Si el modo anterior era DEMO LITE -> SANDBOX, else -> DEMO LITE)
* K: LOGO
* B: SANDBOX->BOIDS
# Debug Hud
* En el debug hud hay que añadir que se vea SIEMPRE el AppMode (actualmente aparece centrado, hay que ponerlo a la izquierda) y no solo cietos AppModes
* Tiene que aparecer tambien el SimulationMode
* El modo de Vsync
* El modo de escalado entero, stretched, ventana
* la resolucion fisica
* la resolucion logica
* el refresco del panel
* El resto de cosas que salen
# Ventana de ayuda
* La ventana de ayuda actualmente es cuadrada
* Esa es la anchura minima que ha de tener
* Hay que ver cual es la linea mas larga, multiplicarla por el numero de columnas, añadirle los paddings y que ese sea el nuevo ancho
* Actualmente se renderiza a cada frame. El rendimiento cae de los 1200 frames por segundo a 200 frames por segundo. Habria que renderizarla a una textura o algo. El problema es que el cambio de Theme con LERP afecta a los colores de la ventana. Hay que investigar qué se puede hacer.
# Bugs actuales
* En el modo LOGO, si se pulsa un cursor, se activa la gravedad y deja de funcionar los automatismos. Incluso he llegado a ver como sale solo del modo LOGO sin pulsar nada
* En el modo BOIDS, pulsar la G activa la gravedad. La G deberia pasar al modo PHYSICS con la gravedad en OFF y que las pelotas mantuvieran el momento/inercia

View File

@@ -744,6 +744,12 @@ void Engine::toggleFullscreen() {
fullscreen_enabled_ = !fullscreen_enabled_; fullscreen_enabled_ = !fullscreen_enabled_;
SDL_SetWindowFullscreen(window_, fullscreen_enabled_); SDL_SetWindowFullscreen(window_, fullscreen_enabled_);
// Si acabamos de salir de fullscreen, restaurar tamaño de ventana
if (!fullscreen_enabled_) {
SDL_SetWindowSize(window_, base_screen_width_ * current_window_zoom_, base_screen_height_ * current_window_zoom_);
SDL_SetWindowPosition(window_, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
}
// Actualizar dimensiones físicas después del cambio // Actualizar dimensiones físicas después del cambio
updatePhysicalWindowSize(); updatePhysicalWindowSize();
} }
@@ -784,6 +790,17 @@ void Engine::toggleRealFullscreen() {
// Actualizar tamaño de pantalla para boids (wrapping boundaries) // Actualizar tamaño de pantalla para boids (wrapping boundaries)
boid_manager_->updateScreenSize(current_screen_width_, current_screen_height_); boid_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
// Si estamos en modo SHAPE, regenerar la figura con nuevas dimensiones
if (current_mode_ == SimulationMode::SHAPE) {
generateShape(); // Regenerar figura con nuevas dimensiones de pantalla
// Activar atracción física en las bolas nuevas (crítico tras changeScenario)
auto& balls = scene_manager_->getBallsMutable();
for (auto& ball : balls) {
ball->enableShapeAttraction(true);
}
}
} }
SDL_free(displays); SDL_free(displays);
} }
@@ -806,6 +823,17 @@ void Engine::toggleRealFullscreen() {
// Reinicar la escena con resolución original // Reinicar la escena con resolución original
scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_); scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
scene_manager_->changeScenario(scene_manager_->getCurrentScenario(), current_mode_); scene_manager_->changeScenario(scene_manager_->getCurrentScenario(), current_mode_);
// Si estamos en modo SHAPE, regenerar la figura con nuevas dimensiones
if (current_mode_ == SimulationMode::SHAPE) {
generateShape(); // Regenerar figura con nuevas dimensiones de pantalla
// Activar atracción física en las bolas nuevas (crítico tras changeScenario)
auto& balls = scene_manager_->getBallsMutable();
for (auto& ball : balls) {
ball->enableShapeAttraction(true);
}
}
} }
} }

View File

@@ -16,6 +16,8 @@ HelpOverlay::HelpOverlay()
box_height_(0), box_height_(0),
box_x_(0), box_x_(0),
box_y_(0), box_y_(0),
column1_width_(0),
column2_width_(0),
cached_texture_(nullptr), cached_texture_(nullptr),
last_category_color_({0, 0, 0, 255}), last_category_color_({0, 0, 0, 255}),
last_content_color_({0, 0, 0, 255}), last_content_color_({0, 0, 0, 255}),
@@ -84,6 +86,13 @@ HelpOverlay::~HelpOverlay() {
delete text_renderer_; delete text_renderer_;
} }
void HelpOverlay::toggle() {
visible_ = !visible_;
SDL_Log("HelpOverlay::toggle() - visible=%s, box_pos=(%d,%d), box_size=%dx%d, physical=%dx%d",
visible_ ? "TRUE" : "FALSE", box_x_, box_y_, box_width_, box_height_,
physical_width_, physical_height_);
}
void HelpOverlay::initialize(SDL_Renderer* renderer, ThemeManager* theme_mgr, int physical_width, int physical_height, int font_size) { void HelpOverlay::initialize(SDL_Renderer* renderer, ThemeManager* theme_mgr, int physical_width, int physical_height, int font_size) {
renderer_ = renderer; renderer_ = renderer;
theme_mgr_ = theme_mgr; theme_mgr_ = theme_mgr;
@@ -94,7 +103,12 @@ void HelpOverlay::initialize(SDL_Renderer* renderer, ThemeManager* theme_mgr, in
text_renderer_ = new TextRenderer(); text_renderer_ = new TextRenderer();
text_renderer_->init(renderer, "data/fonts/FunnelSans-Regular.ttf", font_size, true); text_renderer_->init(renderer, "data/fonts/FunnelSans-Regular.ttf", font_size, true);
SDL_Log("HelpOverlay::initialize() - physical=%dx%d, font_size=%d", physical_width, physical_height, font_size);
calculateBoxDimensions(); calculateBoxDimensions();
SDL_Log("HelpOverlay::initialize() - AFTER calculateBoxDimensions: box_pos=(%d,%d), box_size=%dx%d",
box_x_, box_y_, box_width_, box_height_);
} }
void HelpOverlay::updatePhysicalWindowSize(int physical_width, int physical_height) { void HelpOverlay::updatePhysicalWindowSize(int physical_width, int physical_height) {
@@ -112,11 +126,37 @@ void HelpOverlay::reinitializeFontSize(int new_font_size) {
// Reinicializar text renderer con nuevo tamaño // Reinicializar text renderer con nuevo tamaño
text_renderer_->reinitialize(new_font_size); text_renderer_->reinitialize(new_font_size);
// Recalcular dimensiones del box (el texto ahora tiene distinto tamaño) // NOTA: NO recalcular dimensiones aquí porque physical_width_ y physical_height_
// pueden tener valores antiguos. updatePhysicalWindowSize() se llamará después
// con las dimensiones correctas y recalculará todo apropiadamente.
// Marcar textura para regeneración completa
texture_needs_rebuild_ = true;
}
void HelpOverlay::updateAll(int font_size, int physical_width, int physical_height) {
SDL_Log("HelpOverlay::updateAll() - INPUT: font_size=%d, physical=%dx%d",
font_size, physical_width, physical_height);
SDL_Log("HelpOverlay::updateAll() - BEFORE: box_pos=(%d,%d), box_size=%dx%d",
box_x_, box_y_, box_width_, box_height_);
// Actualizar dimensiones físicas PRIMERO
physical_width_ = physical_width;
physical_height_ = physical_height;
// Reinicializar text renderer con nuevo tamaño (si cambió)
if (text_renderer_) {
text_renderer_->reinitialize(font_size);
}
// Recalcular dimensiones del box con nuevo font y nuevas dimensiones
calculateBoxDimensions(); calculateBoxDimensions();
// Marcar textura para regeneración completa // Marcar textura para regeneración completa
texture_needs_rebuild_ = true; texture_needs_rebuild_ = true;
SDL_Log("HelpOverlay::updateAll() - AFTER: box_pos=(%d,%d), box_size=%dx%d",
box_x_, box_y_, box_width_, box_height_);
} }
void HelpOverlay::calculateTextDimensions(int& max_width, int& total_height) { void HelpOverlay::calculateTextDimensions(int& max_width, int& total_height) {
@@ -141,15 +181,22 @@ void HelpOverlay::calculateTextDimensions(int& max_width, int& total_height) {
continue; continue;
} }
// Separador vacío o encabezado // Separador vacío (no tiene key ni description)
if (binding.description[0] == '\0') { if (binding.key[0] == '\0') {
continue; continue;
} }
// Calcular ancho de esta línea: key + espacio + description int line_width = 0;
int key_width = text_renderer_->getTextWidthPhysical(binding.key);
int desc_width = text_renderer_->getTextWidthPhysical(binding.description); if (binding.description[0] == '\0') {
int line_width = key_width + 10 + desc_width; // 10px de separación // Es un encabezado (solo tiene key, sin description)
line_width = text_renderer_->getTextWidthPhysical(binding.key);
} else {
// Es una línea normal con key + description
int key_width = text_renderer_->getTextWidthPhysical(binding.key);
int desc_width = text_renderer_->getTextWidthPhysical(binding.description);
line_width = key_width + 10 + desc_width; // 10px de separación
}
// Actualizar máximo de columna correspondiente // Actualizar máximo de columna correspondiente
if (current_column == 0) { if (current_column == 0) {
@@ -159,31 +206,57 @@ void HelpOverlay::calculateTextDimensions(int& max_width, int& total_height) {
} }
} }
// Almacenar anchos de columnas en miembros para uso posterior
column1_width_ = max_col1_width;
column2_width_ = max_col2_width;
// Ancho total: 2 columnas + 3 paddings (izq, medio, der) // Ancho total: 2 columnas + 3 paddings (izq, medio, der)
max_width = max_col1_width + max_col2_width + padding * 3; max_width = max_col1_width + max_col2_width + padding * 3;
// Altura: contar líneas y calcular // Altura: contar líneas REALES en cada columna
int num_lines = 0; int col1_lines = 0;
int col2_lines = 0;
current_column = 0;
for (const auto& binding : key_bindings_) { for (const auto& binding : key_bindings_) {
if (strcmp(binding.key, "[new_col]") != 0) { // Cambio de columna
num_lines++; if (strcmp(binding.key, "[new_col]") == 0) {
current_column = 1;
continue;
}
// Separador vacío no cuenta como línea
if (binding.key[0] == '\0') {
continue;
}
// Contar línea (ya sea encabezado o contenido)
if (current_column == 0) {
col1_lines++;
} else {
col2_lines++;
} }
} }
// Altura: título + espacio + líneas de contenido
total_height = line_height * 2 + (num_lines / 2 + 2) * line_height + padding * 2; // Usar la columna más larga para calcular altura
int max_column_lines = std::max(col1_lines, col2_lines);
// Altura: título (2 líneas) + contenido + padding superior e inferior
total_height = line_height * 2 + max_column_lines * line_height + padding * 2;
} }
void HelpOverlay::calculateBoxDimensions() { void HelpOverlay::calculateBoxDimensions() {
SDL_Log("HelpOverlay::calculateBoxDimensions() START - physical=%dx%d", physical_width_, physical_height_);
// Calcular dimensiones necesarias según el texto // Calcular dimensiones necesarias según el texto
int text_width, text_height; int text_width, text_height;
calculateTextDimensions(text_width, text_height); calculateTextDimensions(text_width, text_height);
// Ancho mínimo: 90% de dimensión menor (como antes, para compatibilidad) SDL_Log("HelpOverlay::calculateBoxDimensions() - text_width=%d, text_height=%d, col1_width=%d, col2_width=%d",
int min_dimension = std::min(physical_width_, physical_height_); text_width, text_height, column1_width_, column2_width_);
int min_width = static_cast<int>(min_dimension * 0.9f);
// Usar el mayor entre ancho calculado y ancho mínimo // Usar directamente el ancho y altura calculados según el contenido
box_width_ = std::max(text_width, min_width); box_width_ = text_width;
// Altura: 90% de altura física o altura calculada, el que sea menor // Altura: 90% de altura física o altura calculada, el que sea menor
int max_height = static_cast<int>(physical_height_ * 0.9f); int max_height = static_cast<int>(physical_height_ * 0.9f);
@@ -192,11 +265,17 @@ void HelpOverlay::calculateBoxDimensions() {
// Centrar en pantalla // Centrar en pantalla
box_x_ = (physical_width_ - box_width_) / 2; box_x_ = (physical_width_ - box_width_) / 2;
box_y_ = (physical_height_ - box_height_) / 2; box_y_ = (physical_height_ - box_height_) / 2;
SDL_Log("HelpOverlay::calculateBoxDimensions() END - box_pos=(%d,%d), box_size=%dx%d, max_height=%d",
box_x_, box_y_, box_width_, box_height_, max_height);
} }
void HelpOverlay::rebuildCachedTexture() { void HelpOverlay::rebuildCachedTexture() {
if (!renderer_ || !theme_mgr_ || !text_renderer_) return; if (!renderer_ || !theme_mgr_ || !text_renderer_) return;
SDL_Log("HelpOverlay::rebuildCachedTexture() - Regenerando textura: box_size=%dx%d, box_pos=(%d,%d)",
box_width_, box_height_, box_x_, box_y_);
// Destruir textura anterior si existe // Destruir textura anterior si existe
if (cached_texture_) { if (cached_texture_) {
SDL_DestroyTexture(cached_texture_); SDL_DestroyTexture(cached_texture_);
@@ -279,10 +358,9 @@ void HelpOverlay::rebuildCachedTexture() {
last_content_color_ = content_color; last_content_color_ = content_color;
last_bg_color_ = {static_cast<Uint8>(notif_bg_r), static_cast<Uint8>(notif_bg_g), static_cast<Uint8>(notif_bg_b), 255}; last_bg_color_ = {static_cast<Uint8>(notif_bg_r), static_cast<Uint8>(notif_bg_g), static_cast<Uint8>(notif_bg_b), 255};
// Configuración de espaciado (misma que renderHelpText()) // Configuración de espaciado
int line_height = text_renderer_->getTextHeight(); int line_height = text_renderer_->getTextHeight();
int padding = 25; int padding = 25;
int column_width = (box_width_ - padding * 3) / 2;
int current_x = padding; // Coordenadas relativas a la textura (0,0) int current_x = padding; // Coordenadas relativas a la textura (0,0)
int current_y = padding; int current_y = padding;
@@ -301,12 +379,27 @@ void HelpOverlay::rebuildCachedTexture() {
if (strcmp(binding.key, "[new_col]") == 0 && binding.description[0] == '\0') { if (strcmp(binding.key, "[new_col]") == 0 && binding.description[0] == '\0') {
if (current_column == 0) { if (current_column == 0) {
current_column = 1; current_column = 1;
current_x = padding + column_width + padding; current_x = padding + column1_width_ + padding; // Usar ancho real de columna 1
current_y = content_start_y; current_y = content_start_y;
} }
continue; continue;
} }
// CHECK PADDING INFERIOR ANTES de escribir la línea (AMBAS COLUMNAS)
// Verificar si la PRÓXIMA línea cabrá dentro del box con padding inferior
if (current_y + line_height >= box_height_ - padding) {
if (current_column == 0) {
// Columna 0 llena: cambiar a columna 1
current_column = 1;
current_x = padding + column1_width_ + padding;
current_y = content_start_y;
} else {
// Columna 1 llena: omitir resto de texto (no cabe)
// Preferible omitir que sobresalir del overlay
continue;
}
}
if (binding.description[0] == '\0') { if (binding.description[0] == '\0') {
text_renderer_->printAbsolute(current_x, current_y, binding.key, category_color); text_renderer_->printAbsolute(current_x, current_y, binding.key, category_color);
current_y += line_height + 2; current_y += line_height + 2;
@@ -318,12 +411,6 @@ void HelpOverlay::rebuildCachedTexture() {
text_renderer_->printAbsolute(current_x + key_width + 10, current_y, binding.description, content_color); text_renderer_->printAbsolute(current_x + key_width + 10, current_y, binding.description, content_color);
current_y += line_height; current_y += line_height;
if (current_y > box_height_ - padding && current_column == 0) {
current_column = 1;
current_x = padding + column_width + padding;
current_y = content_start_y;
}
} }
// Restaurar render target original // Restaurar render target original
@@ -374,10 +461,23 @@ void HelpOverlay::render(SDL_Renderer* renderer) {
// CRÍTICO: Habilitar alpha blending para que la transparencia funcione // CRÍTICO: Habilitar alpha blending para que la transparencia funcione
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
// Renderizar la textura cacheada en la posición del overlay // Obtener viewport actual (en modo letterbox F3 tiene offset para centrar imagen)
SDL_Rect viewport;
SDL_GetRenderViewport(renderer, &viewport);
// Calcular posición centrada dentro del VIEWPORT, no de la pantalla física
// viewport.w y viewport.h son las dimensiones del área visible
// viewport.x y viewport.y son el offset de las barras negras
int centered_x = viewport.x + (viewport.w - box_width_) / 2;
int centered_y = viewport.y + (viewport.h - box_height_) / 2;
SDL_Log("HelpOverlay::render() - viewport=(%d,%d,%dx%d), centered_pos=(%d,%d), box_size=%dx%d",
viewport.x, viewport.y, viewport.w, viewport.h, centered_x, centered_y, box_width_, box_height_);
// Renderizar la textura cacheada centrada en el viewport
SDL_FRect dest_rect; SDL_FRect dest_rect;
dest_rect.x = static_cast<float>(box_x_); dest_rect.x = static_cast<float>(centered_x);
dest_rect.y = static_cast<float>(box_y_); dest_rect.y = static_cast<float>(centered_y);
dest_rect.w = static_cast<float>(box_width_); dest_rect.w = static_cast<float>(box_width_);
dest_rect.h = static_cast<float>(box_height_); dest_rect.h = static_cast<float>(box_height_);

View File

@@ -41,10 +41,18 @@ class HelpOverlay {
*/ */
void reinitializeFontSize(int new_font_size); void reinitializeFontSize(int new_font_size);
/**
* @brief Actualiza font size Y dimensiones físicas de forma atómica
* @param font_size Tamaño de fuente actual
* @param physical_width Nueva anchura física
* @param physical_height Nueva altura física
*/
void updateAll(int font_size, int physical_width, int physical_height);
/** /**
* @brief Toggle visibilidad del overlay * @brief Toggle visibilidad del overlay
*/ */
void toggle() { visible_ = !visible_; } void toggle();
/** /**
* @brief Consulta si el overlay está visible * @brief Consulta si el overlay está visible
@@ -65,6 +73,10 @@ class HelpOverlay {
int box_x_; int box_x_;
int box_y_; int box_y_;
// Anchos individuales de cada columna (para evitar solapamiento)
int column1_width_;
int column2_width_;
// Sistema de caché para optimización de rendimiento // Sistema de caché para optimización de rendimiento
SDL_Texture* cached_texture_; // Textura cacheada del overlay completo SDL_Texture* cached_texture_; // Textura cacheada del overlay completo
SDL_Color last_category_color_; // Último color de categorías renderizado SDL_Color last_category_color_; // Último color de categorías renderizado

View File

@@ -5,6 +5,31 @@
#include "../utils/easing_functions.h" #include "../utils/easing_functions.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
// ============================================================================
// 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;
}
Notifier::Notifier() Notifier::Notifier()
: renderer_(nullptr) : renderer_(nullptr)
, text_renderer_(nullptr) , text_renderer_(nullptr)
@@ -159,10 +184,14 @@ void Notifier::render() {
int bg_width = text_width + (NOTIFICATION_PADDING * 2); int bg_width = text_width + (NOTIFICATION_PADDING * 2);
int bg_height = text_height + (NOTIFICATION_PADDING * 2); int bg_height = text_height + (NOTIFICATION_PADDING * 2);
// Centrar en la ventana FÍSICA (no usar viewport lógico) // Obtener viewport FÍSICO (píxeles reales, no lógicos)
// CRÍTICO: Como renderizamos en píxeles físicos absolutos (bypass de presentación lógica), // CRÍTICO: En F3, SDL_GetRenderViewport() devuelve coordenadas LÓGICAS,
// debemos centrar usando dimensiones físicas, no el viewport lógico de SDL // pero printAbsolute() trabaja en píxeles FÍSICOS. Usar helper para obtener
int x = (window_width_ / 2) - (bg_width / 2); // viewport en coordenadas físicas.
SDL_Rect physical_viewport = getPhysicalViewport(renderer_);
// Centrar en el viewport físico (coordenadas relativas al viewport)
int x = (physical_viewport.w / 2) - (bg_width / 2);
int y = NOTIFICATION_TOP_MARGIN + static_cast<int>(current_notification_->y_offset); int y = NOTIFICATION_TOP_MARGIN + static_cast<int>(current_notification_->y_offset);
// Renderizar fondo semitransparente (con bypass de presentación lógica) // Renderizar fondo semitransparente (con bypass de presentación lógica)

View File

@@ -13,6 +13,31 @@
#include "notifier.h" // for Notifier #include "notifier.h" // for Notifier
#include "help_overlay.h" // for HelpOverlay #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() UIManager::UIManager()
: text_renderer_(nullptr) : text_renderer_(nullptr)
, text_renderer_debug_(nullptr) , text_renderer_debug_(nullptr)
@@ -175,18 +200,15 @@ void UIManager::updatePhysicalWindowSize(int width, int height) {
if (text_renderer_notifier_) { if (text_renderer_notifier_) {
text_renderer_notifier_->reinitialize(current_font_size_); text_renderer_notifier_->reinitialize(current_font_size_);
} }
// Reinicializar help overlay con nuevo tamaño de fuente
if (help_overlay_) {
help_overlay_->reinitializeFontSize(current_font_size_);
}
} }
// Actualizar componentes de UI con nuevas dimensiones // Actualizar help overlay con font size actual Y nuevas dimensiones (atómicamente)
notifier_->updateWindowSize(width, height);
if (help_overlay_) { if (help_overlay_) {
help_overlay_->updatePhysicalWindowSize(width, height); help_overlay_->updateAll(current_font_size_, width, height);
} }
// Actualizar otros componentes de UI con nuevas dimensiones
notifier_->updateWindowSize(width, height);
} }
void UIManager::setTextObsolete(const std::string& text, int pos, int current_screen_width) { void UIManager::setTextObsolete(const std::string& text, int pos, int current_screen_width) {
@@ -208,6 +230,12 @@ void UIManager::renderDebugHUD(const Engine* engine,
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; // Margen constante en píxeles físicos
// 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) // COLUMNA LEFT (Sistema)
// =========================== // ===========================
@@ -310,7 +338,7 @@ void UIManager::renderDebugHUD(const Engine* engine,
// FPS counter (esquina superior derecha) // FPS counter (esquina superior derecha)
int fps_text_width = text_renderer_debug_->getTextWidthPhysical(fps_text_.c_str()); int fps_text_width = text_renderer_debug_->getTextWidthPhysical(fps_text_.c_str());
int fps_x = physical_window_width_ - 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 text_renderer_debug_->printAbsolute(fps_x, right_y, fps_text_.c_str(), {255, 255, 0, 255}); // Amarillo
right_y += line_height; right_y += line_height;
@@ -321,47 +349,47 @@ void UIManager::renderDebugHUD(const Engine* engine,
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)) + ")"; 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()); int pos_width = text_renderer_debug_->getTextWidthPhysical(pos_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - 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; right_y += line_height;
// Velocidad X // Velocidad X
int vx_int = static_cast<int>(first_ball->getVelocityX()); int vx_int = static_cast<int>(first_ball->getVelocityX());
std::string vx_text = "VelX: " + std::to_string(vx_int); std::string vx_text = "VelX: " + std::to_string(vx_int);
int vx_width = text_renderer_debug_->getTextWidthPhysical(vx_text.c_str()); int vx_width = text_renderer_debug_->getTextWidthPhysical(vx_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - 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; right_y += line_height;
// Velocidad Y // Velocidad Y
int vy_int = static_cast<int>(first_ball->getVelocityY()); int vy_int = static_cast<int>(first_ball->getVelocityY());
std::string vy_text = "VelY: " + std::to_string(vy_int); std::string vy_text = "VelY: " + std::to_string(vy_int);
int vy_width = text_renderer_debug_->getTextWidthPhysical(vy_text.c_str()); int vy_width = text_renderer_debug_->getTextWidthPhysical(vy_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - 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; right_y += line_height;
// Fuerza de gravedad // Fuerza de gravedad
int grav_int = static_cast<int>(first_ball->getGravityForce()); int grav_int = static_cast<int>(first_ball->getGravityForce());
std::string grav_text = "Gravity: " + std::to_string(grav_int); std::string grav_text = "Gravity: " + std::to_string(grav_int);
int grav_width = text_renderer_debug_->getTextWidthPhysical(grav_text.c_str()); int grav_width = text_renderer_debug_->getTextWidthPhysical(grav_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - 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; right_y += line_height;
// Estado superficie // Estado superficie
std::string surface_text = first_ball->isOnSurface() ? "Surface: YES" : "Surface: NO"; std::string surface_text = first_ball->isOnSurface() ? "Surface: YES" : "Surface: NO";
int surface_width = text_renderer_debug_->getTextWidthPhysical(surface_text.c_str()); int surface_width = text_renderer_debug_->getTextWidthPhysical(surface_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - 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; right_y += line_height;
// Coeficiente de rebote (loss) // Coeficiente de rebote (loss)
float loss_val = first_ball->getLossCoefficient(); float loss_val = first_ball->getLossCoefficient();
std::string loss_text = "Loss: " + std::to_string(loss_val).substr(0, 4); std::string loss_text = "Loss: " + std::to_string(loss_val).substr(0, 4);
int loss_width = text_renderer_debug_->getTextWidthPhysical(loss_text.c_str()); int loss_width = text_renderer_debug_->getTextWidthPhysical(loss_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - 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; right_y += line_height;
// Dirección de gravedad // Dirección de gravedad
std::string gravity_dir_text = "Dir: " + gravityDirectionToString(static_cast<int>(scene_manager->getCurrentGravity())); 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()); int dir_width = text_renderer_debug_->getTextWidthPhysical(gravity_dir_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - 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; right_y += line_height;
} }
@@ -370,7 +398,7 @@ void UIManager::renderDebugHUD(const Engine* engine,
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) + "%"; std::string convergence_text = "Convergence: " + std::to_string(convergence_percent) + "%";
int conv_width = text_renderer_debug_->getTextWidthPhysical(convergence_text.c_str()); int conv_width = text_renderer_debug_->getTextWidthPhysical(convergence_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - 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; right_y += line_height;
} }
} }