From 8c2a8857fcde48a5a50c57b163842d847738ae98 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Thu, 16 Oct 2025 09:52:33 +0200 Subject: [PATCH] fix: Preservar SimulationMode y mejorar Debug HUD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CAMBIOS: - Debug HUD reorganizado en layout de 2 columnas (LEFT/RIGHT, sin centro) - Añadidos getters públicos en Engine para info de sistema - changeScenario() ahora preserva el SimulationMode actual - Inicialización de pelotas según modo (PHYSICS/SHAPE/BOIDS) - Eliminada duplicación de logo_entered_manually_ (ahora en StateManager) ARCHIVOS MODIFICADOS: - engine.h: Añadidos 8 getters públicos para UIManager - engine.cpp: changeScenario() pasa current_mode_ a SceneManager - scene_manager.h: changeScenario() acepta parámetro SimulationMode - scene_manager.cpp: Inicialización según modo (RULES.md líneas 23-26) - ui_manager.h: render() acepta Engine* y renderDebugHUD() actualizado - ui_manager.cpp: Debug HUD con columnas LEFT (sistema) y RIGHT (física) REGLAS.md IMPLEMENTADO: ✅ Líneas 23-26: Inicialización diferenciada por modo - PHYSICS: Top, 75% distribución central en X, velocidades aleatorias - SHAPE: Centro de pantalla, sin velocidad inicial - BOIDS: Posiciones y velocidades aleatorias ✅ Líneas 88-96: Debug HUD con información de sistema completa BUGS CORREGIDOS: - Fix: Cambiar escenario (1-8) en FIGURE ya no resetea a PHYSICS ❌→✅ - Fix: Las pelotas se inicializan correctamente según el modo activo - Fix: AppMode movido de centro a izquierda en Debug HUD 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- source/engine.cpp | 33 +++-- source/engine.h | 16 ++- source/scene/scene_manager.cpp | 55 ++++++-- source/scene/scene_manager.h | 3 +- source/ui/ui_manager.cpp | 226 ++++++++++++++++++++++----------- source/ui/ui_manager.h | 7 +- 6 files changed, 239 insertions(+), 101 deletions(-) diff --git a/source/engine.cpp b/source/engine.cpp index 6b19132..de871e6 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -340,10 +340,14 @@ void Engine::update() { // Gravedad y física void Engine::handleGravityToggle() { - // Si estamos en modo boids, salir a modo física primero + // Si estamos en modo boids, salir a modo física CON GRAVEDAD OFF + // Según RULES.md: "BOIDS a PHYSICS: Pulsando la tecla G: Gravedad OFF" if (current_mode_ == SimulationMode::BOIDS) { - toggleBoidsMode(); // Esto cambia a PHYSICS y activa gravedad - return; // La notificación ya se muestra en toggleBoidsMode + toggleBoidsMode(); // Cambiar a PHYSICS (preserva inercia, gravedad ya está OFF desde activateBoids) + // NO llamar a forceBallsGravityOff() porque aplica impulsos que destruyen la inercia de BOIDS + // La gravedad ya está desactivada por BoidManager::activateBoids() y se mantiene al salir + showNotificationForAction("Modo Física - Gravedad Off"); + return; } // Si estamos en modo figura, salir a modo física SIN GRAVEDAD @@ -504,13 +508,14 @@ void Engine::switchTexture() { // Escenarios (número de pelotas) void Engine::changeScenario(int scenario_id, const char* notification_text) { - // Resetear modo SHAPE si está activo + // Pasar el modo actual al SceneManager para inicialización correcta + scene_manager_->changeScenario(scenario_id, current_mode_); + + // Si estamos en modo SHAPE, regenerar la figura con nuevo número de pelotas if (current_mode_ == SimulationMode::SHAPE) { - current_mode_ = SimulationMode::PHYSICS; - active_shape_.reset(); + generateShape(); } - scene_manager_->changeScenario(scenario_id); showNotificationForAction(notification_text); } @@ -695,7 +700,7 @@ void Engine::render() { */ // Renderizar UI (debug HUD, texto obsoleto, notificaciones) - delegado a UIManager - ui_manager_->render(renderer_, scene_manager_.get(), current_mode_, state_manager_->getCurrentMode(), + ui_manager_->render(renderer_, this, scene_manager_.get(), current_mode_, state_manager_->getCurrentMode(), active_shape_.get(), shape_convergence_, physical_window_width_, physical_window_height_, current_screen_width_); @@ -769,7 +774,7 @@ void Engine::toggleRealFullscreen() { // Reinicar la escena con nueva resolución scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_); - scene_manager_->changeScenario(scene_manager_->getCurrentScenario()); + scene_manager_->changeScenario(scene_manager_->getCurrentScenario(), current_mode_); // Actualizar tamaño de pantalla para boids (wrapping boundaries) boid_manager_->updateScreenSize(current_screen_width_, current_screen_height_); @@ -794,7 +799,7 @@ void Engine::toggleRealFullscreen() { // Reinicar la escena con resolución original scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_); - scene_manager_->changeScenario(scene_manager_->getCurrentScenario()); + scene_manager_->changeScenario(scene_manager_->getCurrentScenario(), current_mode_); } } @@ -1154,7 +1159,7 @@ void Engine::performLogoAction(bool logo_waiting_for_flip) { // Solo salir automáticamente si la entrada a LOGO fue automática (desde DEMO) // No salir si el usuario entró manualmente con tecla K // Probabilidad de salir: 60% en cada acción → sale rápido (relación DEMO:LOGO = 6:1) - if (!logo_entered_manually_ && rand() % 100 < 60) { + if (!state_manager_->getLogoEnteredManually() && rand() % 100 < 60) { state_manager_->exitLogoMode(true); // Volver a DEMO/DEMO_LITE } } @@ -1313,7 +1318,7 @@ void Engine::executeDemoAction(bool is_lite) { // Escenarios válidos: índices 1, 2, 3, 4, 5 (10, 100, 500, 1000, 10000 pelotas) int valid_scenarios[] = {1, 2, 3, 4, 5}; int new_scenario = valid_scenarios[rand() % 5]; - scene_manager_->changeScenario(new_scenario); + scene_manager_->changeScenario(new_scenario, current_mode_); return; } @@ -1398,7 +1403,7 @@ void Engine::executeRandomizeOnDemoStart(bool is_lite) { // 1. Escenario (excluir índices 0, 6, 7) int valid_scenarios[] = {1, 2, 3, 4, 5}; int new_scenario = valid_scenarios[rand() % 5]; - scene_manager_->changeScenario(new_scenario); + scene_manager_->changeScenario(new_scenario, current_mode_); // 2. Tema (elegir entre TODOS los 15 temas) int random_theme_index = rand() % 15; @@ -1463,7 +1468,7 @@ void Engine::executeEnterLogoMode(size_t ball_count) { // Verificar mínimo de pelotas if (static_cast(ball_count) < LOGO_MODE_MIN_BALLS) { // Ajustar a 5000 pelotas automáticamente - scene_manager_->changeScenario(5); // Escenario 5000 pelotas (índice 5 en BALL_COUNT_SCENARIOS) + scene_manager_->changeScenario(5, current_mode_); // Escenario 5000 pelotas (índice 5 en BALL_COUNT_SCENARIOS) } // Guardar estado previo (para restaurar al salir) diff --git a/source/engine.h b/source/engine.h index 3eaa5b9..0b0ad0a 100644 --- a/source/engine.h +++ b/source/engine.h @@ -87,6 +87,16 @@ class Engine { void executeEnterLogoMode(size_t ball_count); void executeExitLogoMode(); + // === Getters públicos para UIManager (Debug HUD) === + bool getVSyncEnabled() const { return vsync_enabled_; } + bool getFullscreenEnabled() const { return fullscreen_enabled_; } + bool getRealFullscreenEnabled() const { return real_fullscreen_enabled_; } + ScalingMode getCurrentScalingMode() const { return current_scaling_mode_; } + int getCurrentScreenWidth() const { return current_screen_width_; } + int getCurrentScreenHeight() const { return current_screen_height_; } + int getBaseScreenWidth() const { return base_screen_width_; } + int getBaseScreenHeight() const { return base_screen_height_; } + private: // === Componentes del sistema (Composición) === std::unique_ptr input_handler_; // Manejo de entradas SDL @@ -168,9 +178,9 @@ class Engine { float logo_target_flip_percentage_ = 0.0f; // % de flip a esperar (0.2-0.8) int logo_current_flip_count_ = 0; // Flips observados hasta ahora - // Control de entrada manual vs automática a LOGO MODE - // Determina si LOGO debe salir automáticamente o esperar input del usuario - bool logo_entered_manually_ = false; // true si se activó con tecla K, false si automático desde DEMO + // NOTA: logo_entered_manually_ fue eliminado de Engine (duplicado) + // Ahora se obtiene de StateManager con state_manager_->getLogoEnteredManually() + // Esto evita desincronización entre Engine y StateManager // Estado previo antes de entrar a Logo Mode (para restaurar al salir) // Guardado por executeEnterLogoMode(), restaurado por executeExitLogoMode() diff --git a/source/scene/scene_manager.cpp b/source/scene/scene_manager.cpp index cd5d444..b1674c0 100644 --- a/source/scene/scene_manager.cpp +++ b/source/scene/scene_manager.cpp @@ -22,8 +22,8 @@ void SceneManager::initialize(int scenario, std::shared_ptr texture, Th theme_manager_ = theme_manager; current_ball_size_ = texture_->getWidth(); - // Crear bolas iniciales - changeScenario(scenario_); + // Crear bolas iniciales (siempre en modo PHYSICS al inicio) + changeScenario(scenario_, SimulationMode::PHYSICS); } void SceneManager::update(float delta_time) { @@ -33,7 +33,7 @@ void SceneManager::update(float delta_time) { } } -void SceneManager::changeScenario(int scenario_id) { +void SceneManager::changeScenario(int scenario_id, SimulationMode mode) { // Guardar escenario scenario_ = scenario_id; @@ -45,14 +45,49 @@ void SceneManager::changeScenario(int scenario_id) { // Crear las bolas según el escenario for (int i = 0; i < BALL_COUNT_SCENARIOS[scenario_id]; ++i) { - const int SIGN = ((rand() % 2) * 2) - 1; // Genera un signo aleatorio (+ o -) + float X, VX, VY; - // Calcular spawn zone: margen a cada lado, zona central para spawn - const int margin = static_cast(screen_width_ * BALL_SPAWN_MARGIN); - const int spawn_zone_width = screen_width_ - (2 * margin); - const float X = (rand() % spawn_zone_width) + margin; // Posición inicial en X - const float VX = (((rand() % 20) + 10) * 0.1f) * SIGN; // Velocidad en X - const float VY = ((rand() % 60) - 30) * 0.1f; // Velocidad en Y + // Inicialización según SimulationMode (RULES.md líneas 23-26) + switch (mode) { + case SimulationMode::PHYSICS: { + // PHYSICS: Parte superior, 75% distribución central en X + const int SIGN = ((rand() % 2) * 2) - 1; + const int margin = static_cast(screen_width_ * BALL_SPAWN_MARGIN); + const int spawn_zone_width = screen_width_ - (2 * margin); + X = (rand() % spawn_zone_width) + margin; + VX = (((rand() % 20) + 10) * 0.1f) * SIGN; + VY = ((rand() % 60) - 30) * 0.1f; + break; + } + + case SimulationMode::SHAPE: { + // SHAPE: Centro de pantalla, sin velocidad inicial + X = screen_width_ / 2.0f; + VX = 0.0f; + VY = 0.0f; + break; + } + + case SimulationMode::BOIDS: { + // BOIDS: Posiciones aleatorias, velocidades aleatorias + const int SIGN_X = ((rand() % 2) * 2) - 1; + const int SIGN_Y = ((rand() % 2) * 2) - 1; + X = static_cast(rand() % screen_width_); + VX = (((rand() % 40) + 10) * 0.1f) * SIGN_X; // 1.0 - 5.0 px/frame + VY = (((rand() % 40) + 10) * 0.1f) * SIGN_Y; + break; + } + + default: + // Fallback a PHYSICS por seguridad + const int SIGN = ((rand() % 2) * 2) - 1; + const int margin = static_cast(screen_width_ * BALL_SPAWN_MARGIN); + const int spawn_zone_width = screen_width_ - (2 * margin); + X = (rand() % spawn_zone_width) + margin; + VX = (((rand() % 20) + 10) * 0.1f) * SIGN; + VY = ((rand() % 60) - 30) * 0.1f; + break; + } // Seleccionar color de la paleta del tema actual (delegado a ThemeManager) int random_index = rand(); diff --git a/source/scene/scene_manager.h b/source/scene/scene_manager.h index 95969f9..01147b6 100644 --- a/source/scene/scene_manager.h +++ b/source/scene/scene_manager.h @@ -51,8 +51,9 @@ class SceneManager { /** * @brief Cambia el número de bolas según escenario * @param scenario_id Índice del escenario (0-7 para 10 a 50,000 bolas) + * @param mode Modo de simulación actual (afecta inicialización) */ - void changeScenario(int scenario_id); + void changeScenario(int scenario_id, SimulationMode mode); /** * @brief Actualiza textura y tamaño de todas las bolas diff --git a/source/ui/ui_manager.cpp b/source/ui/ui_manager.cpp index 6d6dc70..b3c0b06 100644 --- a/source/ui/ui_manager.cpp +++ b/source/ui/ui_manager.cpp @@ -5,6 +5,7 @@ #include "../ball.h" // for Ball #include "../defines.h" // for TEXT_DURATION, NOTIFICATION_DURATION, AppMode, SimulationMode +#include "../engine.h" // for Engine (info de sistema) #include "../scene/scene_manager.h" // for SceneManager #include "../shapes/shape.h" // for Shape #include "../text/textrenderer.h" // for TextRenderer @@ -96,6 +97,7 @@ void UIManager::update(Uint64 current_time, float delta_time) { } void UIManager::render(SDL_Renderer* renderer, + const Engine* engine, const SceneManager* scene_manager, SimulationMode current_mode, AppMode current_app_mode, @@ -115,7 +117,7 @@ void UIManager::render(SDL_Renderer* renderer, // Renderizar debug HUD si está activo if (show_debug_) { - renderDebugHUD(scene_manager, current_mode, current_app_mode, + renderDebugHUD(engine, scene_manager, current_mode, current_app_mode, active_shape, shape_convergence); } @@ -167,7 +169,8 @@ void UIManager::setTextObsolete(const std::string& text, int pos, int current_sc // === Métodos privados === -void UIManager::renderDebugHUD(const SceneManager* scene_manager, +void UIManager::renderDebugHUD(const Engine* engine, + const SceneManager* scene_manager, SimulationMode current_mode, AppMode current_app_mode, const Shape* active_shape, @@ -175,92 +178,171 @@ void UIManager::renderDebugHUD(const SceneManager* scene_manager, // Obtener altura de línea para espaciado dinámico int line_height = text_renderer_debug_->getTextHeight(); int margin = 8; // Margen constante en píxeles físicos - int current_y = margin; // Y inicial en píxeles físicos - // Mostrar contador de FPS en esquina superior derecha + // =========================== + // COLUMNA LEFT (Sistema) + // =========================== + int left_y = margin; + + // AppMode (antes estaba centrado, ahora va a la izquierda) + std::string appmode_text; + SDL_Color appmode_color = {255, 255, 255, 255}; // Blanco por defecto + + if (current_app_mode == AppMode::LOGO) { + appmode_text = "AppMode: LOGO"; + appmode_color = {255, 128, 0, 255}; // Naranja + } else if (current_app_mode == AppMode::DEMO) { + appmode_text = "AppMode: DEMO"; + appmode_color = {255, 165, 0, 255}; // Naranja + } else if (current_app_mode == AppMode::DEMO_LITE) { + appmode_text = "AppMode: DEMO LITE"; + appmode_color = {255, 200, 0, 255}; // Amarillo-naranja + } else { + 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; + if (current_mode == SimulationMode::PHYSICS) { + simmode_text = "SimMode: PHYSICS"; + } else if (current_mode == SimulationMode::SHAPE) { + if (active_shape) { + simmode_text = std::string("SimMode: SHAPE (") + active_shape->getName() + ")"; + } else { + simmode_text = "SimMode: SHAPE"; + } + } else if (current_mode == SimulationMode::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; + + // 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; + if (engine->getFullscreenEnabled() || engine->getRealFullscreenEnabled()) { + ScalingMode scaling = engine->getCurrentScalingMode(); + if (scaling == ScalingMode::INTEGER) { + scaling_text = "Scaling: INTEGER"; + } else if (scaling == ScalingMode::LETTERBOX) { + scaling_text = "Scaling: LETTERBOX"; + } else if (scaling == ScalingMode::STRETCH) { + scaling_text = "Scaling: STRETCH"; + } + } else { + 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_); + 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()); + 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; + int num_displays = 0; + SDL_DisplayID* displays = SDL_GetDisplays(&num_displays); + if (displays && num_displays > 0) { + const auto* dm = SDL_GetCurrentDisplayMode(displays[0]); + if (dm) { + refresh_text = "Refresh: " + std::to_string(static_cast(dm->refresh_rate)) + " Hz"; + } else { + refresh_text = "Refresh: N/A"; + } + SDL_free(displays); + } else { + 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(); + text_renderer_debug_->printAbsolute(margin, left_y, theme_text.c_str(), {128, 255, 255, 255}); // Cian claro + left_y += line_height; + + // =========================== + // COLUMNA RIGHT (Primera pelota) + // =========================== + int right_y = margin; + + // FPS counter (esquina superior derecha) int fps_text_width = text_renderer_debug_->getTextWidthPhysical(fps_text_.c_str()); int fps_x = physical_window_width_ - fps_text_width - margin; - text_renderer_debug_->printAbsolute(fps_x, current_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; - // Mostrar estado V-Sync en esquina superior izquierda - text_renderer_debug_->printAbsolute(margin, current_y, vsync_text_.c_str(), {0, 255, 255, 255}); // Cian - current_y += line_height; - - // Debug: Mostrar valores de la primera pelota (si existe) + // Info de la primera pelota (si existe) const Ball* first_ball = scene_manager->getFirstBall(); if (first_ball != nullptr) { - // Línea 1: Gravedad - int grav_int = static_cast(first_ball->getGravityForce()); - std::string grav_text = "Gravedad: " + std::to_string(grav_int); - text_renderer_debug_->printAbsolute(margin, current_y, grav_text.c_str(), {255, 0, 255, 255}); // Magenta - current_y += line_height; + // Posición X, Y + SDL_FRect pos = first_ball->getPosition(); + std::string pos_text = "Pos: (" + std::to_string(static_cast(pos.x)) + ", " + std::to_string(static_cast(pos.y)) + ")"; + 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 + right_y += line_height; - // Línea 2: Velocidad Y + // Velocidad X + int vx_int = static_cast(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_window_width_ - 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(first_ball->getVelocityY()); - std::string vy_text = "Velocidad Y: " + std::to_string(vy_int); - text_renderer_debug_->printAbsolute(margin, current_y, vy_text.c_str(), {255, 0, 255, 255}); // Magenta - current_y += line_height; + 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_window_width_ - vy_width - margin, right_y, vy_text.c_str(), {128, 255, 128, 255}); // Verde claro + right_y += line_height; - // Línea 3: Estado superficie - std::string surface_text = first_ball->isOnSurface() ? "Superficie: Sí" : "Superficie: No"; - text_renderer_debug_->printAbsolute(margin, current_y, surface_text.c_str(), {255, 0, 255, 255}); // Magenta - current_y += line_height; + // Fuerza de gravedad + int grav_int = static_cast(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_window_width_ - grav_width - margin, right_y, grav_text.c_str(), {255, 255, 128, 255}); // Amarillo claro + right_y += line_height; - // Línea 4: Coeficiente de rebote (loss) + // 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_window_width_ - 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 = "Rebote: " + std::to_string(loss_val).substr(0, 4); - text_renderer_debug_->printAbsolute(margin, current_y, loss_text.c_str(), {255, 0, 255, 255}); // Magenta - current_y += line_height; + 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_window_width_ - loss_width - margin, right_y, loss_text.c_str(), {255, 128, 255, 255}); // Magenta + right_y += line_height; - // Línea 5: Dirección de gravedad - std::string gravity_dir_text = "Dirección: " + gravityDirectionToString(static_cast(scene_manager->getCurrentGravity())); - text_renderer_debug_->printAbsolute(margin, current_y, gravity_dir_text.c_str(), {255, 255, 0, 255}); // Amarillo - current_y += line_height; + // Dirección de gravedad + std::string gravity_dir_text = "Dir: " + gravityDirectionToString(static_cast(scene_manager->getCurrentGravity())); + 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 + right_y += line_height; } - // Debug: Mostrar tema actual (delegado a ThemeManager) - std::string theme_text = std::string("Tema: ") + theme_manager_->getCurrentThemeNameEN(); - text_renderer_debug_->printAbsolute(margin, current_y, theme_text.c_str(), {255, 255, 128, 255}); // Amarillo claro - current_y += line_height; - - // Debug: Mostrar modo de simulación actual - std::string mode_text; - if (current_mode == SimulationMode::PHYSICS) { - mode_text = "Modo: Física"; - } else if (active_shape) { - mode_text = std::string("Modo: ") + active_shape->getName(); - } else { - mode_text = "Modo: Forma"; - } - text_renderer_debug_->printAbsolute(margin, current_y, mode_text.c_str(), {0, 255, 128, 255}); // Verde claro - current_y += line_height; - - // Debug: Mostrar convergencia en modo LOGO (solo cuando está activo) + // Convergencia en modo LOGO (solo cuando está activo) - Parte inferior derecha if (current_app_mode == AppMode::LOGO && current_mode == SimulationMode::SHAPE) { int convergence_percent = static_cast(shape_convergence * 100.0f); - std::string convergence_text = "Convergencia: " + std::to_string(convergence_percent) + "%"; - text_renderer_debug_->printAbsolute(margin, current_y, convergence_text.c_str(), {255, 128, 0, 255}); // Naranja - current_y += line_height; - } - - // Debug: Mostrar modo DEMO/LOGO activo (siempre visible cuando debug está ON) - // FIJO en tercera fila (no se mueve con otros elementos del HUD) - int fixed_y = margin + (line_height * 2); // Tercera fila fija - if (current_app_mode == AppMode::LOGO) { - const char* logo_text = "Modo Logo"; - int logo_text_width = text_renderer_debug_->getTextWidthPhysical(logo_text); - int logo_x = (physical_window_width_ - logo_text_width) / 2; - text_renderer_debug_->printAbsolute(logo_x, fixed_y, logo_text, {255, 128, 0, 255}); // Naranja - } else if (current_app_mode == AppMode::DEMO) { - const char* demo_text = "Modo Demo"; - int demo_text_width = text_renderer_debug_->getTextWidthPhysical(demo_text); - int demo_x = (physical_window_width_ - demo_text_width) / 2; - text_renderer_debug_->printAbsolute(demo_x, fixed_y, demo_text, {255, 165, 0, 255}); // Naranja - } else if (current_app_mode == AppMode::DEMO_LITE) { - const char* lite_text = "Modo Demo Lite"; - int lite_text_width = text_renderer_debug_->getTextWidthPhysical(lite_text); - int lite_x = (physical_window_width_ - lite_text_width) / 2; - text_renderer_debug_->printAbsolute(lite_x, fixed_y, lite_text, {255, 200, 0, 255}); // Amarillo-naranja + 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(physical_window_width_ - conv_width - margin, right_y, convergence_text.c_str(), {255, 128, 0, 255}); // Naranja + right_y += line_height; } } diff --git a/source/ui/ui_manager.h b/source/ui/ui_manager.h index ec677b8..8fd044c 100644 --- a/source/ui/ui_manager.h +++ b/source/ui/ui_manager.h @@ -11,6 +11,7 @@ class ThemeManager; class TextRenderer; class Notifier; class HelpOverlay; +class Engine; enum class SimulationMode; enum class AppMode; @@ -59,6 +60,7 @@ class UIManager { /** * @brief Renderiza todos los elementos UI * @param renderer Renderizador SDL3 + * @param engine Puntero a Engine (para info de sistema) * @param scene_manager SceneManager (para info de debug) * @param current_mode Modo de simulación actual (PHYSICS/SHAPE) * @param current_app_mode Modo de aplicación (SANDBOX/DEMO/LOGO) @@ -69,6 +71,7 @@ class UIManager { * @param current_screen_width Ancho lógico de pantalla (para texto centrado) */ void render(SDL_Renderer* renderer, + const Engine* engine, const SceneManager* scene_manager, SimulationMode current_mode, AppMode current_app_mode, @@ -136,13 +139,15 @@ class UIManager { private: /** * @brief Renderiza HUD de debug (solo si show_debug_ == true) + * @param engine Puntero a Engine (para info de sistema) * @param scene_manager SceneManager (para info de pelotas) * @param current_mode Modo de simulación (PHYSICS/SHAPE) * @param current_app_mode Modo de aplicación (SANDBOX/DEMO/LOGO) * @param active_shape Figura 3D activa (puede ser nullptr) * @param shape_convergence % de convergencia en LOGO mode */ - void renderDebugHUD(const SceneManager* scene_manager, + void renderDebugHUD(const Engine* engine, + const SceneManager* scene_manager, SimulationMode current_mode, AppMode current_app_mode, const Shape* active_shape,