Repository navigation
Core Architettura
Game è il coordinatore dell'applicazione. È un singleton polimorfico: ogni progetto crea una propria sottoclasse, la registra con Game::setInstance() e ridefinisce almeno init().
| Componente | Responsabilità |
|---|---|
Game |
Conserva la pila di scene, misura il tempo, distribuisce eventi e governa il ciclo. |
Window |
Possiede SDL_Window e SDL_Renderer, pulisce e presenta il frame. |
Scene |
Organizza oggetti, aggiornamento, query e rendering della propria area. |
GameScene |
Aggiunge simulazione con semi-fixed timestep, camera, overlay e indice spaziale. |
UIScene |
Aggiorna l'interfaccia con il tempo effettivo del frame. |
Una sequenza tipica è:
agp::Game::setInstance(new MyGame());
agp::Game::instance()->init();
agp::Game::instance()->run();Il costruttore di Game crea e inizializza la finestra, quindi il renderer è disponibile prima di creare texture nelle factory del progetto. init() non viene chiamato automaticamente dal costruttore: il main() dei prototipi lo invoca esplicitamente prima di run(). Alla fine del ciclo, Game::destroy() elimina le scene, la finestra e chiama SDL_Quit().
Il costruttore di Game sceglie anche il sistema di rendering:
| Modalità | Finestra creata | Uso |
|---|---|---|
Rendering::SDL |
Window |
Rendering SDL3 normale. |
Rendering::SDL_CPU_SHADERS |
CPUShaderWindow |
Post-processing eseguito sulla CPU. |
Rendering::SDL_GPU_SHADERS |
GPUShaderWindow |
Post-processing SDL_GPU opzionale, con effetti CPU ancora disponibili. |
sequenceDiagram
participant G as Game
participant S as Scene stack
participant W as Window
G->>G: processEvents()
G->>S: update(frameTime)
Note over S: dall'alto verso il basso<br/>fino a una scena blocking
G->>W: render(scenes)
W->>S: render()
Note over S: dal basso verso l'alto
W->>W: SDL_RenderPresent()
Il tempo trascorso dall'ultimo frame viene misurato e passato alle scene. Game calcola anche gli FPS correnti e supporta una scala temporale utile per debug e dimostrazioni.
L'ordine è preciso: prima tutti gli eventi SDL in attesa, poi il tempo del frame, quindi l'aggiornamento delle scene dall'alto della pila verso il basso e infine il rendering dal basso verso l'alto. Window::render() pulisce il renderer una volta sola e chiama SDL_RenderPresent() alla fine. Il VSync viene richiesto quando viene creato il renderer SDL.
Per seguire le chiamate dall'alto verso il basso, usa Dal game loop al singolo oggetto: diagramma di sequenza. La pagina sviluppa il frame completo e poi approfondisce l'aggiornamento di Mario e la rimozione degli oggetti.
Le scene sono organizzate come una pila:
-
pushScene()aggiunge una scena in primo piano; -
popSceneLater()richiede la rimozione in modo differito; -
blocking()interrompe la propagazione di update ed eventi verso le scene sottostanti; - il rendering continua a includere le scene sottostanti, utile per menu in sovraimpressione.
blocking riguarda eventi e aggiornamento, non la visibilità. Una scena sovrapposta può quindi fermare il gioco senza nasconderlo. L'aggiornamento parte dalla scena in cima; quando incontra una scena bloccante, non raggiunge quelle più in basso. popScene() elimina subito la scena in cima; popSceneLater() ne rinvia la rimozione al successivo processEvents(), scelta utile mentre si gestisce un evento. Anche reset() rinvia la ricostruzione delle scene: le elimina e richiama init() nel ciclo seguente.
Gli eventi di finestra vengono distribuiti a tutte le scene, perché un ridimensionamento può cambiare ogni viewport. Gli altri eventi procedono dalla scena superiore verso il basso fino alla prima scena bloccante.
GameScene usa un semi-fixed timestep. Accumula il tempo reale e aggiorna gli oggetti con passi costanti _dt; questo rende più stabile la simulazione.
UIScene usa invece il tempo reale del frame. Per menu, cursori e HUD è spesso preferibile una risposta immediata senza simulazione fisica.
Per esempio, se _dt vale 0,01 s e un frame dura 0,025 s, la simulazione esegue due update da 0,01 s e conserva 0,005 s nell'accumulatore. GameScene::updateControls() e updateCamera() ricevono invece il delta del frame; updateWorld() usa l'accumulatore. Il codice non impone un massimo di passi di recupero: un frame molto lungo può produrre molti update consecutivi.
Game moltiplica il delta per un fattore di scala temporale prima di consegnarlo alle scene. I tasti Ctrl+Shift+Su/Giù cambiano quel fattore; non cambiano _dt e non sono un controllo dell'FPS. currentFPS() riporta la misura aggiornata periodicamente dal contatore dei frame.
Il refresh del monitor e il VSync regolano la presentazione dei frame; _dt regola la simulazione. In Box2DPlatformer anche i controlli che applicano forze vengono eseguiti a ogni passo fisico tramite updatePhysicsControls(), e ogni oggetto riceve update(_dt). Questo evita che il movimento o le animazioni cambino velocità passando, per esempio, a un proiettore a 60 Hz. I dettagli sono in World, body e contatti.
Il punto normale di estensione non è riscrivere run(), ma:
- ridefinire
Game::init()per costruire le scene; - ridefinire
GameScene::updateControls()per i comandi; - ridefinire
Scene::event()per eventi specifici; - ridefinire
Object::update()per il comportamento degli oggetti.
In questo modo il ciclo principale rimane unico e prevedibile.
Per capire come blocking e input interagiscono, prosegui con Input ed eventi.
- Panoramica
- Diagramma delle classi
- Architettura e game loop
- Sequenza di un frame
- Input ed eventi
- Scene e oggetti
- Rendering e coordinate
- Sprite e testo
- UI e overlay
- Quadtree
- Servizi e asset
- Estendere il core
- Editor dei livelli
- LevelData e livelli JSON
- Shader