diff --git a/source/ball.cpp b/source/ball.cpp index c4db21a..32fac57 100644 --- a/source/ball.cpp +++ b/source/ball.cpp @@ -22,7 +22,7 @@ float generateLateralLoss() { } // Constructor -Ball::Ball(float x, float vx, float vy, Color color, std::shared_ptr texture, GravityDirection gravity_dir, float mass_factor) +Ball::Ball(float x, float vx, float vy, Color color, std::shared_ptr texture, int screen_width, int screen_height, GravityDirection gravity_dir, float mass_factor) : sprite_(std::make_unique(texture)), pos_({x, 0.0f, BALL_SIZE, BALL_SIZE}) { // Convertir velocidades de píxeles/frame a píxeles/segundo (multiplicar por 60) @@ -36,6 +36,8 @@ Ball::Ball(float x, float vx, float vy, Color color, std::shared_ptr te gravity_force_ = GRAVITY_FORCE * 60.0f * 60.0f; gravity_mass_factor_ = mass_factor; // Factor de masa individual para esta pelota gravity_direction_ = gravity_dir; + screen_width_ = screen_width; // Dimensiones del terreno de juego + screen_height_ = screen_height; on_surface_ = false; stopped_ = false; // Coeficiente base IGUAL para todas las pelotas (solo variación por rebote individual) @@ -76,7 +78,7 @@ void Ball::update(float deltaTime) { // Si está en superficie, mantener posición según dirección de gravedad switch (gravity_direction_) { case GravityDirection::DOWN: - pos_.y = SCREEN_HEIGHT - pos_.h; + pos_.y = screen_height_ - pos_.h; pos_.x += vx_ * deltaTime; // Seguir moviéndose en X break; case GravityDirection::UP: @@ -88,7 +90,7 @@ void Ball::update(float deltaTime) { pos_.y += vy_ * deltaTime; // Seguir moviéndose en Y break; case GravityDirection::RIGHT: - pos_.x = SCREEN_WIDTH - pos_.w; + pos_.x = screen_width_ - pos_.w; pos_.y += vy_ * deltaTime; // Seguir moviéndose en Y break; } @@ -113,8 +115,8 @@ void Ball::update(float deltaTime) { } // Comprueba las colisiones con el lateral derecho - if (pos_.x + pos_.w > SCREEN_WIDTH) { - pos_.x = SCREEN_WIDTH - pos_.w; + if (pos_.x + pos_.w > screen_width_) { + pos_.x = screen_width_ - pos_.w; if (gravity_direction_ == GravityDirection::RIGHT) { // Colisión con superficie de gravedad - aplicar variación aleatoria vx_ = -vx_ * loss_ * generateBounceVariation(); @@ -149,8 +151,8 @@ void Ball::update(float deltaTime) { } // Comprueba las colisiones con la parte inferior - if (pos_.y + pos_.h > SCREEN_HEIGHT) { - pos_.y = SCREEN_HEIGHT - pos_.h; + if (pos_.y + pos_.h > screen_height_) { + pos_.y = screen_height_ - pos_.h; if (gravity_direction_ == GravityDirection::DOWN) { // Colisión con superficie de gravedad - aplicar variación aleatoria vy_ = -vy_ * loss_ * generateBounceVariation(); diff --git a/source/ball.h b/source/ball.h index f971e7a..2aac468 100644 --- a/source/ball.h +++ b/source/ball.h @@ -16,6 +16,8 @@ class Ball { float gravity_force_; // Gravedad base float gravity_mass_factor_; // Factor de masa individual (0.7-1.3, afecta gravedad) GravityDirection gravity_direction_; // Direcci\u00f3n de la gravedad + int screen_width_; // Ancho del terreno de juego + int screen_height_; // Alto del terreno de juego Color color_; // Color de la pelota bool on_surface_; // Indica si la pelota est\u00e1 en la superficie (suelo/techo/pared) bool stopped_; // Indica si la pelota ha terminado de moverse; @@ -23,7 +25,7 @@ class Ball { public: // Constructor - Ball(float x, float vx, float vy, Color color, std::shared_ptr texture, GravityDirection gravity_dir = GravityDirection::DOWN, float mass_factor = 1.0f); + Ball(float x, float vx, float vy, Color color, std::shared_ptr texture, int screen_width, int screen_height, GravityDirection gravity_dir = GravityDirection::DOWN, float mass_factor = 1.0f); // Destructor ~Ball() = default; diff --git a/source/defines.h b/source/defines.h index 6d28913..6e7fc0d 100644 --- a/source/defines.h +++ b/source/defines.h @@ -3,36 +3,39 @@ // Configuración de ventana y pantalla constexpr char WINDOW_CAPTION[] = "vibe3_physics"; -constexpr int SCREEN_WIDTH = 640; // Ancho de la pantalla lógica (píxeles) -constexpr int SCREEN_HEIGHT = 480; // Alto de la pantalla lógica (píxeles) -constexpr int WINDOW_ZOOM = 2; // Zoom inicial de la ventana -constexpr int BALL_SIZE = 10; // Tamaño de las pelotas (píxeles) +constexpr int SCREEN_WIDTH = 640; // Ancho de la pantalla lógica (píxeles) +constexpr int SCREEN_HEIGHT = 360; // Alto de la pantalla lógica (píxeles) +constexpr int WINDOW_ZOOM = 2; // Zoom inicial de la ventana +constexpr int BALL_SIZE = 10; // Tamaño de las pelotas (píxeles) // Configuración de zoom dinámico de ventana -constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240) -constexpr int WINDOW_ZOOM_MAX = 10; // Zoom máximo teórico (3200x2400) -constexpr int WINDOW_DESKTOP_MARGIN = 10; // Margen mínimo con bordes del escritorio -constexpr int WINDOW_DECORATION_HEIGHT = 30; // Altura estimada de decoraciones del SO +constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240) +constexpr int WINDOW_ZOOM_MAX = 10; // Zoom máximo teórico (3200x2400) +constexpr int WINDOW_DESKTOP_MARGIN = 10; // Margen mínimo con bordes del escritorio +constexpr int WINDOW_DECORATION_HEIGHT = 30; // Altura estimada de decoraciones del SO // Configuración de física -constexpr float GRAVITY_FORCE = 0.2f; // Fuerza de gravedad (píxeles/frame²) +constexpr float GRAVITY_FORCE = 0.2f; // Fuerza de gravedad (píxeles/frame²) // Configuración de interfaz -constexpr Uint64 TEXT_DURATION = 2000; // Duración del texto informativo (ms) +constexpr Uint64 TEXT_DURATION = 2000; // Duración del texto informativo (ms) // Configuración de pérdida aleatoria en rebotes -constexpr float BASE_BOUNCE_COEFFICIENT = 0.75f; // Coeficiente base IGUAL para todas las pelotas -constexpr float BOUNCE_RANDOM_LOSS_PERCENT = 0.1f; // 0-10% pérdida adicional aleatoria en cada rebote -constexpr float LATERAL_LOSS_PERCENT = 0.02f; // ±2% pérdida lateral en rebotes +constexpr float BASE_BOUNCE_COEFFICIENT = 0.75f; // Coeficiente base IGUAL para todas las pelotas +constexpr float BOUNCE_RANDOM_LOSS_PERCENT = 0.1f; // 0-10% pérdida adicional aleatoria en cada rebote +constexpr float LATERAL_LOSS_PERCENT = 0.02f; // ±2% pérdida lateral en rebotes // Configuración de masa/peso individual por pelota -constexpr float GRAVITY_MASS_MIN = 0.7f; // Factor mínimo de masa (pelota ligera - 70% gravedad) -constexpr float GRAVITY_MASS_MAX = 1.3f; // Factor máximo de masa (pelota pesada - 130% gravedad) +constexpr float GRAVITY_MASS_MIN = 0.7f; // Factor mínimo de masa (pelota ligera - 70% gravedad) +constexpr float GRAVITY_MASS_MAX = 1.3f; // Factor máximo de masa (pelota pesada - 130% gravedad) // Configuración de velocidad lateral al cambiar gravedad (muy sutil) constexpr float GRAVITY_CHANGE_LATERAL_MIN = 0.04f; // Velocidad lateral mínima (2.4 px/s) constexpr float GRAVITY_CHANGE_LATERAL_MAX = 0.08f; // Velocidad lateral máxima (4.8 px/s) +// Configuración de spawn inicial de pelotas +constexpr float BALL_SPAWN_MARGIN = 0.15f; // Margen lateral para spawn (0.25 = 25% a cada lado) + // Estructura para representar colores RGB struct Color { int r, g, b; // Componentes rojo, verde, azul (0-255) @@ -40,10 +43,10 @@ struct Color { // Enum para dirección de gravedad enum class GravityDirection { - DOWN, // ↓ Gravedad hacia abajo (por defecto) - UP, // ↑ Gravedad hacia arriba - LEFT, // ← Gravedad hacia la izquierda - RIGHT // → Gravedad hacia la derecha + DOWN, // ↓ Gravedad hacia abajo (por defecto) + UP, // ↑ Gravedad hacia arriba + LEFT, // ← Gravedad hacia la izquierda + RIGHT // → Gravedad hacia la derecha }; // Enum para temas de colores (seleccionables con teclado numérico) diff --git a/source/engine.cpp b/source/engine.cpp index 801a8e5..4b6d387 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -264,19 +264,28 @@ void Engine::handleEvents() { initBalls(scenario_); break; - // Controles de zoom dinámico + // Controles de zoom dinámico (solo si no estamos en fullscreen) case SDLK_F1: - zoomOut(); + if (!fullscreen_enabled_ && !real_fullscreen_enabled_) { + zoomOut(); + } break; case SDLK_F2: - zoomIn(); + if (!fullscreen_enabled_ && !real_fullscreen_enabled_) { + zoomIn(); + } break; // Control de pantalla completa case SDLK_F3: toggleFullscreen(); break; + + // Modo real fullscreen (cambia resolución interna) + case SDLK_F4: + toggleRealFullscreen(); + break; } } } @@ -310,7 +319,7 @@ void Engine::render() { std::string theme_names_es[] = {"ATARDECER", "OCEANO", "NEON", "BOSQUE"}; std::string theme_name = theme_names_es[static_cast(current_theme_)]; int theme_text_width = static_cast(theme_name.length() * 8); // 8 píxeles por carácter - int theme_x = (SCREEN_WIDTH - theme_text_width) / 2; // Centrar horizontalmente + int theme_x = (current_screen_width_ - theme_text_width) / 2; // Centrar horizontalmente // Colores acordes a cada tema int theme_colors[][3] = { @@ -327,7 +336,7 @@ void Engine::render() { if (show_debug_) { // Mostrar contador de FPS en esquina superior derecha int fps_text_width = static_cast(fps_text_.length() * 8); // 8 píxeles por carácter - int fps_x = SCREEN_WIDTH - fps_text_width - 8; // 8 píxeles de margen + int fps_x = current_screen_width_ - fps_text_width - 8; // 8 píxeles de margen dbg_print(fps_x, 8, fps_text_.c_str(), 255, 255, 0); // Amarillo para distinguir // Mostrar estado V-Sync en esquina superior izquierda @@ -378,7 +387,10 @@ void Engine::initBalls(int value) { // Crear las bolas según el escenario for (int i = 0; i < test_.at(value); ++i) { const int SIGN = ((rand() % 2) * 2) - 1; // Genera un signo aleatorio (+ o -) - const float X = (rand() % (SCREEN_WIDTH / 2)) + (SCREEN_WIDTH / 4); // Posición inicial en X + // Calcular spawn zone: margen a cada lado, zona central para spawn + const int margin = static_cast(current_screen_width_ * BALL_SPAWN_MARGIN); + const int spawn_zone_width = current_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 // Seleccionar color de la paleta del tema actual @@ -389,7 +401,7 @@ void Engine::initBalls(int value) { theme.ball_colors[color_index][2]}; // Generar factor de masa aleatorio (0.7 = ligera, 1.3 = pesada) float mass_factor = GRAVITY_MASS_MIN + (rand() % 1000) / 1000.0f * (GRAVITY_MASS_MAX - GRAVITY_MASS_MIN); - balls_.emplace_back(std::make_unique(X, VX, VY, COLOR, texture_, current_gravity_, mass_factor)); + balls_.emplace_back(std::make_unique(X, VX, VY, COLOR, texture_, current_screen_width_, current_screen_height_, current_gravity_, mass_factor)); } setText(); // Actualiza el texto } @@ -401,7 +413,7 @@ void Engine::setText() { } else { text_ = std::to_string(num_balls) + " PELOTAS"; } - text_pos_ = (SCREEN_WIDTH - static_cast(text_.length() * 8)) / 2; // Centrar texto + text_pos_ = (current_screen_width_ - static_cast(text_.length() * 8)) / 2; // Centrar texto show_text_ = true; text_init_time_ = SDL_GetTicks(); } @@ -438,10 +450,64 @@ void Engine::toggleVSync() { } void Engine::toggleFullscreen() { + // Si está en modo real fullscreen, primero salir de él + if (real_fullscreen_enabled_) { + toggleRealFullscreen(); // Esto lo desactiva + } + fullscreen_enabled_ = !fullscreen_enabled_; SDL_SetWindowFullscreen(window_, fullscreen_enabled_); } +void Engine::toggleRealFullscreen() { + // Si está en modo fullscreen normal, primero desactivarlo + if (fullscreen_enabled_) { + fullscreen_enabled_ = false; + SDL_SetWindowFullscreen(window_, false); + } + + real_fullscreen_enabled_ = !real_fullscreen_enabled_; + + if (real_fullscreen_enabled_) { + // Obtener resolución del escritorio + int num_displays = 0; + SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); + if (displays != nullptr && num_displays > 0) { + const auto *dm = SDL_GetCurrentDisplayMode(displays[0]); + if (dm != nullptr) { + // Cambiar a resolución nativa del escritorio + current_screen_width_ = dm->w; + current_screen_height_ = dm->h; + + // Recrear ventana con nueva resolución + SDL_SetWindowSize(window_, current_screen_width_, current_screen_height_); + SDL_SetWindowFullscreen(window_, true); + + // Actualizar presentación lógica del renderizador + SDL_SetRenderLogicalPresentation(renderer_, current_screen_width_, current_screen_height_, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); + + // Reinicar la escena con nueva resolución + initBalls(scenario_); + } + SDL_free(displays); + } + } else { + // Volver a resolución original + current_screen_width_ = SCREEN_WIDTH; + current_screen_height_ = SCREEN_HEIGHT; + + // Restaurar ventana normal + SDL_SetWindowFullscreen(window_, false); + SDL_SetWindowSize(window_, SCREEN_WIDTH * WINDOW_ZOOM, SCREEN_HEIGHT * WINDOW_ZOOM); + + // Restaurar presentación lógica original + SDL_SetRenderLogicalPresentation(renderer_, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); + + // Reinicar la escena con resolución original + initBalls(scenario_); + } +} + std::string Engine::gravityDirectionToString(GravityDirection direction) const { switch (direction) { case GravityDirection::DOWN: return "DOWN"; @@ -473,17 +539,17 @@ void Engine::renderGradientBackground() { bg_vertices[0].color = {top_r, top_g, top_b, 1.0f}; // Vértice superior derecho - bg_vertices[1].position = {SCREEN_WIDTH, 0}; + bg_vertices[1].position = {static_cast(current_screen_width_), 0}; bg_vertices[1].tex_coord = {1.0f, 0.0f}; bg_vertices[1].color = {top_r, top_g, top_b, 1.0f}; // Vértice inferior derecho - bg_vertices[2].position = {SCREEN_WIDTH, SCREEN_HEIGHT}; + bg_vertices[2].position = {static_cast(current_screen_width_), static_cast(current_screen_height_)}; bg_vertices[2].tex_coord = {1.0f, 1.0f}; bg_vertices[2].color = {bottom_r, bottom_g, bottom_b, 1.0f}; // Vértice inferior izquierdo - bg_vertices[3].position = {0, SCREEN_HEIGHT}; + bg_vertices[3].position = {0, static_cast(current_screen_height_)}; bg_vertices[3].tex_coord = {0.0f, 1.0f}; bg_vertices[3].color = {bottom_r, bottom_g, bottom_b, 1.0f}; diff --git a/source/engine.h b/source/engine.h index 2136e9c..43937ad 100644 --- a/source/engine.h +++ b/source/engine.h @@ -56,6 +56,11 @@ private: bool vsync_enabled_ = true; std::string vsync_text_ = "VSYNC ON"; bool fullscreen_enabled_ = false; + bool real_fullscreen_enabled_ = false; + + // Resolución dinámica para modo real fullscreen + int current_screen_width_ = SCREEN_WIDTH; + int current_screen_height_ = SCREEN_HEIGHT; // Sistema de temas ColorTheme current_theme_ = ColorTheme::SUNSET; @@ -105,6 +110,7 @@ private: void changeGravityDirection(GravityDirection direction); void toggleVSync(); void toggleFullscreen(); + void toggleRealFullscreen(); std::string gravityDirectionToString(GravityDirection direction) const; // Sistema de zoom dinámico