refactor(director): extreu iterate/handleEvent/advanceScene del runFrameLoop

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.
This commit is contained in:
2026-05-22 12:38:16 +02:00
parent 8bb052981d
commit 120b8ada38
2 changed files with 126 additions and 67 deletions
+107 -63
View File
@@ -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<float>(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<float>(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;
}