Skip to content

Core Servizi

Alessandro Bria edited this page Oct 31, 2026 · 7 revisions

Servizi e risorse

Alcune funzionalità devono essere condivise da tutto il programma. Il core le raccoglie in servizi accessibili tramite il template Singleton.

Singleton

Singleton<T> conserva una sola istanza di un tipo e la espone con instance().

Audio::instance()->playSound("jump");
auto font = Fonts::instance()->font("Lucida");

setInstance() permette anche un uso polimorfico. Game, per esempio, può registrare come istanza globale un oggetto di una classe derivata definita dal progetto.

Le istanze vengono create al primo accesso e devono essere distrutte con destroyInstance() durante la chiusura. Il pattern è comodo per servizi realmente globali, ma non va usato come sostituto automatico del passaggio esplicito delle dipendenze.

instance() costruisce l'oggetto soltanto alla prima richiesta. setInstance() registra un puntatore già creato ed elimina l'eventuale istanza precedente; se il puntatore è già quello registrato, non fa nulla. Questo consente a Game* di riferirsi alla sottoclasse scelta dal progetto. Il distruttore del template azzera il riferimento anche se l'oggetto viene eliminato da un proprietario esterno, come accade all'HUD singleton di ActionRPG quando la scena viene rimossa. Evita di chiamare instance() dopo destroyInstance() durante la chiusura, perché ne creerebbe una nuova.

Avvio e chiusura dei servizi

Nei quattro prototipi, il costruttore di Game crea la finestra e il renderer. Il costruttore di PlatformerGame o RPGGame inizializza poi SpriteFactory, Audio e, con WITH_TTF, Fonts. LevelLoader non è un singleton: offre funzioni statiche per caricare i livelli.

SpriteFactory ha un costruttore vuoto; il gioco chiama SpriteFactory::instance()->init(window()->renderer()). Ricevere il renderer evita di richiamare Game::instance() mentre il gioco è ancora in costruzione. La factory carica le texture e le libera nel proprio distruttore.

Dopo la costruzione, il main registra il gioco e chiama init() per creare livelli, HUD e menu, quindi run(). Il reset ricostruisce le scene tramite init(), senza ricaricare i servizi.

Alla chiusura, il distruttore del gioco concreto elimina prima le scene con clearScenes() e poi i servizi tramite releaseServices() privato; quello di Game libera la finestra. In ActionRPG viene gestita anche l'istanza HUD. Infine il main chiama SDL_Quit(). Se l'inizializzazione dei servizi fallisce nel costruttore, un catch (...) chiama releaseServices() e rilancia: il distruttore della classe non verrebbe eseguito per un oggetto la cui costruzione è fallita.

Audio

Audio inizializza SDL_mixer e carica una sola volta le risorse presenti in:

