Merge branch 'feat/sdl-callbacks': migració a SDL_MAIN_USE_CALLBACKS
This commit is contained in:
@@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace Config {
|
namespace Config {
|
||||||
@@ -67,14 +66,4 @@ namespace Config {
|
|||||||
bool console{false};
|
bool console{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Capa de persistència delegada cap a l'EngineConfig. Permet al Director
|
|
||||||
// orquestrar init/load/save sense conèixer cap esquema concret (YAML,
|
|
||||||
// SQLite, ...) ni la capa que el conté (`game/config_yaml.cpp`).
|
|
||||||
struct ConfigPersistence {
|
|
||||||
std::function<void()> init_defaults; // Restaura valors per defecte
|
|
||||||
std::function<void(const std::string& path)> set_path; // Indica on guardar
|
|
||||||
std::function<bool()> load; // Llegeix path → EngineConfig
|
|
||||||
std::function<bool()> save; // Escriu EngineConfig → path
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
|
|||||||
+154
-137
@@ -21,6 +21,7 @@
|
|||||||
#include "core/system/notifier.hpp"
|
#include "core/system/notifier.hpp"
|
||||||
#include "core/utils/path_utils.hpp"
|
#include "core/utils/path_utils.hpp"
|
||||||
#include "debug_overlay.hpp"
|
#include "debug_overlay.hpp"
|
||||||
|
#include "game/config_yaml.hpp"
|
||||||
#include "game/scenes/game_scene.hpp"
|
#include "game/scenes/game_scene.hpp"
|
||||||
#include "game/scenes/logo_scene.hpp"
|
#include "game/scenes/logo_scene.hpp"
|
||||||
#include "game/scenes/title_scene.hpp"
|
#include "game/scenes/title_scene.hpp"
|
||||||
@@ -39,17 +40,15 @@ using SceneManager::SceneContext;
|
|||||||
using SceneType = SceneContext::SceneType;
|
using SceneType = SceneContext::SceneType;
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Director::Director(std::vector<std::string> const& args,
|
Director::Director(int argc, char* argv[])
|
||||||
Config::EngineConfig& cfg,
|
: cfg_(&ConfigYaml::engine_config) {
|
||||||
Config::ConfigPersistence persistence)
|
|
||||||
: cfg_(&cfg),
|
|
||||||
persistence_(std::move(persistence)) {
|
|
||||||
std::cout << "Orni Attack - Inici\n";
|
std::cout << "Orni Attack - Inici\n";
|
||||||
|
|
||||||
// Inicialitzar opciones con valors per defecte
|
// Inicialitzar opciones con valors per defecte
|
||||||
persistence_.init_defaults();
|
ConfigYaml::init();
|
||||||
|
|
||||||
// Comprovar arguments del programa
|
// Convertir arguments a std::vector<std::string> i comprovar-los
|
||||||
|
std::vector<std::string> args(argv, argv + argc);
|
||||||
executable_path_ = checkProgramArguments(args);
|
executable_path_ = checkProgramArguments(args);
|
||||||
|
|
||||||
// Inicialitzar sistema de rutes
|
// Inicialitzar sistema de rutes
|
||||||
@@ -95,10 +94,10 @@ Director::Director(std::vector<std::string> const& args,
|
|||||||
createSystemFolder(std::string("jailgames/") + Project::NAME);
|
createSystemFolder(std::string("jailgames/") + Project::NAME);
|
||||||
|
|
||||||
// Establir ruta del file de configuración
|
// Establir ruta del file de configuración
|
||||||
persistence_.set_path(system_folder_ + "/config.yaml");
|
ConfigYaml::setConfigFile(system_folder_ + "/config.yaml");
|
||||||
|
|
||||||
// Carregar o crear configuración
|
// Carregar o crear configuración
|
||||||
persistence_.load();
|
ConfigYaml::loadFromFile();
|
||||||
|
|
||||||
// Inicialitzar sistema de input
|
// Inicialitzar sistema de input
|
||||||
Input::init("data/gamecontrollerdb.txt");
|
Input::init("data/gamecontrollerdb.txt");
|
||||||
@@ -116,21 +115,72 @@ Director::Director(std::vector<std::string> const& args,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::cout << '\n';
|
std::cout << '\n';
|
||||||
|
|
||||||
|
// === Bootstrap de finestra, audio i subsistemes de runtime ===
|
||||||
|
|
||||||
|
int initial_width = static_cast<int>(std::round(
|
||||||
|
Defaults::Window::WIDTH * cfg_->window.zoom_factor));
|
||||||
|
int initial_height = static_cast<int>(std::round(
|
||||||
|
Defaults::Window::HEIGHT * cfg_->window.zoom_factor));
|
||||||
|
|
||||||
|
sdl_ = std::make_unique<SDLManager>(initial_width, initial_height, cfg_->window.fullscreen, *cfg_, [] { ConfigYaml::saveToFile(); });
|
||||||
|
|
||||||
|
// CRÍTIC: forçar ocultació del cursor DESPRÉS d'inicialitzar SDL,
|
||||||
|
// perquè la creació de la finestra el reactiva.
|
||||||
|
if (!cfg_->window.fullscreen) {
|
||||||
|
Mouse::forceHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Audio::Config AUDIO_CONFIG{
|
||||||
|
.enabled = Defaults::Audio::ENABLED,
|
||||||
|
.volume = Defaults::Audio::VOLUME,
|
||||||
|
.music_enabled = Defaults::Audio::MUSIC_ENABLED,
|
||||||
|
.music_volume = Defaults::Audio::MUSIC_VOLUME,
|
||||||
|
.sound_enabled = Defaults::Audio::SOUND_ENABLED,
|
||||||
|
.sound_volume = Defaults::Audio::SOUND_VOLUME,
|
||||||
|
};
|
||||||
|
Audio::init(AUDIO_CONFIG);
|
||||||
|
Audio::get()->applySettings(AUDIO_CONFIG);
|
||||||
|
|
||||||
|
AudioResource::getMusic("title.ogg");
|
||||||
|
AudioResource::getMusic("game.ogg");
|
||||||
|
if (cfg_->console) {
|
||||||
|
std::cout << "Música precacheada\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
context_ = std::make_unique<SceneContext>();
|
||||||
|
#ifdef _DEBUG
|
||||||
|
context_->setNextScene(SceneType::TITLE);
|
||||||
|
#else
|
||||||
|
context_->setNextScene(SceneType::LOGO);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
debug_overlay_ = std::make_unique<System::DebugOverlay>(
|
||||||
|
sdl_->getRenderer(),
|
||||||
|
cfg_->rendering);
|
||||||
|
|
||||||
|
System::Notifier::init(sdl_->getRenderer());
|
||||||
|
|
||||||
|
last_ticks_ms_ = SDL_GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
Director::~Director() {
|
Director::~Director() {
|
||||||
// Guardar opciones
|
// Guardar opciones
|
||||||
persistence_.save();
|
ConfigYaml::saveToFile();
|
||||||
|
|
||||||
|
// Destruir subsistemes en ordre invers a la construcció. El Notifier
|
||||||
|
// referencia el renderer, així que ha de morir abans que sdl_.
|
||||||
|
// SDL_Quit() el crida SDL automàticament després de SDL_AppQuit; no
|
||||||
|
// l'hem de cridar nosaltres.
|
||||||
|
current_scene_.reset();
|
||||||
|
debug_overlay_.reset();
|
||||||
|
System::Notifier::destroy();
|
||||||
|
context_.reset();
|
||||||
|
sdl_.reset();
|
||||||
|
|
||||||
// Cleanup input
|
|
||||||
Input::destroy();
|
Input::destroy();
|
||||||
|
|
||||||
// Cleanup audio
|
|
||||||
Audio::destroy();
|
Audio::destroy();
|
||||||
|
|
||||||
// Cleanup SDL
|
|
||||||
SDL_Quit();
|
|
||||||
|
|
||||||
std::cout << "\nAdéu!\n";
|
std::cout << "\nAdéu!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,8 +194,8 @@ auto Director::checkProgramArguments(std::vector<std::string> const& args)
|
|||||||
cfg_->console = true;
|
cfg_->console = true;
|
||||||
std::cout << "Mode consola activat\n";
|
std::cout << "Mode consola activat\n";
|
||||||
} else if (argument == "--reset-config") {
|
} else if (argument == "--reset-config") {
|
||||||
persistence_.init_defaults();
|
ConfigYaml::init();
|
||||||
persistence_.save();
|
ConfigYaml::saveToFile();
|
||||||
std::cout << "Configuración restablida als valors per defecte\n";
|
std::cout << "Configuración restablida als valors per defecte\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,75 +267,6 @@ void Director::createSystemFolder(const std::string& folder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bucle principal del juego
|
|
||||||
auto Director::run() -> int {
|
|
||||||
// Calculate initial size from saved zoom_factor
|
|
||||||
int initial_width = static_cast<int>(std::round(
|
|
||||||
Defaults::Window::WIDTH * cfg_->window.zoom_factor));
|
|
||||||
int initial_height = static_cast<int>(std::round(
|
|
||||||
Defaults::Window::HEIGHT * cfg_->window.zoom_factor));
|
|
||||||
|
|
||||||
// Crear gestor SDL amb la engine_config + callback de persistència
|
|
||||||
// per a quan toggleVSync (F4) muti vsync. Mantenim sdl_manager agnòstic.
|
|
||||||
SDLManager sdl(initial_width, initial_height, cfg_->window.fullscreen, *cfg_, [this] { persistence_.save(); });
|
|
||||||
|
|
||||||
// CRÍTIC: Forçar ocultació del cursor DESPRÉS de toda la inicialización SDL
|
|
||||||
// Això evita que SDL mostre el cursor automàticament durante la creació de la finestra
|
|
||||||
if (!cfg_->window.fullscreen) {
|
|
||||||
Mouse::forceHide();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inicializar sistema de audio (config inyectada desde Defaults)
|
|
||||||
const Audio::Config AUDIO_CONFIG{
|
|
||||||
.enabled = Defaults::Audio::ENABLED,
|
|
||||||
.volume = Defaults::Audio::VOLUME,
|
|
||||||
.music_enabled = Defaults::Audio::MUSIC_ENABLED,
|
|
||||||
.music_volume = Defaults::Audio::MUSIC_VOLUME,
|
|
||||||
.sound_enabled = Defaults::Audio::SOUND_ENABLED,
|
|
||||||
.sound_volume = Defaults::Audio::SOUND_VOLUME,
|
|
||||||
};
|
|
||||||
Audio::init(AUDIO_CONFIG);
|
|
||||||
Audio::get()->applySettings(AUDIO_CONFIG); // Aplicar volúmenes iniciales al motor
|
|
||||||
|
|
||||||
// Precachear música para evitar lag al empezar
|
|
||||||
AudioResource::getMusic("title.ogg");
|
|
||||||
AudioResource::getMusic("game.ogg");
|
|
||||||
if (cfg_->console) {
|
|
||||||
std::cout << "Música precacheada\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crear context de escenes
|
|
||||||
SceneContext context;
|
|
||||||
#ifdef _DEBUG
|
|
||||||
context.setNextScene(SceneType::TITLE);
|
|
||||||
#else
|
|
||||||
context.setNextScene(SceneType::LOGO);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Overlay de debug (FPS + VSync). Vive en el Director porque es global
|
|
||||||
// a todas las escenas. Toggle con F11 (visible por defecto en _DEBUG).
|
|
||||||
System::DebugOverlay debug_overlay(sdl.getRenderer(), cfg_->rendering);
|
|
||||||
|
|
||||||
// Sistema de notificacions toast: singleton accessible des d'on calgui
|
|
||||||
// (F1-F5 a sdl_manager, ESC a global_events). El renderer ha de viure
|
|
||||||
// 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();
|
|
||||||
std::unique_ptr<Scene> scene = buildScene(context.nextScene(), sdl, context);
|
|
||||||
if (!scene) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
runFrameLoop(*scene, sdl, context, debug_overlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
SceneManager::actual = SceneType::EXIT;
|
|
||||||
System::Notifier::destroy();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Director::buildScene(SceneType type, SDLManager& sdl, SceneContext& context)
|
auto Director::buildScene(SceneType type, SDLManager& sdl, SceneContext& context)
|
||||||
-> std::unique_ptr<Scene> {
|
-> std::unique_ptr<Scene> {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -301,55 +282,91 @@ auto Director::buildScene(SceneType type, SDLManager& sdl, SceneContext& context
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Director::runFrameLoop(Scene& scene, SDLManager& sdl, SceneContext& context, System::DebugOverlay& debug_overlay) {
|
auto Director::advanceScene() -> SDL_AppResult {
|
||||||
SDL_Event event;
|
current_scene_.reset();
|
||||||
Uint64 last_time = SDL_GetTicks();
|
const SceneType NEXT = context_->nextScene();
|
||||||
|
if (NEXT == SceneType::EXIT) {
|
||||||
while (!scene.isFinished()) {
|
SceneManager::actual = SceneType::EXIT;
|
||||||
// Delta time real, capeado a 50ms para evitar grandes saltos.
|
return SDL_APP_SUCCESS;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -15,22 +17,38 @@ namespace System {
|
|||||||
|
|
||||||
class Director {
|
class Director {
|
||||||
public:
|
public:
|
||||||
// `cfg` ha de viure tant com el Director (típicament owned per main).
|
// El Director és el programa: posseeix la configuració (via ConfigYaml)
|
||||||
// `persistence` encapsula init/load/save delegats a la capa concreta
|
// i orquestra tots els subsistemes. main.cpp és pur tràmit que el
|
||||||
// (game/ConfigYaml::*).
|
// construeix i delega cap a SDL.
|
||||||
Director(std::vector<std::string> const& args,
|
Director(int argc, char* argv[]);
|
||||||
Config::EngineConfig& cfg,
|
|
||||||
Config::ConfigPersistence persistence);
|
|
||||||
~Director();
|
~Director();
|
||||||
|
|
||||||
// Bucle principal del juego.
|
// Una iteració del bucle: pivot d'escena si cal, delta time, update i
|
||||||
auto run() -> int;
|
// 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:
|
private:
|
||||||
std::string executable_path_;
|
std::string executable_path_;
|
||||||
std::string system_folder_;
|
std::string system_folder_;
|
||||||
Config::EngineConfig* cfg_;
|
Config::EngineConfig* cfg_{nullptr};
|
||||||
Config::ConfigPersistence persistence_;
|
|
||||||
|
// Subsistemes que viuen tant com el Director (abans eren locals de run()).
|
||||||
|
// Preparació per a la migració a SDL_MAIN_USE_CALLBACKS: amb les 4
|
||||||
|
// callbacks de SDL3 no hi ha un scope que englobi tot el bucle, així
|
||||||
|
// que cal que aquest estat sigui membre del Director.
|
||||||
|
std::unique_ptr<SDLManager> sdl_;
|
||||||
|
std::unique_ptr<SceneManager::SceneContext> context_;
|
||||||
|
std::unique_ptr<System::DebugOverlay> debug_overlay_;
|
||||||
|
std::unique_ptr<Scene> current_scene_;
|
||||||
|
|
||||||
|
Uint64 last_ticks_ms_{0};
|
||||||
|
bool wants_quit_{false};
|
||||||
|
|
||||||
auto checkProgramArguments(std::vector<std::string> const& args)
|
auto checkProgramArguments(std::vector<std::string> const& args)
|
||||||
-> std::string;
|
-> std::string;
|
||||||
@@ -43,8 +61,8 @@ class Director {
|
|||||||
SceneManager::SceneContext& context)
|
SceneManager::SceneContext& context)
|
||||||
-> std::unique_ptr<Scene>;
|
-> std::unique_ptr<Scene>;
|
||||||
|
|
||||||
// Ejecuta el bucle de frames de UNA escena hasta que scene.isFinished()
|
// Pivota a la següent escena: destrueix l'actual, llegeix context_->nextScene()
|
||||||
// sea true. Maneja delta_time, eventos (globales + escena), update y draw.
|
// i construeix la nova. Retorna SDL_APP_SUCCESS si la nova és EXIT o no es pot
|
||||||
// El debug_overlay es global a todas las escenas; el Director lo posee.
|
// construir; SDL_APP_CONTINUE si tot OK.
|
||||||
static void runFrameLoop(Scene& scene, SDLManager& sdl, SceneManager::SceneContext& context, System::DebugOverlay& debug_overlay);
|
auto advanceScene() -> SDL_AppResult;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
//
|
//
|
||||||
// La configuració runtime viu en Config::EngineConfig (core/config/).
|
// La configuració runtime viu en Config::EngineConfig (core/config/).
|
||||||
// Aquest fitxer afegeix una capa de persistència YAML que llegeix i
|
// Aquest fitxer afegeix una capa de persistència YAML que llegeix i
|
||||||
// escriu aquesta struct a disc. La connexió amb el Director es fa via
|
// escriu aquesta struct a disc. El Director crida ConfigYaml::* directament
|
||||||
// Config::ConfigPersistence (lambdes a `main.cpp`), mantenint `core/`
|
// (init / setConfigFile / loadFromFile / saveToFile): la separació
|
||||||
// agnòstic respecte d'aquesta capa.
|
// core/game queda relaxada al Director, que és EL programa, no part del motor.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
+29
-26
@@ -1,33 +1,36 @@
|
|||||||
// main.cpp - Punt d'entrada de l'aplicació
|
// main.cpp - Punt d'entrada amb SDL_MAIN_USE_CALLBACKS
|
||||||
// © 2026 JailDesigner
|
// © 2026 JailDesigner
|
||||||
//
|
//
|
||||||
// Aquí orquestrem la capa de persistència (YAML via game/ConfigYaml::*) i
|
// El Director és EL programa: posseeix la configuració, els subsistemes i
|
||||||
// injectem el resultat al Director. El Director queda independent de
|
// l'estat. Aquestes 4 callbacks són la fontaneria mínima que SDL3 demana
|
||||||
// game/config_yaml.hpp i pot operar només amb Config::EngineConfig.
|
// per arrencar, processar events, iterar i tancar.
|
||||||
|
|
||||||
#include <string>
|
#define SDL_MAIN_USE_CALLBACKS 1
|
||||||
#include <vector>
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3/SDL_main.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "core/config/engine_config.hpp"
|
|
||||||
#include "core/system/director.hpp"
|
#include "core/system/director.hpp"
|
||||||
#include "game/config_yaml.hpp"
|
|
||||||
|
|
||||||
auto main(int argc, char* argv[]) -> int {
|
auto SDL_AppInit(void** appstate, int argc, char* argv[]) -> SDL_AppResult {
|
||||||
// Convertir arguments a std::vector<std::string>
|
auto director = std::make_unique<Director>(argc, argv);
|
||||||
std::vector<std::string> args(argv, argv + argc);
|
*appstate = director.release();
|
||||||
|
return SDL_APP_CONTINUE;
|
||||||
// Capa de persistència delegada: lambdes prim que enllacen el contracte
|
}
|
||||||
// de Config::ConfigPersistence amb la implementació YAML de ConfigYaml::*.
|
|
||||||
const Config::ConfigPersistence PERSISTENCE{
|
auto SDL_AppEvent(void* appstate, SDL_Event* event) -> SDL_AppResult {
|
||||||
.init_defaults = [] { ConfigYaml::init(); },
|
auto* director = static_cast<Director*>(appstate);
|
||||||
.set_path = [](const std::string& path) { ConfigYaml::setConfigFile(path); },
|
return director->handleEvent(*event);
|
||||||
.load = [] { return ConfigYaml::loadFromFile(); },
|
}
|
||||||
.save = [] { return ConfigYaml::saveToFile(); },
|
|
||||||
};
|
auto SDL_AppIterate(void* appstate) -> SDL_AppResult {
|
||||||
|
auto* director = static_cast<Director*>(appstate);
|
||||||
// El Director rep la struct d'engine_config + la capa de persistència.
|
return director->iterate();
|
||||||
// No coneix ConfigYaml:: directament.
|
}
|
||||||
Director director(args, ConfigYaml::engine_config, PERSISTENCE);
|
|
||||||
|
void SDL_AppQuit(void* appstate, SDL_AppResult /*result*/) {
|
||||||
return director.run();
|
// Reabsorbim la propietat: el destructor del Director allibera tot.
|
||||||
|
std::unique_ptr<Director> director(static_cast<Director*>(appstate));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user