Implementar sistema de zoom dinámico y fullscreen

- Agregar zoom dinámico de ventana con F1/F2
- F1: reducir zoom hasta 1x mínimo
- F2: aumentar zoom hasta máximo basado en resolución
- Centrado inteligente al cambiar zoom
- Cálculo correcto de zoom máximo usando SDL_GetCurrentDisplayMode()
- F3: toggle fullscreen entre ventana y pantalla completa
- Mover temas de colores de F1-F4 a teclado numérico (KP_1-4)
- Mejorar resolución base a 640x480 con zoom inicial 2x
- Resolver paths absolutos para data/ball.png

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-18 18:48:12 +02:00
parent 036268f135
commit 8a05f69442
3 changed files with 145 additions and 10 deletions

View File

@@ -2,10 +2,16 @@
constexpr char WINDOW_CAPTION[] = "vibe3_physics";
constexpr int SCREEN_WIDTH = 320;
constexpr int SCREEN_HEIGHT = 240;
constexpr int WINDOW_SIZE = 3;
constexpr int SCREEN_WIDTH = 640;
constexpr int SCREEN_HEIGHT = 480;
constexpr int WINDOW_ZOOM = 2; // Zoom inicial de la ventana
constexpr int BALL_SIZE = 10;
// 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 float GRAVITY_FORCE = 0.2f;
// DEMO_SPEED eliminado - ya no se usa con delta time

View File