assets/sounds/*.wav
assets/musics/*.wav
assets/musics/*.mp3

Le musiche MP3 sono usate anche da CustomPlatformerSlopes. Se esistono un WAV e un MP3 con lo stesso nome, viene preferito il WAV.

L'identificatore usato dal codice deriva dal nome del file. I suoni dispongono di più tracce, così lo stesso effetto può sovrapporsi a sé stesso; la musica usa invece una traccia dedicata.

Il servizio espone operazioni per riprodurre, fermare, mettere in pausa e riprendere suoni e musica. La riproduzione in loop è controllata dal parametro loops.

Audio crea un mixer SDL3_mixer e una traccia dedicata alla musica. Ogni chiamata a playSound() può creare una nuova traccia per quel suono; forceReplay ferma le tracce precedenti dello stesso ID prima di riprodurlo. stopSound(id) agisce su tutte le tracce di quell'effetto. Per la musica ci sono playMusic(), pauseMusic(), resumeMusic() e haltMusic(). Gli ID sono i nomi dei file senza estensione: assets/sounds/jump.wav diventa "jump".

Se un ID audio non esiste, il servizio non può riprodurre il file; controlla prima il nome e la cartella esportata accanto all'eseguibile. pauseAllLoopingSounds() e resumeAllLoopingSounds() riguardano gli effetti in loop, non sostituiscono i comandi separati per la musica.

toggleMute() attiva o disattiva il volume dell'intero mixer, quindi agisce sia sulla musica sia sugli effetti senza fermarne la riproduzione. Nei quattro prototipi il tasto V richiama questa funzione, anche nei menu e in pausa. Durante l'inserimento di testo il comando è sospeso, per consentire di scrivere la lettera V.

Font

Fonts è disponibile quando il core viene compilato con WITH_TTF. Carica i file trovati in:

assets/fonts/

I font vengono aperti a una risoluzione di rasterizzazione comune e sono poi usati da TextSprite per generare le texture del testo. Anche qui l'accesso avviene per nome, senza riaprire il file a ogni scritta.

Fonts::font(name) restituisce un TTF_Font* caricato in precedenza; se il nome non compare nella mappa, segnala l'assenza e restituisce nullptr. TextSprite usa questo font per generare una texture soltanto quando cambia testo o colore. La dimensione finale della scritta viene calcolata durante il rendering in base al rettangolo dell'oggetto, ai margini e ai limiti opzionali.

Scheduler

Lo Scheduler appartiene a ogni Scene e consente di eseguire una funzione dopo un intervallo, una sola volta o periodicamente. Anche Object espone metodi di scheduling, collegando le attività alla scena che lo contiene.

È adatto a eventi di gioco come cooldown, animazioni ritardate e transizioni. Non crea thread: le funzioni vengono eseguite durante l'aggiornamento della scena.

Per i dettagli su loop, congelamento degli oggetti e rimozione differita degli scheduler, vedi Scene, oggetti e scheduling.

Percorso degli asset

I servizi costruiscono i percorsi a partire da SDL_GetBasePath(), cioè dalla cartella dell'eseguibile, e poi cercano la sottocartella assets.

cartella-eseguibile/
|-- gioco
`-- assets/
    |-- fonts/
    |-- musics/
    `-- sounds/

Per rispettare questo contratto, i progetti copiano gli asset accanto all'eseguibile con un comando post-build di CMake. Il programma non dipende quindi dalla cartella dalla quale viene avviato.

Le SpriteFactory dei progetti leggono invece la cartella sprites/ accanto all'eseguibile; altri contenuti, come livelli o shader, mantengono cartelle proprie. Il principio resta lo stesso: il riferimento è la directory dell'eseguibile, non la directory corrente da cui si lancia il programma.

CustomPlatformer, Box2DPlatformer e ActionRPG caricano gli shader GPU da assets/shaders/ accanto all'eseguibile; CMake copia i formati precompilati dalla cartella condivisa postprocessing/assets/shaders/.

Risorsa Percorso tipico accanto all'eseguibile Caricatore
Sprite e spritesheet sprites/ SpriteFactory del progetto
Effetti sonori assets/sounds/ Audio del core
Musica assets/musics/ Audio del core
Font assets/fonts/ Fonts del core, con WITH_TTF
Livelli dei quattro prototipi levels/<name>.json LevelData e LevelLoader del progetto
Shader GPU assets/shaders/ GPUShaderWindow, con WITH_SHADERS

I CMakeLists.txt dei quattro progetti contengono comandi POST_BUILD con copy_directory verso $<TARGET_FILE_DIR:...>. Se un asset compare nel repository ma non nel gioco avviato, verifica sia il percorso atteso dal caricatore sia che la copia post-build sia stata eseguita per la configurazione e il target effettivamente lanciati.

Per i JSON dei livelli, i quattro progetti usano invece il target copy_levels, incluso in ogni build: la copia viene aggiornata anche se il C++ non cambia. L'editor salva la copia accanto all'eseguibile; per conservarne le modifiche, riportala in levels/ del rispettivo progetto prima della build successiva. Vedi LevelData e livelli JSON.

Dipendenze opzionali

Il file CMake del core collega SDL3 e SDL3_image. Le altre capacità sono abilitate in base alle opzioni del progetto:

Opzione Funzionalità
WITH_TTF Fonts e TextSprite con SDL3_ttf
WITH_SHADERS post-processing GPU con SDL_GPU (richiede SDL3 3.4 o successiva)

SDL3_mixer è attualmente una dipendenza del core ed è collegato sempre, perché il servizio Audio fa parte della libreria.

Sorgenti

Prosegui con Come estendere il core.

Clone this wiki locally