Implementar cambio de sprite dinámico con hot-swap (Tecla N)

Sistema de múltiples texturas:
- Carga ball.png (10x10) y ball_small.png (6x6) al inicio
- Variable current_ball_size_ obtiene tamaño desde texture->getWidth()
- Eliminar constante BALL_SIZE hardcoded

Cambio de tamaño con ajuste de posiciones:
- updateBallSizes() ajusta pos según gravedad y superficie
- DOWN: mueve Y hacia abajo si crece
- UP: mueve Y hacia arriba si crece
- LEFT/RIGHT: mueve X correspondiente
- Solo ajusta pelotas en superficie (isOnSurface())

Ball class actualizada:
- Constructor recibe ball_size como parámetro
- updateSize(new_size): actualiza hitbox y sprite
- setTexture(texture): cambia textura del sprite
- setPosition() usa setRotoBallScreenPosition()

Sprite class:
- Añadido setTexture() inline para hot-swap

Tecla N:
- Cicla entre texturas disponibles
- Actualiza todas las pelotas sin reiniciar física
- Texto informativo "SPRITE: NORMAL" / "SPRITE: SMALL"

Fix bug initBalls():
- Ahora usa current_ball_size_ en constructor
- Pelotas nuevas tienen tamaño correcto según textura activa

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-04 08:08:00 +02:00
parent 6bb814e61c
commit dcd05e502f
6 changed files with 133 additions and 12 deletions

View File

