Skip to content

Core Estendere il Core

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

Come estendere il core

Il core offre il ciclo e le strutture comuni; ogni progetto decide quali scene creare, quali oggetti istanziare e come implementare regole e collisioni. Questa pagina collega le classi del core in un percorso di lavoro concreto.

1. Componi l'applicazione

Una sottoclasse di Game configura titolo, dimensioni, rapporto d'aspetto e rendering nel costruttore e inizializza i servizi dopo che la finestra è pronta. La SpriteFactory riceve il renderer tramite init(renderer). Il main registra il gioco con Game::setInstance() e chiama init() e run(). In init() si costruiscono soltanto scene, HUD e menu: il reset richiama questo metodo senza ricreare i servizi. Il distruttore concreto libera prima le scene e poi i servizi; la classe base libera la finestra. Per il caso di errore durante la costruzione, vedi Servizi e risorse.

Prima di creare una GameScene, deve esistere il Game: il costruttore della scena crea una View che legge Game::instance()->aspectRatio(). Anche le texture hanno bisogno del renderer, disponibile dopo la costruzione di Game.

2. Definisci la scena di gioco

Deriva da GameScene e ricevi nel costruttore dimensioni della mappa, pixelUnitSize, intervallo _dt e colore di sfondo, definiti nel loader C++. Configura la vista iniziale nella scena concreta. Usa updateControls() per i comandi continui (in Box2DPlatformer le forze sono applicate da updatePhysicsControls() a ogni passo fisico), event() per quelli discreti e, soltanto se serve, updateWorld() o updateCamera() per modificare il comportamento standard. Se ridefinisci event() o update(), valuta la chiamata alla versione base: altrimenti perdi inoltro ai viewport, scheduler, overlay o simulazione.

I LevelLoader degli esempi costruiscono la scena di gioco e gli oggetti, quindi restituiscono una Scene* da inserire nello stack. load() legge il file tramite LevelData e crea la scena; loadJson(world, level) interpreta categorie e proprietà. Questa separazione è comune ai quattro prototipi. I loader sono classi di utilità con metodi statici, senza singleton; appartengono ai progetti, mentre LevelData è nel core. Imposta setPlayer() se vuoi usare la camera che segue il giocatore: updateCamera() accede al rettangolo di _player quando quel comportamento è attivo.

3. Aggiungi un oggetto

Scegli la classe base in funzione del ruolo:

Ruolo Punto di partenza
Logica invisibile, trigger, dato spaziale Object
Entità disegnata o semplice elemento UI RenderableObject
Corpo fisico o collider specifico del gioco Sottoclasse del progetto, costruita sopra Object/RenderableObject

Il costruttore di Object chiama scene->newObject(this): non aggiungere una seconda volta lo stesso puntatore. La scena incorpora le nuove istanze al successivo refreshObjects(). Per rimuoverle usa kill(), non delete diretto. Se sovrascrivi update(), chiama Object::update(dt) o RenderableObject::update(dt) secondo la tua classe base per conservare timer, scheduler, rotazione e animazione.

Se conservi un puntatore a un altro oggetto della stessa scena, ridefinisci forgetObject(Object* objectToRemove) per azzerarlo quando quell'oggetto sta per essere distrutto. Per esempio, in una sottoclasse di CollidableObject con un campo _target:

void forgetObject(Object* objectToRemove) override
{
    CollidableObject::forgetObject(objectToRemove);
    if (_target == objectToRemove)
        _target = nullptr;
}

Richiama la versione della tua classe base per conservare la pulizia già prevista; per una collezione, rimuovi il puntatore anziché assegnare nullptr. Chi usa _target, anche in una callback differita, deve gestire il caso nullo. La scena invia questa notifica anche agli oggetti congelati: la pulizia non deve dipendere da update(). Il significato del parametro è spiegato nel ciclo di vita degli oggetti.

Movimento e cambio del rettangolo devono passare da setPos() o setRect(), che notificano Scene::objectMoved(). setSize() della classe base non lo fa: con un quadtree attivo la dimensione indicizzata potrebbe diventare obsoleta.

4. Dai un aspetto all'oggetto

Il RenderableObject può disegnare un rettangolo colorato oppure usare uno Sprite. Una factory del progetto può caricare una texture una volta e creare sprite diversi per le varie regioni della spritesheet. Sprite non possiede il file immagine e RenderableObject non elimina automaticamente lo sprite nel proprio distruttore: definisci esplicitamente chi gestisce la durata delle risorse del tuo progetto. setSprite() offre un parametro deallocateSprite per l'eventuale sprite sostituito.

Se il rettangolo logico è il collider e l'immagine deve estendersi oltre, usa fit = false e configura correttamente pixelUnitSize. Per UI e testo consulta Sprite, animazioni e testo.

Una classe specifica richiede alla factory lo sprite nel proprio costruttore: per esempio Lift sceglie platform e Gear sceglie gear. Il loader passa posizione e parametri di gioco. I collider StaticObject generici sono trasparenti e senza sprite. La grafica di sfondo viene creata nel loader C++ con RenderableObject; il suo costruttore può ricevere una sprite dal codice. Il JSON non sceglie sprite, colori o layer. Il layer ha un default nel costruttore e può essere adattato dalla classe o dal loader.

5. Collega input, query e risorse

  • Input ed eventi: usa event() per pressioni singole e updateControls() per stato continuo.
  • Quadtree: attivalo prima di creare gli oggetti e verifica che la radice comprenda le posizioni raggiungibili.
  • Servizi e asset: carica file rispetto a SDL_GetBasePath(), non alla directory corrente del processo.
  • UI e overlay: scegli fra scena UI nello stack e layer di parallax collegato alla GameScene.

6. Aggiungi una categoria al livello

Definisci la classe dell'entità e aggiungi il suo nome in categories. Nel loadJson() del progetto, associa quella categoria al costruttore e leggi le proprietà necessarie, fornendo default dove opportuno. Ogni regione usa l'indice della categoria e una geometria; aggiungi name soltanto se serve identificarla individualmente. L'editor conserva gli attributi aggiuntivi durante il salvataggio. Consulta LevelData e livelli JSON per formato ed esempi.

I comportamenti semplici possono restare C++: il trigger di CustomPlatformer riceve una lambda dal loader, senza introdurre nel JSON un sistema di azioni, ID e riferimenti.

Dove mettere una modifica?

Esigenza Modulo da modificare o estendere
Nuova regola di movimento Oggetto del progetto e GameScene::updateControls()
Nuovo menu UIScene o sua sottoclasse, poi Game::pushScene()
Nuova animazione AnimatedSprite creato dalla factory del progetto
Nuova collisione precisa Metodo geometrico del collider del progetto; il core offre le query candidate
Effetto su tutto il frame CPUShaderWindow o GPUShaderWindow
Formato comune di livello LevelData del core per lettura, validazione e geometrie; editor per la modifica visuale
Categoria o proprietà di gameplay nel JSON LevelLoader::loadJson() del progetto e costruttore dell'entità

Letture nel repository

Per gli strumenti opzionali, prosegui con Editor dei livelli e Post-processing e shader.

Clone this wiki locally