@@ -6,17 +6,36 @@
#include <SDL3/SDL_keycode.h> // for SDL_Keycode
#include <SDL3/SDL_render.h> // for SDL_SetRenderDrawColor, SDL_RenderPresent
#include <SDL3/SDL_timer.h> // for SDL_GetTicks
#include <SDL3/SDL_video.h> // for SDL_CreateWindow, SDL_DestroyWindow
#include <SDL3/SDL_video.h> // for SDL_CreateWindow, SDL_DestroyWindow, SDL_GetDisplayBounds
#include <algorithm> // for std::min, std::max
#include <cstdlib> // for rand, srand
#include <ctime> // for time
#include <iostream> // for cout
#include <string> // for string
#include <filesystem> // for path operations
#ifdef _WIN32
#include <windows.h> // for GetModuleFileName
#endif
#include "ball.h" // for Ball
#include "external/dbgtxt.h" // for dbg_init, dbg_print
#include "external/texture.h" // for Texture
// Función auxiliar para obtener la ruta del directorio del ejecutable
std::string getExecutableDirectory() {
#ifdef _WIN32
char buffer[MAX_PATH];
GetModuleFileNameA(NULL, buffer, MAX_PATH);
std::filesystem::path exe_path(buffer);
return exe_path.parent_path().string();
#else
// Para Linux/macOS se podría usar readlink("/proc/self/exe") o dladdr
return "."; // Fallback para otros sistemas
#endif
}
// Implementación de métodos públicos
bool Engine::initialize() {
bool success = true;
@@ -26,7 +45,7 @@ bool Engine::initialize() {
success = false;
} else {
// Crear ventana principal
window_ = SDL_CreateWindow(WINDOW_CAPTION, SCREEN_WIDTH * WINDOW_SIZE, SCREEN_HEIGHT * WINDOW_SIZE, SDL_WINDOW_OPENGL);
window_ = SDL_CreateWindow(WINDOW_CAPTION, SCREEN_WIDTH * WINDOW_ZOOM, SCREEN_HEIGHT * WINDOW_ZOOM, SDL_WINDOW_OPENGL);
if (window_ == nullptr) {
std::cout << "¡No se pudo crear la ventana! Error de SDL: " << SDL_GetError() << std::endl;
success = false;
@@ -51,7 +70,10 @@ bool Engine::initialize() {
// Inicializar otros componentes si SDL se inicializó correctamente
if (success) {
texture_ = std::make_shared<Texture>(renderer_, "data/ball.png");
// Construir ruta absoluta a la imagen
std::string exe_dir = getExecutableDirectory();
std::string texture_path = exe_dir + "/data/ball.png";
texture_ = std::make_shared<Texture>(renderer_, texture_path);
srand(static_cast<unsigned>(time(nullptr)));
dbg_init(renderer_);
initBalls(scenario_);
@@ -181,22 +203,23 @@ void Engine::handleEvents() {
initBalls(scenario_); // Regenerar bolas con nueva paleta
break;
case SDLK_F1:
// Temas de colores con teclado numérico
case SDLK_KP_1:
current_theme_ = ColorTheme::SUNSET;
initBalls(scenario_);
break;
case SDLK_F2:
case SDLK_KP_2:
current_theme_ = ColorTheme::OCEAN;
initBalls(scenario_);
break;
case SDLK_F3:
case SDLK_KP_3:
current_theme_ = ColorTheme::NEON;
initBalls(scenario_);
break;
case SDLK_F4:
case SDLK_KP_4:
current_theme_ = ColorTheme::FOREST;
initBalls(scenario_);
break;
@@ -240,6 +263,20 @@ void Engine::handleEvents() {
scenario_ = 7;
initBalls(scenario_);
break;
// Controles de zoom dinámico
case SDLK_F1:
zoomOut();
break;
case SDLK_F2:
zoomIn();
break;
// Control de pantalla completa
case SDLK_F3:
toggleFullscreen();
break;
}
}
}
@@ -400,6 +437,11 @@ void Engine::toggleVSync() {
SDL_SetRenderVSync(renderer_, vsync_enabled_ ? 1 : 0);
}
void Engine::toggleFullscreen() {
fullscreen_enabled_ = !fullscreen_enabled_;
SDL_SetWindowFullscreen(window_, fullscreen_enabled_);
}
std::string Engine::gravityDirectionToString(GravityDirection direction) const {
switch (direction) {
case GravityDirection::DOWN: return "DOWN";
@@ -496,3 +538,79 @@ void Engine::addSpriteToBatch(float x, float y, float w, float h, int r, int g,
batch_indices_.push_back(vertex_index + 3);
batch_indices_.push_back(vertex_index + 0);
}
// Sistema de zoom dinámico
int Engine::calculateMaxWindowZoom() const {
// Obtener información del display usando el método de Coffee Crisis
int num_displays = 0;
SDL_DisplayID *displays = SDL_GetDisplays(&num_displays);
if (displays == nullptr || num_displays == 0) {
return WINDOW_ZOOM_MIN; // Fallback si no se puede obtener
}
// Obtener el modo de display actual
const auto *dm = SDL_GetCurrentDisplayMode(displays[0]);
if (dm == nullptr) {
SDL_free(displays);
return WINDOW_ZOOM_MIN;
}
// Calcular zoom máximo usando la fórmula de Coffee Crisis
const int MAX_ZOOM = std::min(dm->w / SCREEN_WIDTH, (dm->h - WINDOW_DECORATION_HEIGHT) / SCREEN_HEIGHT);
SDL_free(displays);
// Aplicar límites
return std::max(WINDOW_ZOOM_MIN, std::min(MAX_ZOOM, WINDOW_ZOOM_MAX));
}
void Engine::setWindowZoom(int new_zoom) {
// Validar zoom
int max_zoom = calculateMaxWindowZoom();
new_zoom = std::max(WINDOW_ZOOM_MIN, std::min(new_zoom, max_zoom));
if (new_zoom == current_window_zoom_) {
return; // No hay cambio
}
// Obtener posición actual del centro de la ventana
int current_x, current_y;
SDL_GetWindowPosition(window_, &current_x, &current_y);
int current_center_x = current_x + (SCREEN_WIDTH * current_window_zoom_) / 2;
int current_center_y = current_y + (SCREEN_HEIGHT * current_window_zoom_) / 2;
// Calcular nuevo tamaño
int new_width = SCREEN_WIDTH * new_zoom;
int new_height = SCREEN_HEIGHT * new_zoom;
// Calcular nueva posición (centrada en el punto actual)
int new_x = current_center_x - new_width / 2;
int new_y = current_center_y - new_height / 2;
// Obtener límites del escritorio para no salirse
SDL_Rect display_bounds;
if (SDL_GetDisplayBounds(SDL_GetPrimaryDisplay(), &display_bounds) == 0) {
// Aplicar márgenes
int min_x = WINDOW_DESKTOP_MARGIN;
int min_y = WINDOW_DESKTOP_MARGIN;
int max_x = display_bounds.w - new_width - WINDOW_DESKTOP_MARGIN;
int max_y = display_bounds.h - new_height - WINDOW_DESKTOP_MARGIN - WINDOW_DECORATION_HEIGHT;
// Limitar posición
new_x = std::max(min_x, std::min(new_x, max_x));
new_y = std::max(min_y, std::min(new_y, max_y));
}
// Aplicar cambios
SDL_SetWindowSize(window_, new_width, new_height);
SDL_SetWindowPosition(window_, new_x, new_y);
current_window_zoom_ = new_zoom;
}
void Engine::zoomIn() {
setWindowZoom(current_window_zoom_ + 1);
}
void Engine::zoomOut() {
setWindowZoom(current_window_zoom_ - 1);
}

View File

@@ -41,6 +41,9 @@ private:
// UI y debug
bool show_debug_ = false;
bool show_text_ = true;
// Sistema de zoom dinámico
int current_window_zoom_ = WINDOW_ZOOM;
std::string text_;
int text_pos_ = 0;
Uint64 text_init_time_ = 0;
@@ -52,6 +55,7 @@ private:
std::string fps_text_ = "FPS: 0";
bool vsync_enabled_ = true;
std::string vsync_text_ = "VSYNC ON";
bool fullscreen_enabled_ = false;
// Sistema de temas
ColorTheme current_theme_ = ColorTheme::SUNSET;
@@ -100,8 +104,15 @@ private:
void switchBallsGravity();
void changeGravityDirection(GravityDirection direction);
void toggleVSync();
void toggleFullscreen();
std::string gravityDirectionToString(GravityDirection direction) const;
// Sistema de zoom dinámico
int calculateMaxWindowZoom() const;
void setWindowZoom(int new_zoom);
void zoomIn();
void zoomOut();
// Rendering
void renderGradientBackground();
void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b);