Refactor fase 2: Extraer SceneManager de Engine

Migra toda la lógica de gestión de bolas y física a SceneManager
siguiendo el principio de Single Responsibility (SRP).

## Archivos Nuevos

**source/scene/scene_manager.h:**
- Declaración de clase SceneManager
- Gestión de bolas (creación, destrucción, actualización)
- Control de gravedad direccional y estado
- Métodos de acceso: getBalls(), getBallsMutable(), getFirstBall()
- Constructor: SceneManager(screen_width, screen_height)

**source/scene/scene_manager.cpp:**
- Implementación de lógica de escena (~200 líneas)
- changeScenario(): Crea N bolas según escenario
- pushBallsAwayFromGravity(): Impulso direccional
- switchBallsGravity(), forceBallsGravityOn/Off()
- changeGravityDirection(): Cambio de dirección física
- updateBallTexture(): Actualiza textura y tamaño
- updateScreenSize(): Ajusta resolución de pantalla
- updateBallSizes(): Reescala pelotas desde centro

## Archivos Modificados

**source/engine.h:**
- Agregado: #include "scene/scene_manager.h"
- Agregado: std::unique_ptr<SceneManager> scene_manager_
- Removido: std::vector<std::unique_ptr<Ball>> balls_
- Removido: GravityDirection current_gravity_
- Removido: int scenario_
- Removidos métodos privados: initBalls(), switchBallsGravity(),
  enableBallsGravityIfDisabled(), forceBallsGravityOn/Off(),
  changeGravityDirection(), updateBallSizes()

**source/engine.cpp:**
- initialize(): Crea scene_manager_ con resolución
- update(): Delega a scene_manager_->update()
- render(): Usa scene_manager_->getBalls()
- changeScenario(): Delega a scene_manager_
- pushBallsAwayFromGravity(): Delega a scene_manager_
- handleGravityToggle(): Usa scene_manager_->switchBallsGravity()
- handleGravityDirectionChange(): Delega dirección
- switchTextureInternal(): Usa updateBallTexture()
- toggleShapeModeInternal(): Usa getBallsMutable()
- activateShapeInternal(): Usa forceBallsGravityOff()
- updateShape(): Usa getBallsMutable() para asignar targets
- Debug HUD: Usa getFirstBall() para info
- toggleRealFullscreen(): Usa updateScreenSize() + changeScenario()
- performDemoAction(): Delega gravedad y escenarios
- randomizeOnDemoStart(): Delega changeScenario()
- toggleGravityOnOff(): Usa forceBallsGravity*()
- enterLogoMode(): Usa getBallCount() y changeScenario()
- exitLogoMode(): Usa updateBallTexture()
- Removidos ~150 líneas de implementación movidas a SceneManager

