From 120b8ada3851156689b9fb2ba6948f0c5d338c22 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Fri, 22 May 2026 12:38:16 +0200 Subject: [PATCH] refactor(director): extreu iterate/handleEvent/advanceScene del runFrameLoop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit run() ara delega a iterate() i handleEvent() per cada frame. runFrameLoop desapareix; la seva lògica es divideix entre els tres nous mètodes. La primera escena es construeix lazy via advanceScene() dins d'iterate(). Cap canvi de comportament visible. --- source/core/system/director.cpp | 170 ++++++++++++++++++++------------ source/core/system/director.hpp | 23 ++++- 2 files changed, 126 insertions(+), 67 deletions(-) diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index b7b9420..cde1a13 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -282,20 +282,28 @@ auto Director::run() -> int { // tant com el Notifier; el destruim explícitament abans de tornar. System::Notifier::init(sdl_->getRenderer()); - // Bucle principal: construir escena → frame loop → destruir → siguiente. - while (context_->nextScene() != SceneType::EXIT) { - SceneManager::actual = context_->nextScene(); - current_scene_ = buildScene(context_->nextScene(), *sdl_, *context_); - if (!current_scene_) { - break; - } - runFrameLoop(*current_scene_, *sdl_, *context_, *debug_overlay_); - current_scene_.reset(); - } + // Comptador de delta time per al primer iterate(). + last_ticks_ms_ = SDL_GetTicks(); - SceneManager::actual = SceneType::EXIT; - System::Notifier::destroy(); - return 0; + // Bucle principal: poll d'events + iterate() per frame. La primera escena + // es construeix lazy dins d'iterate() via advanceScene(). En migrar a + // SDL_MAIN_USE_CALLBACKS, aquest while desapareixerà i SDL_AppEvent/ + // SDL_AppIterate cridaran handleEvent()/iterate() directament. + while (true) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + SDL_AppResult r = handleEvent(event); + if (r != SDL_APP_CONTINUE) { + System::Notifier::destroy(); + return (r == SDL_APP_SUCCESS) ? 0 : 1; + } + } + SDL_AppResult r = iterate(); + if (r != SDL_APP_CONTINUE) { + System::Notifier::destroy(); + return (r == SDL_APP_SUCCESS) ? 0 : 1; + } + } } auto Director::buildScene(SceneType type, SDLManager& sdl, SceneContext& context) @@ -313,55 +321,91 @@ auto Director::buildScene(SceneType type, SDLManager& sdl, SceneContext& context } } -void Director::runFrameLoop(Scene& scene, SDLManager& sdl, SceneContext& context, System::DebugOverlay& debug_overlay) { - SDL_Event event; - Uint64 last_time = SDL_GetTicks(); - - while (!scene.isFinished()) { - // Delta time real, capeado a 50ms para evitar grandes saltos. - const Uint64 NOW = SDL_GetTicks(); - float delta_time = static_cast(NOW - last_time) / 1000.0F; - last_time = NOW; - delta_time = std::min(delta_time, 0.05F); - - Mouse::updateCursorVisibility(); - Input::get()->update(); - - // Event loop: primero ventana, después globales, después F11 - // (toggle del overlay), después escena. - while (SDL_PollEvent(&event)) { - if (sdl.handleWindowEvent(event)) { - continue; - } - if (GlobalEvents::handle(event, sdl, context)) { - continue; - } - if (event.type == SDL_EVENT_KEY_DOWN && event.key.scancode == SDL_SCANCODE_F11) { - debug_overlay.toggle(); - continue; - } - scene.handleEvent(event); - } - - scene.update(delta_time); - debug_overlay.update(delta_time); - if (auto* notifier = System::Notifier::get(); notifier != nullptr) { - notifier->update(delta_time); - } - Audio::update(); - - // Si la swapchain no está disponible (ventana minimizada, etc.), - // saltarse draw+present ese frame: dibujar dejaría vértices - // colgando en el batch interno sin nadie que los presente. - if (!sdl.clear(0, 0, 0)) { - continue; - } - sdl.updateRenderingContext(); - scene.draw(); - debug_overlay.draw(); // sempre per damunt de l'escena - if (const auto* notifier = System::Notifier::get(); notifier != nullptr) { - notifier->draw(); // toast: per damunt de tot - } - sdl.present(); +auto Director::advanceScene() -> SDL_AppResult { + current_scene_.reset(); + const SceneType NEXT = context_->nextScene(); + if (NEXT == SceneType::EXIT) { + SceneManager::actual = SceneType::EXIT; + return SDL_APP_SUCCESS; } + SceneManager::actual = NEXT; + current_scene_ = buildScene(NEXT, *sdl_, *context_); + if (!current_scene_) { + SceneManager::actual = SceneType::EXIT; + return SDL_APP_SUCCESS; + } + return SDL_APP_CONTINUE; +} + +auto Director::handleEvent(const SDL_Event& event) -> SDL_AppResult { + // 1. Window events (resize, minimize, focus...) + if (sdl_->handleWindowEvent(event)) { + return SDL_APP_CONTINUE; + } + + // 2. Events globals (F1-F6, ESC, QUIT, gamepad hotplug). + // GlobalEvents marca context_->nextScene() = EXIT en ESC doble o QUIT; + // activem la bandera per fer-ho fluir cap a SDL_APP_SUCCESS al pròxim tick. + if (GlobalEvents::handle(event, *sdl_, *context_)) { + if (context_->nextScene() == SceneType::EXIT) { + wants_quit_ = true; + } + return SDL_APP_CONTINUE; + } + + // 3. F11 → toggle del debug overlay (cas especial fora de GlobalEvents). + if (event.type == SDL_EVENT_KEY_DOWN && event.key.scancode == SDL_SCANCODE_F11) { + debug_overlay_->toggle(); + return SDL_APP_CONTINUE; + } + + // 4. Esdeveniment específic de l'escena actual. + if (current_scene_) { + current_scene_->handleEvent(event); + } + return SDL_APP_CONTINUE; +} + +auto Director::iterate() -> SDL_AppResult { + if (wants_quit_) { + return SDL_APP_SUCCESS; + } + + // Pivotar a la següent escena si l'actual ha acabat (o és la primera). + if (!current_scene_ || current_scene_->isFinished()) { + SDL_AppResult pivot = advanceScene(); + if (pivot != SDL_APP_CONTINUE) { + return pivot; + } + } + + // Delta time real, capeado a 50ms per evitar grans salts. + const Uint64 NOW = SDL_GetTicks(); + float delta_time = static_cast(NOW - last_ticks_ms_) / 1000.0F; + last_ticks_ms_ = NOW; + delta_time = std::min(delta_time, 0.05F); + + Mouse::updateCursorVisibility(); + Input::get()->update(); + + current_scene_->update(delta_time); + debug_overlay_->update(delta_time); + if (auto* notifier = System::Notifier::get(); notifier != nullptr) { + notifier->update(delta_time); + } + Audio::update(); + + // Si la swapchain no està disponible (finestra minimitzada, etc.), + // saltar-se draw+present aquest frame. + if (!sdl_->clear(0, 0, 0)) { + return SDL_APP_CONTINUE; + } + sdl_->updateRenderingContext(); + current_scene_->draw(); + debug_overlay_->draw(); // sempre per damunt de l'escena + if (const auto* notifier = System::Notifier::get(); notifier != nullptr) { + notifier->draw(); // toast: per damunt de tot + } + sdl_->present(); + return SDL_APP_CONTINUE; } diff --git a/source/core/system/director.hpp b/source/core/system/director.hpp index 206722a..158eb26 100644 --- a/source/core/system/director.hpp +++ b/source/core/system/director.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -26,6 +28,16 @@ class Director { // Bucle principal del juego. auto run() -> int; + // Una iteració del bucle: pivot d'escena si cal, delta time, update i + // render. Retorna SDL_APP_CONTINUE per seguir, SDL_APP_SUCCESS si vol + // sortir net, SDL_APP_FAILURE si no es pot recuperar. + auto iterate() -> SDL_AppResult; + + // Enruta un sol esdeveniment cap a la cadena finestra → globals → F11 → + // escena. Si detecta sortida (ESC doble, QUIT) marca wants_quit_ perquè + // el següent iterate() retorni SDL_APP_SUCCESS. + auto handleEvent(const SDL_Event& event) -> SDL_AppResult; + private: std::string executable_path_; std::string system_folder_; @@ -41,6 +53,9 @@ class Director { std::unique_ptr debug_overlay_; std::unique_ptr current_scene_; + Uint64 last_ticks_ms_{0}; + bool wants_quit_{false}; + auto checkProgramArguments(std::vector const& args) -> std::string; void createSystemFolder(const std::string& folder); @@ -52,8 +67,8 @@ class Director { SceneManager::SceneContext& context) -> std::unique_ptr; - // Ejecuta el bucle de frames de UNA escena hasta que scene.isFinished() - // sea true. Maneja delta_time, eventos (globales + escena), update y draw. - // El debug_overlay es global a todas las escenas; el Director lo posee. - static void runFrameLoop(Scene& scene, SDLManager& sdl, SceneManager::SceneContext& context, System::DebugOverlay& debug_overlay); + // Pivota a la següent escena: destrueix l'actual, llegeix context_->nextScene() + // i construeix la nova. Retorna SDL_APP_SUCCESS si la nova és EXIT o no es pot + // construir; SDL_APP_CONTINUE si tot OK. + auto advanceScene() -> SDL_AppResult; };