From b196683e4aeadafb3427637e0b971ba997f74833 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Fri, 3 Oct 2025 19:48:52 +0200 Subject: [PATCH] =?UTF-8?q?Hacer=20RotoBall=20totalmente=20escalable=20con?= =?UTF-8?q?=20resoluci=C3=B3n=20de=20pantalla?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problema: - Radio fijo de 80px funcionaba bien en 320x240 - En F4 fullscreen (1920x1080), radio de 360px era correcto visualmente - PERO las fuerzas físicas (spring_k, damping) seguían siendo para 80px - Resultado: pelotas nunca llegaban a pegarse en resoluciones altas Solución: 1. Radio proporcional a altura de pantalla (ROTOBALL_RADIUS_FACTOR = 0.333) 2. Escalar TODAS las constantes de física proporcionalmente al radio 3. Fórmula: scale = sphere_radius / BASE_RADIUS (80px) Cambios técnicos: - defines.h: ROTOBALL_RADIUS → ROTOBALL_RADIUS_FACTOR (0.333) - engine.cpp: Calcular radius dinámicamente en generate/update - ball.h: applyRotoBallForce() ahora recibe sphere_radius - ball.cpp: Escalar spring_k, damping_base, damping_near, near_threshold, max_force Resultado: - 320x240: Radio 80px, scale=1.0 (idéntico a antes) - 640x480: Radio 160px, scale=2.0 (fuerzas 2x) - 1920x1080: Radio 360px, scale=4.5 (fuerzas 4.5x) Comportamiento físico IDÉNTICO en todas las resoluciones ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- source/ball.cpp | 32 ++++++++++++++++++++++---------- source/ball.h | 2 +- source/defines.h | 2 +- source/engine.cpp | 24 +++++++++++++++--------- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/source/ball.cpp b/source/ball.cpp index 328f1c2..f76a7ec 100644 --- a/source/ball.cpp +++ b/source/ball.cpp @@ -309,9 +309,21 @@ void Ball::enableRotoBallAttraction(bool enable) { } // Aplicar fuerza de resorte hacia punto objetivo en esfera rotante -void Ball::applyRotoBallForce(float target_x, float target_y, float deltaTime) { +void Ball::applyRotoBallForce(float target_x, float target_y, float sphere_radius, float deltaTime) { if (!rotoball_attraction_active_) return; + // Calcular factor de escala basado en el radio (radio base = 80px) + // Si radius=80 → scale=1.0, si radius=160 → scale=2.0, si radius=360 → scale=4.5 + const float BASE_RADIUS = 80.0f; + float scale = sphere_radius / BASE_RADIUS; + + // Escalar constantes de física proporcionalmente + float spring_k = ROTOBALL_SPRING_K * scale; + float damping_base = ROTOBALL_DAMPING_BASE * scale; + float damping_near = ROTOBALL_DAMPING_NEAR * scale; + float near_threshold = ROTOBALL_NEAR_THRESHOLD * scale; + float max_force = ROTOBALL_MAX_FORCE * scale; + // Calcular vector diferencia (dirección hacia el target) float diff_x = target_x - pos_.x; float diff_y = target_y - pos_.y; @@ -320,13 +332,13 @@ void Ball::applyRotoBallForce(float target_x, float target_y, float deltaTime) { float distance = sqrtf(diff_x * diff_x + diff_y * diff_y); // Fuerza de resorte (Ley de Hooke: F = -k * x) - float spring_force_x = ROTOBALL_SPRING_K * diff_x; - float spring_force_y = ROTOBALL_SPRING_K * diff_y; + float spring_force_x = spring_k * diff_x; + float spring_force_y = spring_k * diff_y; // Amortiguación variable: más cerca del punto = más amortiguación (estabilización) - float damping = (distance < ROTOBALL_NEAR_THRESHOLD) - ? ROTOBALL_DAMPING_NEAR - : ROTOBALL_DAMPING_BASE; + float damping = (distance < near_threshold) + ? damping_near + : damping_base; // Fuerza de amortiguación (proporcional a la velocidad) float damping_force_x = damping * vx_; @@ -338,10 +350,10 @@ void Ball::applyRotoBallForce(float target_x, float target_y, float deltaTime) { // Limitar magnitud de fuerza (evitar explosiones numéricas) float force_magnitude = sqrtf(total_force_x * total_force_x + total_force_y * total_force_y); - if (force_magnitude > ROTOBALL_MAX_FORCE) { - float scale = ROTOBALL_MAX_FORCE / force_magnitude; - total_force_x *= scale; - total_force_y *= scale; + if (force_magnitude > max_force) { + float scale_limit = max_force / force_magnitude; + total_force_x *= scale_limit; + total_force_y *= scale_limit; } // Aplicar aceleración (F = ma, asumiendo m = 1 para simplificar) diff --git a/source/ball.h b/source/ball.h index 77110b3..9ca4026 100644 --- a/source/ball.h +++ b/source/ball.h @@ -85,5 +85,5 @@ class Ball { // Sistema de atracción física hacia esfera RotoBall void enableRotoBallAttraction(bool enable); - void applyRotoBallForce(float target_x, float target_y, float deltaTime); + void applyRotoBallForce(float target_x, float target_y, float sphere_radius, float deltaTime); }; \ No newline at end of file diff --git a/source/defines.h b/source/defines.h index 8da12f8..115b28c 100644 --- a/source/defines.h +++ b/source/defines.h @@ -65,7 +65,7 @@ enum class SimulationMode { }; // Configuración de RotoBall (esfera 3D rotante) -constexpr float ROTOBALL_RADIUS = 80.0f; // Radio de la esfera (píxeles) +constexpr float ROTOBALL_RADIUS_FACTOR = 0.333f; // Radio como proporción de altura de pantalla (80/240 ≈ 0.333) constexpr float ROTOBALL_ROTATION_SPEED_Y = 1.5f; // Velocidad rotación eje Y (rad/s) constexpr float ROTOBALL_ROTATION_SPEED_X = 0.8f; // Velocidad rotación eje X (rad/s) constexpr float ROTOBALL_TRANSITION_TIME = 1.5f; // Tiempo de transición (segundos) diff --git a/source/engine.cpp b/source/engine.cpp index 41fe635..be7f4ab 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -980,6 +980,9 @@ void Engine::generateRotoBallSphere() { int num_points = static_cast(balls_.size()); if (num_points == 0) return; + // Calcular radio dinámico proporcional a la altura de pantalla + float radius = current_screen_height_ * ROTOBALL_RADIUS_FACTOR; + // Constante Golden Ratio para Fibonacci sphere const float golden_ratio = (1.0f + sqrtf(5.0f)) / 2.0f; const float angle_increment = PI * 2.0f * golden_ratio; @@ -991,9 +994,9 @@ void Engine::generateRotoBallSphere() { float theta = angle_increment * static_cast(i); // Longitud // Convertir coordenadas esféricas a cartesianas - float x = cosf(theta) * sinf(phi) * ROTOBALL_RADIUS; - float y = sinf(theta) * sinf(phi) * ROTOBALL_RADIUS; - float z = cosf(phi) * ROTOBALL_RADIUS; + float x = cosf(theta) * sinf(phi) * radius; + float y = sinf(theta) * sinf(phi) * radius; + float z = cosf(phi) * radius; // Guardar posición 3D en la pelota balls_[i]->setRotoBallPosition3D(x, y, z); @@ -1004,7 +1007,7 @@ void Engine::generateRotoBallSphere() { balls_[i]->setRotoBallTarget2D(center_x + x, center_y + y); // Calcular brillo inicial según profundidad Z - float z_normalized = (z + ROTOBALL_RADIUS) / (2.0f * ROTOBALL_RADIUS); + float z_normalized = (z + radius) / (2.0f * radius); balls_[i]->setDepthBrightness(z_normalized); } } @@ -1013,6 +1016,9 @@ void Engine::generateRotoBallSphere() { void Engine::updateRotoBall() { if (current_mode_ != SimulationMode::ROTOBALL) return; + // Calcular radio dinámico proporcional a la altura de pantalla + float radius = current_screen_height_ * ROTOBALL_RADIUS_FACTOR; + // Actualizar ángulos de rotación de la esfera rotoball_.angle_y += ROTOBALL_ROTATION_SPEED_Y * delta_time_; rotoball_.angle_x += ROTOBALL_ROTATION_SPEED_X * delta_time_; @@ -1032,9 +1038,9 @@ void Engine::updateRotoBall() { float phi = acosf(1.0f - 2.0f * t); float theta = angle_increment * static_cast(i); - float x = cosf(theta) * sinf(phi) * ROTOBALL_RADIUS; - float y = sinf(theta) * sinf(phi) * ROTOBALL_RADIUS; - float z = cosf(phi) * ROTOBALL_RADIUS; + float x = cosf(theta) * sinf(phi) * radius; + float y = sinf(theta) * sinf(phi) * radius; + float z = cosf(phi) * radius; // Aplicar rotación en eje Y float cos_y = cosf(rotoball_.angle_y); @@ -1053,10 +1059,10 @@ void Engine::updateRotoBall() { float target_y = center_y + y_rot; // Aplicar fuerza de atracción física hacia el punto rotado - balls_[i]->applyRotoBallForce(target_x, target_y, delta_time_); + balls_[i]->applyRotoBallForce(target_x, target_y, radius, delta_time_); // Calcular brillo según profundidad Z para renderizado - float z_normalized = (z_final + ROTOBALL_RADIUS) / (2.0f * ROTOBALL_RADIUS); + float z_normalized = (z_final + radius) / (2.0f * radius); z_normalized = std::max(0.0f, std::min(1.0f, z_normalized)); balls_[i]->setDepthBrightness(z_normalized); }