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:
@@ -2,10 +2,16 @@
|
|||||||
|
|
||||||
constexpr char WINDOW_CAPTION[] = "vibe3_physics";
|
constexpr char WINDOW_CAPTION[] = "vibe3_physics";
|
||||||
|
|
||||||
constexpr int SCREEN_WIDTH = 320;
|
constexpr int SCREEN_WIDTH = 640;
|
||||||
constexpr int SCREEN_HEIGHT = 240;
|
constexpr int SCREEN_HEIGHT = 480;
|
||||||
constexpr int WINDOW_SIZE = 3;
|
constexpr int WINDOW_ZOOM = 2; // Zoom inicial de la ventana
|
||||||
constexpr int BALL_SIZE = 10;
|
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;
|
constexpr float GRAVITY_FORCE = 0.2f;
|
||||||
|
|
||||||
// DEMO_SPEED eliminado - ya no se usa con delta time
|
// DEMO_SPEED eliminado - ya no se usa con delta time
|
||||||
|
|||||||
@@ -6,17 +6,36 @@
|
|||||||
#include <SDL3/SDL_keycode.h> // for SDL_Keycode
|
#include <SDL3/SDL_keycode.h> // for SDL_Keycode
|
||||||
#include <SDL3/SDL_render.h> // for SDL_SetRenderDrawColor, SDL_RenderPresent
|
#include <SDL3/SDL_render.h> // for SDL_SetRenderDrawColor, SDL_RenderPresent
|
||||||
#include <SDL3/SDL_timer.h> // for SDL_GetTicks
|
#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 <cstdlib> // for rand, srand
|
||||||
#include <ctime> // for time
|
#include <ctime> // for time
|
||||||
#include <iostream> // for cout
|
#include <iostream> // for cout
|
||||||
#include <string> // for string
|
#include <string> // for string
|
||||||
|
#include <filesystem> // for path operations
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h> // for GetModuleFileName
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "ball.h" // for Ball
|
#include "ball.h" // for Ball
|
||||||
#include "external/dbgtxt.h" // for dbg_init, dbg_print
|
#include "external/dbgtxt.h" // for dbg_init, dbg_print
|
||||||
#include "external/texture.h" // for Texture
|
#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
|
// Implementación de métodos públicos
|
||||||
bool Engine::initialize() {
|
bool Engine::initialize() {
|
||||||
bool success = true;
|
bool success = true;
|
||||||
@@ -26,7 +45,7 @@ bool Engine::initialize() {
|
|||||||
success = false;
|
success = false;
|
||||||
} else {
|
} else {
|
||||||
// Crear ventana principal
|
// 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) {
|
if (window_ == nullptr) {
|
||||||
std::cout << "¡No se pudo crear la ventana! Error de SDL: " << SDL_GetError() << std::endl;
|
std::cout << "¡No se pudo crear la ventana! Error de SDL: " << SDL_GetError() << std::endl;
|
||||||
success = false;
|
success = false;
|
||||||
@@ -51,7 +70,10 @@ bool Engine::initialize() {
|
|||||||
|
|
||||||
// Inicializar otros componentes si SDL se inicializó correctamente
|
// Inicializar otros componentes si SDL se inicializó correctamente
|
||||||
if (success) {
|
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)));
|
srand(static_cast<unsigned>(time(nullptr)));
|
||||||
dbg_init(renderer_);
|
dbg_init(renderer_);
|
||||||
initBalls(scenario_);
|
initBalls(scenario_);
|
||||||
@@ -181,22 +203,23 @@ void Engine::handleEvents() {
|
|||||||
initBalls(scenario_); // Regenerar bolas con nueva paleta
|
initBalls(scenario_); // Regenerar bolas con nueva paleta
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_F1:
|
// Temas de colores con teclado numérico
|
||||||
|
case SDLK_KP_1:
|
||||||
current_theme_ = ColorTheme::SUNSET;
|
current_theme_ = ColorTheme::SUNSET;
|
||||||
initBalls(scenario_);
|
initBalls(scenario_);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_F2:
|
case SDLK_KP_2:
|
||||||
current_theme_ = ColorTheme::OCEAN;
|
current_theme_ = ColorTheme::OCEAN;
|
||||||
initBalls(scenario_);
|
initBalls(scenario_);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_F3:
|
case SDLK_KP_3:
|
||||||
current_theme_ = ColorTheme::NEON;
|
current_theme_ = ColorTheme::NEON;
|
||||||
initBalls(scenario_);
|
initBalls(scenario_);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_F4:
|
case SDLK_KP_4:
|
||||||
current_theme_ = ColorTheme::FOREST;
|
current_theme_ = ColorTheme::FOREST;
|
||||||
initBalls(scenario_);
|
initBalls(scenario_);
|
||||||
break;
|
break;
|
||||||
@@ -240,6 +263,20 @@ void Engine::handleEvents() {
|
|||||||
scenario_ = 7;
|
scenario_ = 7;
|
||||||
initBalls(scenario_);
|
initBalls(scenario_);
|
||||||
break;
|
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);
|
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 {
|
std::string Engine::gravityDirectionToString(GravityDirection direction) const {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case GravityDirection::DOWN: return "DOWN";
|
case GravityDirection::DOWN: return "DOWN";
|
||||||
@@ -495,4 +537,80 @@ void Engine::addSpriteToBatch(float x, float y, float w, float h, int r, int g,
|
|||||||
batch_indices_.push_back(vertex_index + 2);
|
batch_indices_.push_back(vertex_index + 2);
|
||||||
batch_indices_.push_back(vertex_index + 3);
|
batch_indices_.push_back(vertex_index + 3);
|
||||||
batch_indices_.push_back(vertex_index + 0);
|
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_, ¤t_x, ¤t_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);
|
||||||
}
|
}
|
||||||
@@ -41,6 +41,9 @@ private:
|
|||||||
// UI y debug
|
// UI y debug
|
||||||
bool show_debug_ = false;
|
bool show_debug_ = false;
|
||||||
bool show_text_ = true;
|
bool show_text_ = true;
|
||||||
|
|
||||||
|
// Sistema de zoom dinámico
|
||||||
|
int current_window_zoom_ = WINDOW_ZOOM;
|
||||||
std::string text_;
|
std::string text_;
|
||||||
int text_pos_ = 0;
|
int text_pos_ = 0;
|
||||||
Uint64 text_init_time_ = 0;
|
Uint64 text_init_time_ = 0;
|
||||||
@@ -52,6 +55,7 @@ private:
|
|||||||
std::string fps_text_ = "FPS: 0";
|
std::string fps_text_ = "FPS: 0";
|
||||||
bool vsync_enabled_ = true;
|
bool vsync_enabled_ = true;
|
||||||
std::string vsync_text_ = "VSYNC ON";
|
std::string vsync_text_ = "VSYNC ON";
|
||||||
|
bool fullscreen_enabled_ = false;
|
||||||
|
|
||||||
// Sistema de temas
|
// Sistema de temas
|
||||||
ColorTheme current_theme_ = ColorTheme::SUNSET;
|
ColorTheme current_theme_ = ColorTheme::SUNSET;
|
||||||
@@ -100,8 +104,15 @@ private:
|
|||||||
void switchBallsGravity();
|
void switchBallsGravity();
|
||||||
void changeGravityDirection(GravityDirection direction);
|
void changeGravityDirection(GravityDirection direction);
|
||||||
void toggleVSync();
|
void toggleVSync();
|
||||||
|
void toggleFullscreen();
|
||||||
std::string gravityDirectionToString(GravityDirection direction) const;
|
std::string gravityDirectionToString(GravityDirection direction) const;
|
||||||
|
|
||||||
|
// Sistema de zoom dinámico
|
||||||
|
int calculateMaxWindowZoom() const;
|
||||||
|
void setWindowZoom(int new_zoom);
|
||||||
|
void zoomIn();
|
||||||
|
void zoomOut();
|
||||||
|
|
||||||
// Rendering
|
// Rendering
|
||||||
void renderGradientBackground();
|
void renderGradientBackground();
|
||||||
void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b);
|
void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b);
|
||||||
|
|||||||
Reference in New Issue
Block a user