@@ -4,7 +4,7 @@
#include <cmath> // for fabs #include <cmath> // for fabs
#include "defines.h" // for BALL_SIZE, Color, SCREEN_HEIGHT, GRAVITY_FORCE #include "defines.h" // for Color, SCREEN_HEIGHT, GRAVITY_FORCE
class Texture; class Texture;
// Función auxiliar para generar pérdida aleatoria en rebotes // Función auxiliar para generar pérdida aleatoria en rebotes
@@ -22,15 +22,15 @@ float generateLateralLoss() {
} }
// Constructor // Constructor
Ball::Ball(float x, float vx, float vy, Color color, std::shared_ptr<Texture> texture, int screen_width, int screen_height, GravityDirection gravity_dir, float mass_factor) Ball::Ball(float x, float vx, float vy, Color color, std::shared_ptr<Texture> texture, int screen_width, int screen_height, int ball_size, GravityDirection gravity_dir, float mass_factor)
: sprite_(std::make_unique<Sprite>(texture)), : sprite_(std::make_unique<Sprite>(texture)),
pos_({x, 0.0f, BALL_SIZE, BALL_SIZE}) { pos_({x, 0.0f, static_cast<float>(ball_size), static_cast<float>(ball_size)}) {
// Convertir velocidades de píxeles/frame a píxeles/segundo (multiplicar por 60) // Convertir velocidades de píxeles/frame a píxeles/segundo (multiplicar por 60)
vx_ = vx * 60.0f; vx_ = vx * 60.0f;
vy_ = vy * 60.0f; vy_ = vy * 60.0f;
sprite_->setPos({pos_.x, pos_.y}); sprite_->setPos({pos_.x, pos_.y});
sprite_->setSize(BALL_SIZE, BALL_SIZE); sprite_->setSize(ball_size, ball_size);
sprite_->setClip({0, 0, BALL_SIZE, BALL_SIZE}); sprite_->setClip({0.0f, 0.0f, static_cast<float>(ball_size), static_cast<float>(ball_size)});
color_ = color; color_ = color;
// Convertir gravedad de píxeles/frame² a píxeles/segundo² (multiplicar por 60²) // Convertir gravedad de píxeles/frame² a píxeles/segundo² (multiplicar por 60²)
gravity_force_ = GRAVITY_FORCE * 60.0f * 60.0f; gravity_force_ = GRAVITY_FORCE * 60.0f * 60.0f;
@@ -379,3 +379,19 @@ void Ball::applyRotoBallForce(float target_x, float target_y, float sphere_radiu
// Actualizar sprite para renderizado // Actualizar sprite para renderizado
sprite_->setPos({pos_.x, pos_.y}); sprite_->setPos({pos_.x, pos_.y});
} }
// Sistema de cambio de sprite dinámico
void Ball::updateSize(int new_size) {
// Actualizar tamaño del hitbox
pos_.w = static_cast<float>(new_size);
pos_.h = static_cast<float>(new_size);
// Actualizar sprite
sprite_->setSize(new_size, new_size);
sprite_->setClip({0.0f, 0.0f, static_cast<float>(new_size), static_cast<float>(new_size)});
}
void Ball::setTexture(std::shared_ptr<Texture> texture) {
// Actualizar textura del sprite
sprite_->setTexture(texture);
}

View File

@@ -32,7 +32,7 @@ class Ball {
public: public:
// Constructor // Constructor
Ball(float x, float vx, float vy, Color color, std::shared_ptr<Texture> texture, int screen_width, int screen_height, GravityDirection gravity_dir = GravityDirection::DOWN, float mass_factor = 1.0f); Ball(float x, float vx, float vy, Color color, std::shared_ptr<Texture> texture, int screen_width, int screen_height, int ball_size, GravityDirection gravity_dir = GravityDirection::DOWN, float mass_factor = 1.0f);
// Destructor // Destructor
~Ball() = default; ~Ball() = default;
@@ -78,6 +78,10 @@ class Ball {
Color getColor() const { return color_; } Color getColor() const { return color_; }
void setColor(const Color& color) { color_ = color; } void setColor(const Color& color) { color_ = color; }
// Sistema de cambio de sprite dinámico
void updateSize(int new_size); // Actualizar tamaño de hitbox
void setTexture(std::shared_ptr<Texture> texture); // Cambiar textura del sprite
// Funciones para modo RotoBall // Funciones para modo RotoBall
void setRotoBallPosition3D(float x, float y, float z); void setRotoBallPosition3D(float x, float y, float z);
void setRotoBallTarget2D(float x, float y); void setRotoBallTarget2D(float x, float y);

View File

@@ -8,7 +8,7 @@ constexpr char WINDOW_CAPTION[] = "vibe3_physics";
constexpr int SCREEN_WIDTH = 320; // Ancho de la pantalla lógica (píxeles) constexpr int SCREEN_WIDTH = 320; // Ancho de la pantalla lógica (píxeles)
constexpr int SCREEN_HEIGHT = 240; // Alto de la pantalla lógica (píxeles) constexpr int SCREEN_HEIGHT = 240; // Alto de la pantalla lógica (píxeles)
constexpr int WINDOW_ZOOM = 3; // Zoom inicial de la ventana constexpr int WINDOW_ZOOM = 3; // Zoom inicial de la ventana
constexpr int BALL_SIZE = 10; // Tamaño de las pelotas (píxeles) // BALL_SIZE eliminado: ahora se obtiene dinámicamente desde texture_->getWidth()
// Configuración de zoom dinámico de ventana // Configuración de zoom dinámico de ventana
constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240) constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240)

View File

@@ -80,10 +80,22 @@ bool Engine::initialize() {
// Inicializar otros componentes si SDL se inicializó correctamente // Inicializar otros componentes si SDL se inicializó correctamente
if (success) { if (success) {
// Construir ruta absoluta a la imagen // Cargar todas las texturas disponibles
std::string exe_dir = getExecutableDirectory(); std::string exe_dir = getExecutableDirectory();
std::string texture_path = exe_dir + "/data/ball.png";
texture_ = std::make_shared<Texture>(renderer_, texture_path); // Textura 0: ball.png (10x10)
std::string texture_path_normal = exe_dir + "/data/ball.png";
textures_.push_back(std::make_shared<Texture>(renderer_, texture_path_normal));
// Textura 1: ball_small.png (6x6)
std::string texture_path_small = exe_dir + "/data/ball_small.png";
textures_.push_back(std::make_shared<Texture>(renderer_, texture_path_small));
// Establecer textura inicial (índice 0)
current_texture_index_ = 0;
texture_ = textures_[current_texture_index_];
current_ball_size_ = texture_->getWidth(); // Obtener tamaño dinámicamente
srand(static_cast<unsigned>(time(nullptr))); srand(static_cast<unsigned>(time(nullptr)));
dbg_init(renderer_); dbg_init(renderer_);
initializeThemes(); initializeThemes();
@@ -337,6 +349,11 @@ void Engine::handleEvents() {
startThemeTransition(ColorTheme::MONOCHROME); startThemeTransition(ColorTheme::MONOCHROME);
break; break;
// Cambio de sprite/textura dinámico
case SDLK_N:
switchTexture();
break;
// Control de escala de figura (solo en modo SHAPE) // Control de escala de figura (solo en modo SHAPE)
case SDLK_KP_PLUS: case SDLK_KP_PLUS:
if (current_mode_ == SimulationMode::SHAPE) { if (current_mode_ == SimulationMode::SHAPE) {
@@ -609,7 +626,7 @@ void Engine::initBalls(int value) {
const Color COLOR = theme.ball_colors[color_index]; const Color COLOR = theme.ball_colors[color_index];
// Generar factor de masa aleatorio (0.7 = ligera, 1.3 = pesada) // 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); float mass_factor = GRAVITY_MASS_MIN + (rand() % 1000) / 1000.0f * (GRAVITY_MASS_MAX - GRAVITY_MASS_MIN);
balls_.emplace_back(std::make_unique<Ball>(X, VX, VY, COLOR, texture_, current_screen_width_, current_screen_height_, current_gravity_, mass_factor)); balls_.emplace_back(std::make_unique<Ball>(X, VX, VY, COLOR, texture_, current_screen_width_, current_screen_height_, current_ball_size_, current_gravity_, mass_factor));
} }
setText(); // Actualiza el texto setText(); // Actualiza el texto
} }
@@ -1113,6 +1130,80 @@ void Engine::performRandomRestart() {
all_balls_stopped_start_time_ = 0; all_balls_stopped_start_time_ = 0;
} }
// Sistema de cambio de sprites dinámico
void Engine::updateBallSizes(int old_size, int new_size) {
float delta_size = static_cast<float>(new_size - old_size);
for (auto& ball : balls_) {
SDL_FRect pos = ball->getPosition();
// Solo ajustar posición si la pelota está en superficie
if (ball->isOnSurface()) {
GravityDirection grav_dir = ball->getGravityDirection();
switch (grav_dir) {
case GravityDirection::DOWN:
// Superficie inferior: ajustar Y hacia abajo si crece
pos.y += delta_size;
break;
case GravityDirection::UP:
// Superficie superior: ajustar Y hacia arriba si crece
pos.y -= delta_size;
break;
case GravityDirection::LEFT:
// Superficie izquierda: ajustar X hacia izquierda si crece
pos.x -= delta_size;
break;
case GravityDirection::RIGHT:
// Superficie derecha: ajustar X hacia derecha si crece
pos.x += delta_size;
break;
}
}
// Actualizar tamaño del hitbox
ball->updateSize(new_size);
// Si ajustamos posición, aplicarla ahora
if (ball->isOnSurface()) {
ball->setRotoBallScreenPosition(pos.x, pos.y);
}
}
}
void Engine::switchTexture() {
if (textures_.empty()) return;
// Guardar tamaño antiguo
int old_size = current_ball_size_;
// Cambiar a siguiente textura (ciclar)
current_texture_index_ = (current_texture_index_ + 1) % textures_.size();
texture_ = textures_[current_texture_index_];
// Obtener nuevo tamaño de la textura
int new_size = texture_->getWidth();
current_ball_size_ = new_size;
// Actualizar texturas y tamaños de todas las pelotas
for (auto& ball : balls_) {
ball->setTexture(texture_);
}
// Ajustar posiciones según el cambio de tamaño
updateBallSizes(old_size, new_size);
// Mostrar texto informativo
std::string texture_name = (current_texture_index_ == 0) ? "NORMAL" : "SMALL";
text_ = "SPRITE: " + texture_name;
text_pos_ = (current_screen_width_ - static_cast<int>(text_.length() * 8)) / 2;
show_text_ = true;
text_init_time_ = SDL_GetTicks();
}
// Sistema de Figuras 3D - Alternar entre modo física y última figura (Toggle con tecla F) // Sistema de Figuras 3D - Alternar entre modo física y última figura (Toggle con tecla F)
void Engine::toggleShapeMode(bool force_gravity_on_exit) { void Engine::toggleShapeMode(bool force_gravity_on_exit) {
if (current_mode_ == SimulationMode::PHYSICS) { if (current_mode_ == SimulationMode::PHYSICS) {

View File

@@ -26,7 +26,10 @@ private:
// Recursos SDL // Recursos SDL
SDL_Window* window_ = nullptr; SDL_Window* window_ = nullptr;
SDL_Renderer* renderer_ = nullptr; SDL_Renderer* renderer_ = nullptr;
std::shared_ptr<Texture> texture_ = nullptr; std::shared_ptr<Texture> texture_ = nullptr; // Textura activa actual
std::vector<std::shared_ptr<Texture>> textures_; // Todas las texturas disponibles
size_t current_texture_index_ = 0; // Índice de textura activa
int current_ball_size_ = 10; // Tamaño actual de pelotas (dinámico, se actualiza desde texture)
// Estado del simulador // Estado del simulador
std::vector<std::unique_ptr<Ball>> balls_; std::vector<std::unique_ptr<Ball>> balls_;
@@ -128,6 +131,10 @@ private:
Color getInterpolatedColor(size_t ball_index) const; // Obtener color interpolado durante transición Color getInterpolatedColor(size_t ball_index) const; // Obtener color interpolado durante transición
void startThemeTransition(ColorTheme new_theme); void startThemeTransition(ColorTheme new_theme);
// Sistema de cambio de sprites dinámico
void switchTexture(); // Cambia a siguiente textura disponible
void updateBallSizes(int old_size, int new_size); // Ajusta posiciones al cambiar tamaño
// Sistema de zoom dinámico // Sistema de zoom dinámico
int calculateMaxWindowZoom() const; int calculateMaxWindowZoom() const;
void setWindowZoom(int new_zoom); void setWindowZoom(int new_zoom);

View File

@@ -32,4 +32,7 @@ class Sprite {
// Modulación de color // Modulación de color
void setColor(int r, int g, int b); void setColor(int r, int g, int b);
// Cambio de textura dinámico
void setTexture(std::shared_ptr<Texture> texture) { texture_ = texture; }
}; };