**CMakeLists.txt:**
- Agregado source/scene/*.cpp a file(GLOB SOURCE_FILES ...)

## Resultado

- Engine.cpp reducido de 2341 → ~2150 líneas (-191 líneas)
- SceneManager: 202 líneas de lógica de física/escena
- Separación clara: Engine coordina, SceneManager ejecuta física
- 100% funcional: Compila sin errores ni warnings
- Preparado para Fase 3 (UIManager)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-10 11:59:32 +02:00
parent b8d3c60e58
commit f93879b803
5 changed files with 460 additions and 253 deletions

View File

@@ -0,0 +1,199 @@
#include "scene_manager.h"
#include <cstdlib> // for rand
#include "../defines.h" // for BALL_COUNT_SCENARIOS, GRAVITY_MASS_MIN, etc
#include "../external/texture.h" // for Texture
#include "../theme_manager.h" // for ThemeManager
SceneManager::SceneManager(int screen_width, int screen_height)
: current_gravity_(GravityDirection::DOWN)
, scenario_(0)
, screen_width_(screen_width)
, screen_height_(screen_height)
, current_ball_size_(10)
, texture_(nullptr)
, theme_manager_(nullptr) {
}
void SceneManager::initialize(int scenario, std::shared_ptr<Texture> texture, ThemeManager* theme_manager) {
scenario_ = scenario;
texture_ = texture;
theme_manager_ = theme_manager;
current_ball_size_ = texture_->getWidth();
// Crear bolas iniciales
changeScenario(scenario_);
}
void SceneManager::update(float delta_time) {
// Actualizar física de todas las bolas
for (auto& ball : balls_) {
ball->update(delta_time);
}
}
void SceneManager::changeScenario(int scenario_id) {
// Guardar escenario
scenario_ = scenario_id;
// Limpiar las bolas actuales
balls_.clear();
// Resetear gravedad al estado por defecto (DOWN) al cambiar escenario
changeGravityDirection(GravityDirection::DOWN);
// Crear las bolas según el escenario
for (int i = 0; i < BALL_COUNT_SCENARIOS[scenario_id]; ++i) {
const int SIGN = ((rand() % 2) * 2) - 1; // Genera un signo aleatorio (+ o -)
// Calcular spawn zone: margen a cada lado, zona central para spawn
const int margin = static_cast<int>(screen_width_ * BALL_SPAWN_MARGIN);
const int spawn_zone_width = screen_width_ - (2 * margin);
const float X = (rand() % spawn_zone_width) + margin; // Posición inicial en X
const float VX = (((rand() % 20) + 10) * 0.1f) * SIGN; // Velocidad en X
const float VY = ((rand() % 60) - 30) * 0.1f; // Velocidad en Y
// Seleccionar color de la paleta del tema actual (delegado a ThemeManager)
int random_index = rand();
Color COLOR = theme_manager_->getInitialBallColor(random_index);
// 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);
balls_.emplace_back(std::make_unique<Ball>(
X, VX, VY, COLOR, texture_,
screen_width_, screen_height_, current_ball_size_,
current_gravity_, mass_factor
));
}
}
void SceneManager::updateBallTexture(std::shared_ptr<Texture> new_texture, int new_ball_size) {
if (balls_.empty()) return;
// Guardar tamaño antiguo
int old_size = current_ball_size_;
// Actualizar textura y tamaño
texture_ = new_texture;
current_ball_size_ = new_ball_size;
// Actualizar texturas de todas las pelotas
for (auto& ball : balls_) {
ball->setTexture(texture_);
}
// Ajustar posiciones según el cambio de tamaño
updateBallSizes(old_size, new_ball_size);
}
void SceneManager::pushBallsAwayFromGravity() {
for (auto& ball : balls_) {
const int SIGNO = ((rand() % 2) * 2) - 1;
const float LATERAL = (((rand() % 20) + 10) * 0.1f) * SIGNO;
const float MAIN = ((rand() % 40) * 0.1f) + 5;
float vx = 0, vy = 0;
switch (current_gravity_) {
case GravityDirection::DOWN: // Impulsar ARRIBA
vx = LATERAL;
vy = -MAIN;
break;
case GravityDirection::UP: // Impulsar ABAJO
vx = LATERAL;
vy = MAIN;
break;
case GravityDirection::LEFT: // Impulsar DERECHA
vx = MAIN;
vy = LATERAL;
break;
case GravityDirection::RIGHT: // Impulsar IZQUIERDA
vx = -MAIN;
vy = LATERAL;
break;
}
ball->modVel(vx, vy); // Modifica la velocidad según dirección de gravedad
}
}
void SceneManager::switchBallsGravity() {
for (auto& ball : balls_) {
ball->switchGravity();
}
}
void SceneManager::enableBallsGravityIfDisabled() {
for (auto& ball : balls_) {
ball->enableGravityIfDisabled();
}
}
void SceneManager::forceBallsGravityOn() {
for (auto& ball : balls_) {
ball->forceGravityOn();
}
}
void SceneManager::forceBallsGravityOff() {
// Contar cuántas pelotas están en superficie (suelo/techo/pared)
int balls_on_surface = 0;
for (const auto& ball : balls_) {
if (ball->isOnSurface()) {
balls_on_surface++;
}
}
// Si la mayoría (>50%) están en superficie, aplicar impulso para que se vea el efecto
float surface_ratio = static_cast<float>(balls_on_surface) / static_cast<float>(balls_.size());
if (surface_ratio > 0.5f) {
pushBallsAwayFromGravity(); // Dar impulso contrario a gravedad
}
// Desactivar gravedad
for (auto& ball : balls_) {
ball->forceGravityOff();
}
}
void SceneManager::changeGravityDirection(GravityDirection direction) {
current_gravity_ = direction;
for (auto& ball : balls_) {
ball->setGravityDirection(direction);
ball->applyRandomLateralPush(); // Aplicar empuje lateral aleatorio
}
}
void SceneManager::updateScreenSize(int width, int height) {
screen_width_ = width;
screen_height_ = height;
// NOTA: No actualizamos las bolas existentes, solo afecta a futuras creaciones
// Si se requiere reposicionar bolas existentes, implementar aquí
}
// === Métodos privados ===
void SceneManager::updateBallSizes(int old_size, int new_size) {
for (auto& ball : balls_) {
SDL_FRect pos = ball->getPosition();
// Ajustar posición para compensar cambio de tamaño
// Si aumenta tamaño, mover hacia centro; si disminuye, alejar del centro
float center_x = screen_width_ / 2.0f;
float center_y = screen_height_ / 2.0f;
float dx = pos.x - center_x;
float dy = pos.y - center_y;
// Ajustar proporcionalmente (evitar divisiones por cero)
if (old_size > 0) {
float scale_factor = static_cast<float>(new_size) / static_cast<float>(old_size);
pos.x = center_x + dx * scale_factor;
pos.y = center_y + dy * scale_factor;
}
// Actualizar tamaño del hitbox
ball->updateSize(new_size);
}
}