afegit escenari personalitzat per parametre

This commit is contained in:
2026-03-11 22:44:17 +01:00
parent ea27a771ab
commit dfbd8a430b
8 changed files with 99 additions and 6 deletions

View File

@@ -59,6 +59,7 @@ constexpr int BALL_COUNT_SCENARIOS[8] = {10, 50, 100, 500, 1000, 5000, 10000, 50
constexpr int DEMO_AUTO_MIN_SCENARIO = 2; // mínimo 100 bolas
constexpr int DEMO_AUTO_MAX_SCENARIO = 7; // máximo sin restricción hardware (ajustado por benchmark)
constexpr int LOGO_MIN_SCENARIO_IDX = 4; // mínimo 1000 bolas (sustituye LOGO_MODE_MIN_BALLS)
constexpr int CUSTOM_SCENARIO_IDX = 8; // Escenario custom opcional (tecla 9, --custom-balls)
// Estructura para representar colores RGB
struct Color {

View File

@@ -226,6 +226,10 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMod
scene_manager_ = std::make_unique<SceneManager>(current_screen_width_, current_screen_height_);
scene_manager_->initialize(0, texture_, theme_manager_.get()); // Escenario 0 (10 bolas) por defecto
// Propagar configuración custom si fue establecida antes de initialize()
if (custom_scenario_enabled_)
scene_manager_->setCustomBallCount(custom_scenario_balls_);
// Calcular tamaño físico de ventana ANTES de inicializar UIManager
// NOTA: No llamar a updatePhysicalWindowSize() aquí porque ui_manager_ aún no existe
// Calcular manualmente para poder pasar valores al constructor de UIManager
@@ -550,6 +554,15 @@ void Engine::switchTexture() {
switchTextureInternal(true); // Mostrar notificación en modo manual
}
// Escenario custom (--custom-balls)
void Engine::setCustomScenario(int balls) {
custom_scenario_balls_ = balls;
custom_scenario_enabled_ = true;
// scene_manager_ puede no existir aún (llamada pre-init); propagación en initialize()
if (scene_manager_)
scene_manager_->setCustomBallCount(balls);
}
// Escenarios (número de pelotas)
void Engine::changeScenario(int scenario_id, const char* notification_text) {
// Pasar el modo actual al SceneManager para inicialización correcta
@@ -1420,8 +1433,12 @@ void Engine::executeDemoAction(bool is_lite) {
accumulated_weight += DEMO_WEIGHT_SCENARIO;
if (random_value < accumulated_weight) {
int auto_max = std::min(max_auto_scenario_, DEMO_AUTO_MAX_SCENARIO);
int range = auto_max - DEMO_AUTO_MIN_SCENARIO + 1;
int new_scenario = DEMO_AUTO_MIN_SCENARIO + (rand() % range);
std::vector<int> candidates;
for (int i = DEMO_AUTO_MIN_SCENARIO; i <= auto_max; ++i)
candidates.push_back(i);
if (custom_scenario_enabled_ && custom_auto_available_)
candidates.push_back(CUSTOM_SCENARIO_IDX);
int new_scenario = candidates[rand() % candidates.size()];
scene_manager_->changeScenario(new_scenario, current_mode_);
// Si estamos en modo SHAPE, regenerar la figura con nuevo número de pelotas
@@ -1578,8 +1595,12 @@ void Engine::executeRandomizeOnDemoStart(bool is_lite) {
// 2. Escenario - rango dinámico según benchmark de rendimiento
int auto_max = std::min(max_auto_scenario_, DEMO_AUTO_MAX_SCENARIO);
int range = auto_max - DEMO_AUTO_MIN_SCENARIO + 1;
int new_scenario = DEMO_AUTO_MIN_SCENARIO + (rand() % range);
std::vector<int> candidates;
for (int i = DEMO_AUTO_MIN_SCENARIO; i <= auto_max; ++i)
candidates.push_back(i);
if (custom_scenario_enabled_ && custom_auto_available_)
candidates.push_back(CUSTOM_SCENARIO_IDX);
int new_scenario = candidates[rand() % candidates.size()];
scene_manager_->changeScenario(new_scenario, current_mode_);
// Si estamos en modo SHAPE, generar la figura y activar atracción
@@ -1653,6 +1674,30 @@ void Engine::runPerformanceBenchmark() {
last_frame_time_ = 0;
};
// Test escenario custom (independiente de max_auto_scenario_)
custom_auto_available_ = false;
if (custom_scenario_enabled_) {
scene_manager_->changeScenario(CUSTOM_SCENARIO_IDX, current_mode_);
last_frame_time_ = 0;
for (int w = 0; w < WARMUP_FRAMES; ++w) {
calculateDeltaTime();
SDL_Event e; while (SDL_PollEvent(&e)) {}
update();
render();
}
int frame_count = 0;
Uint64 start = SDL_GetTicks();
while (SDL_GetTicks() - start < static_cast<Uint64>(BENCH_DURATION_MS)) {
calculateDeltaTime();
SDL_Event e; while (SDL_PollEvent(&e)) {}
update();
render();
++frame_count;
}
float fps = static_cast<float>(frame_count) / (BENCH_DURATION_MS / 1000.0f);
custom_auto_available_ = (fps >= monitor_hz);
}
// Probar de más pesado a más ligero
for (int idx = DEMO_AUTO_MAX_SCENARIO; idx >= DEMO_AUTO_MIN_SCENARIO; --idx) {
scene_manager_->changeScenario(idx, current_mode_);

View File

@@ -75,6 +75,12 @@ class Engine {
void setKioskMode(bool enabled) { kiosk_mode_ = enabled; }
bool isKioskMode() const { return kiosk_mode_; }
// Escenario custom (tecla 9, --custom-balls)
void setCustomScenario(int balls);
bool isCustomScenarioEnabled() const { return custom_scenario_enabled_; }
bool isCustomAutoAvailable() const { return custom_auto_available_; }
int getCustomScenarioBalls() const { return custom_scenario_balls_; }
// Notificaciones (público para InputHandler)
void showNotificationForAction(const std::string& text);
@@ -175,6 +181,11 @@ class Engine {
float demo_next_action_time_ = 0.0f; // Tiempo aleatorio hasta próxima acción (segundos)
int max_auto_scenario_ = 5; // Índice máximo en modos auto (default conservador: 5000 bolas)
// Escenario custom (--custom-balls)
int custom_scenario_balls_ = 0;
bool custom_scenario_enabled_ = false;
bool custom_auto_available_ = false;
// Sistema de convergencia para LOGO MODE (escala con resolución)
// Usado por performLogoAction() para detectar cuando las bolas forman el logo
float shape_convergence_ = 0.0f; // % de pelotas cerca del objetivo (0.0-1.0)

View File

@@ -224,6 +224,13 @@ bool InputHandler::processEvents(Engine& engine) {
engine.changeScenario(7, "50,000 Pelotas");
break;
case SDLK_9:
if (engine.isCustomScenarioEnabled()) {
std::string custom_notif = std::to_string(engine.getCustomScenarioBalls()) + " Pelotas";
engine.changeScenario(CUSTOM_SCENARIO_IDX, custom_notif.c_str());
}
break;
// Controles de zoom dinámico (solo si no estamos en fullscreen)
case SDLK_F1:
if (engine.isKioskMode()) engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT);

View File

@@ -18,6 +18,7 @@ void printHelp() {
std::cout << " -F, --real-fullscreen Modo pantalla completa real (F4 - nativo)\n";
std::cout << " -k, --kiosk Modo kiosko (F4 fijo, sin ESC, sin zoom)\n";
std::cout << " -m, --mode <mode> Modo inicial: sandbox, demo, demo-lite, logo (default: sandbox)\n";
std::cout << " --custom-balls <n> Activa escenario custom (tecla 9) con N pelotas\n";
std::cout << " --help Mostrar esta ayuda\n\n";
std::cout << "Ejemplos:\n";
std::cout << " vibe3_physics # 320x240 zoom 3 (ventana 960x720)\n";
@@ -36,6 +37,7 @@ int main(int argc, char* argv[]) {
int width = 0;
int height = 0;
int zoom = 0;
int custom_balls = 0;
bool fullscreen = false;
bool real_fullscreen = false;
bool kiosk_mode = false;
@@ -104,6 +106,18 @@ int main(int argc, char* argv[]) {
std::cerr << "Error: -m/--mode requiere un valor\n";
return -1;
}
} else if (strcmp(argv[i], "--custom-balls") == 0) {
if (i + 1 < argc) {
int n = atoi(argv[++i]);
if (n < 1) {
std::cerr << "Error: --custom-balls requiere un valor >= 1\n";
return -1;
}
custom_balls = n;
} else {
std::cerr << "Error: --custom-balls requiere un valor\n";
return -1;
}
} else {
std::cerr << "Error: Opción desconocida '" << argv[i] << "'\n";
printHelp();
@@ -118,6 +132,9 @@ int main(int argc, char* argv[]) {
Engine engine;
if (custom_balls > 0)
engine.setCustomScenario(custom_balls); // pre-init: asigna campos antes del benchmark
if (!engine.initialize(width, height, zoom, fullscreen, initial_mode)) {
std::cout << "¡Error al inicializar el engine!" << std::endl;
return -1;

View File

@@ -44,7 +44,10 @@ void SceneManager::changeScenario(int scenario_id, SimulationMode mode) {
changeGravityDirection(GravityDirection::DOWN);
// Crear las bolas según el escenario
for (int i = 0; i < BALL_COUNT_SCENARIOS[scenario_id]; ++i) {
int ball_count = (scenario_id == CUSTOM_SCENARIO_IDX)
? custom_ball_count_
: BALL_COUNT_SCENARIOS[scenario_id];
for (int i = 0; i < ball_count; ++i) {
float X, Y, VX, VY;
// Inicialización según SimulationMode (RULES.md líneas 23-26)

View File

@@ -50,11 +50,17 @@ class SceneManager {
/**
* @brief Cambia el número de bolas según escenario
* @param scenario_id Índice del escenario (0-7 para 10 a 50,000 bolas)
* @param scenario_id Índice del escenario (0-7 para 10 a 50,000 bolas; 8 = custom)
* @param mode Modo de simulación actual (afecta inicialización)
*/
void changeScenario(int scenario_id, SimulationMode mode);
/**
* @brief Configura el número de bolas para el escenario custom (índice 8)
* @param n Número de bolas del escenario custom
*/
void setCustomBallCount(int n) { custom_ball_count_ = n; }
/**
* @brief Actualiza textura y tamaño de todas las bolas
* @param new_texture Nueva textura compartida
@@ -146,6 +152,7 @@ class SceneManager {
std::vector<std::unique_ptr<Ball>> balls_;
GravityDirection current_gravity_;
int scenario_;
int custom_ball_count_ = 0; // Número de bolas para escenario custom (índice 8)
// === Configuración de pantalla ===
int screen_width_;

View File

@@ -279,6 +279,8 @@ void UIManager::renderDebugHUD(const Engine* engine,
// Máximo de pelotas en modos automáticos (resultado del benchmark)
int max_auto_idx = engine->getMaxAutoScenario();
int max_auto_balls = BALL_COUNT_SCENARIOS[max_auto_idx];
if (engine->isCustomAutoAvailable() && engine->getCustomScenarioBalls() > max_auto_balls)
max_auto_balls = engine->getCustomScenarioBalls();
std::string max_auto_text;
if (max_auto_balls >= 1000) {
std::string count_str = std::to_string(max_auto_balls);