Implementar física de atracción con resorte en RotoBall
Reemplazar interpolación lineal artificial por sistema de fuerzas físicamente realista usando Ley de Hooke con amortiguación variable. ## Cambios Principales ### Sistema de Física Implementado **Fuerza de Resorte (Hooke's Law):** ```cpp F_spring = k * (target - position) F_damping = c * velocity F_total = F_spring - F_damping acceleration = F_total / mass ``` **Constantes (defines.h):** - `ROTOBALL_SPRING_K = 300.0f`: Rigidez del resorte - `ROTOBALL_DAMPING_BASE = 15.0f`: Amortiguación lejos del punto - `ROTOBALL_DAMPING_NEAR = 50.0f`: Amortiguación cerca (estabilización) - `ROTOBALL_NEAR_THRESHOLD = 5.0f`: Distancia considerada "cerca" - `ROTOBALL_MAX_FORCE = 1000.0f`: Límite de seguridad ### Nuevas Funciones (Ball class) - `enableRotoBallAttraction(bool)`: Activa/desactiva atracción física - `applyRotoBallForce(target_x, target_y, deltaTime)`: Aplica fuerza de resorte ### Comportamiento Físico **Al entrar (PHYSICS → ROTOBALL):** 1. Pelotas mantienen velocidad actual (vx, vy) 2. Fuerza de atracción las acelera hacia puntos en esfera rotante 3. Amortiguación variable evita oscilaciones infinitas 4. Convergen al punto con aceleración natural **Durante RotoBall:** - Punto destino rota constantemente - Fuerza se recalcula cada frame hacia posición rotada - Pelotas "persiguen" su punto móvil - Efecto: Convergencia orgánica con ligera oscilación **Al salir (ROTOBALL → PHYSICS):** 1. Atracción se desactiva 2. Pelotas conservan velocidad tangencial actual 3. Gravedad vuelve a aplicarse 4. Caen con la inercia que traían de la esfera ### Archivos Modificados - `defines.h`: 5 nuevas constantes físicas - `ball.h/cpp`: Sistema de resorte completo - `engine.cpp`: Enable/disable atracción en toggle, updateRotoBall() usa física - `CLAUDE.md`: Documentación técnica completa ## Ventajas del Sistema ✅ Física realista con conservación de momento ✅ Transición orgánica (no artificial) ✅ Inercia preservada entrada/salida ✅ Amortiguación automática (no oscila infinito) ✅ Constantes ajustables para tuning ✅ Performance: O(1) por pelota 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -861,6 +861,12 @@ void Engine::toggleRotoBallMode() {
|
||||
// Generar esfera 3D
|
||||
generateRotoBallSphere();
|
||||
|
||||
// Activar atracción física en todas las pelotas
|
||||
// Las pelotas mantienen su velocidad actual y son atraídas hacia la esfera
|
||||
for (auto& ball : balls_) {
|
||||
ball->enableRotoBallAttraction(true);
|
||||
}
|
||||
|
||||
// Mostrar texto informativo
|
||||
text_ = "MODO ROTOBALL";
|
||||
int text_width = static_cast<int>(text_.length() * 8);
|
||||
@@ -868,11 +874,16 @@ void Engine::toggleRotoBallMode() {
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
show_text_ = true;
|
||||
} else {
|
||||
// Volver a modo física
|
||||
// Volver a modo física normal
|
||||
current_mode_ = SimulationMode::PHYSICS;
|
||||
rotoball_.transitioning = false;
|
||||
rotoball_.transition_progress = 0.0f;
|
||||
|
||||
// Desactivar atracción - las pelotas conservan su velocidad tangencial actual
|
||||
for (auto& ball : balls_) {
|
||||
ball->enableRotoBallAttraction(false);
|
||||
}
|
||||
|
||||
// Mostrar texto informativo
|
||||
text_ = "MODO FISICA";
|
||||
int text_width = static_cast<int>(text_.length() * 8);
|
||||
@@ -916,20 +927,11 @@ void Engine::generateRotoBallSphere() {
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar esfera RotoBall (rotación + proyección)
|
||||
// Actualizar esfera RotoBall con física de atracción
|
||||
void Engine::updateRotoBall() {
|
||||
if (current_mode_ != SimulationMode::ROTOBALL) return;
|
||||
|
||||
// Actualizar transición si está activa
|
||||
if (rotoball_.transitioning) {
|
||||
rotoball_.transition_progress += delta_time_ / ROTOBALL_TRANSITION_TIME;
|
||||
if (rotoball_.transition_progress >= 1.0f) {
|
||||
rotoball_.transition_progress = 1.0f;
|
||||
rotoball_.transitioning = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar ángulos de rotación
|
||||
// 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_;
|
||||
|
||||
@@ -937,15 +939,8 @@ void Engine::updateRotoBall() {
|
||||
float center_x = current_screen_width_ / 2.0f;
|
||||
float center_y = current_screen_height_ / 2.0f;
|
||||
|
||||
// Actualizar cada pelota
|
||||
// Actualizar cada pelota con física de atracción
|
||||
for (size_t i = 0; i < balls_.size(); i++) {
|
||||
// Obtener posición 3D original (almacenada en generateRotoBallSphere)
|
||||
SDL_FRect pos = balls_[i]->getPosition();
|
||||
|
||||
// Reconstruir coordenadas 3D originales desde los datos almacenados
|
||||
// En generateRotoBallSphere guardamos: x, y, z en setRotoBallPosition3D
|
||||
// Pero necesitamos acceder a esos datos... por ahora recalcularemos
|
||||
|
||||
// Recalcular posición 3D original usando Fibonacci sphere
|
||||
int num_points = static_cast<int>(balls_.size());
|
||||
const float golden_ratio = (1.0f + sqrtf(5.0f)) / 2.0f;
|
||||
@@ -971,26 +966,16 @@ void Engine::updateRotoBall() {
|
||||
float y_rot = y * cos_x - z_rot * sin_x;
|
||||
float z_final = y * sin_x + z_rot * cos_x;
|
||||
|
||||
// Proyección 2D ortográfica
|
||||
float screen_x = center_x + x_rot;
|
||||
float screen_y = center_y + y_rot;
|
||||
// Proyección 2D ortográfica (punto objetivo móvil)
|
||||
float target_x = center_x + x_rot;
|
||||
float target_y = center_y + y_rot;
|
||||
|
||||
// Calcular brillo según profundidad Z (normalizado 0-1)
|
||||
// Aplicar fuerza de atracción física hacia el punto rotado
|
||||
balls_[i]->applyRotoBallForce(target_x, target_y, delta_time_);
|
||||
|
||||
// Calcular brillo según profundidad Z para renderizado
|
||||
float z_normalized = (z_final + ROTOBALL_RADIUS) / (2.0f * ROTOBALL_RADIUS);
|
||||
z_normalized = std::max(0.0f, std::min(1.0f, z_normalized));
|
||||
|
||||
// Guardar brillo para usar en render
|
||||
balls_[i]->setDepthBrightness(z_normalized);
|
||||
|
||||
// Transición suave desde posición actual a posición de esfera
|
||||
if (rotoball_.transitioning) {
|
||||
// Interpolar desde posición actual hacia posición de esfera
|
||||
float lerp_x = pos.x + (screen_x - pos.x) * rotoball_.transition_progress;
|
||||
float lerp_y = pos.y + (screen_y - pos.y) * rotoball_.transition_progress;
|
||||
balls_[i]->setRotoBallScreenPosition(lerp_x, lerp_y);
|
||||
} else {
|
||||
// Ya en esfera, actualizar directamente
|
||||
balls_[i]->setRotoBallScreenPosition(screen_x, screen_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user