Skip to content

Core Scene e Oggetti

Alessandro Bria edited this page Oct 12, 2026 · 4 revisions

Scene, oggetti e scheduling

Scene e Object formano il modello base della scena di gioco. Una scena possiede gli oggetti; ogni oggetto conosce la scena alla quale appartiene.

Un punto essenziale: scena, oggetto e disegno sono livelli diversi. Scene contiene e interroga; Object ha posizione e comportamento; soltanto RenderableObject aggiunge dati grafici. Una query può quindi restituire anche oggetti che non sono disegnati.

Gerarchia delle scene

classDiagram
    Scene <|-- GameScene
    Scene <|-- UIScene
    Scene <|-- OverlayScene
    UIScene <|-- EditorScene
    UIScene <|-- EditorUI
Loading

Scene

Offre i servizi comuni:

  • contenitore degli oggetti;
  • aggiunta e rimozione differite;
  • query per rettangolo o punto;
  • raycast e ricerca dell'intersezione più vicina;
  • scheduler associati alla scena;
  • stato visible, active e blocking;
  • inoltro di eventi e rendering tramite una View.

L'aggiunta e la rimozione sono differite per evitare di invalidare il vettore degli oggetti mentre lo si sta attraversando. newObject() inserisce in una coda; killObject() marca l'oggetto e la cancellazione effettiva avviene in refreshObjects().

Scene::update() chiama refreshObjects() e poi aggiorna gli scheduler della scena. La scena base non esegue Object::update() per tutti gli oggetti: lo fanno le sottoclassi. Questo è il motivo per cui GameScene e UIScene hanno modelli temporali diversi pur condividendo lo stesso contenitore.

GameScene

Rappresenta la scena di gioco. Aggiunge:

  • semi-fixed timestep;
  • riferimento al player;
  • camera manuale o agganciata al player;
  • scene di sfondo e primo piano;
  • quadtree opzionale;
  • eliminazione opzionale degli oggetti fuori scena;
  • accesso all'editor del livello.

Una sottoclasse normalmente implementa updateControls() e specializza il comportamento della camera o di updateWorld().

L'ordine di GameScene::update() è: manutenzione della scena, overlay, controlli, simulazione (updateWorld()), camera. La simulazione usa un accumulatore: ogni volta che supera _dt, updateWorld() esegue un passo sugli oggetti non freezed. Controlli e camera usano invece il delta ricevuto da Game per il frame corrente. La gestione della fisica e della narrow phase è lasciata alle classi del progetto.

UIScene

Aggiorna gli oggetti una volta per frame con il delta reale. È adatta a HUD e menu, dove non serve una simulazione a passo fisso.

UIScene::update() richiama prima Scene::update() e poi, se la scena è active, chiama update(timeToSimulate) su ciascun oggetto. In questa implementazione non usa l'accumulatore né controlla il flag freezed per ogni oggetto come fa GameScene.

Ciclo di vita di un oggetto

Il costruttore di Object riceve una Scene* e registra automaticamente l'istanza:

MyObject::MyObject(Scene* scene, const RectF& rect)
    : RenderableObject(scene, rect, Color(255, 255, 255))
{
}

Per rimuoverlo si usa kill(), non delete:

if (health <= 0)
    kill();

Questo permette alla scena di completare in sicurezza l'iterazione corrente.

La scena marca l'oggetto come killed e lo inserisce in _killedObjects. All'inizio del successivo aggiornamento della scena, refreshObjects() incorpora i nuovi oggetti da _newObjects e gestisce le rimozioni. Prima di ogni delete, chiama forgetObject(objectToRemove) sugli altri oggetti della stessa scena, anche se congelati, affinché eliminino i riferimenti all'oggetto che sta per essere distrutto. Poi rimuove il puntatore dai contenitori e dealloca l'oggetto. Non si attende più un numero prefissato di aggiornamenti dell'oggetto.

Per esempio, mario->forgetObject(piattaforma) significa: «Mario, questa piattaforma sta per essere distrutta: dimentica i riferimenti che conservi verso di lei». Il destinatario è Mario; l'argomento identifica l'altro oggetto, ancora valido durante la chiamata. Il metodo non deve distruggerlo: questo compito resta alla scena. Vedi anche il diagramma della rimozione.

La versione base di forgetObject() non fa nulla. Le classi che conservano puntatori ad altri oggetti devono specializzarla: la scena invia l'avviso, ma non può trovare e azzerare automaticamente tutti i puntatori. La regola per nuove classi è in Estendere il core. kill() resta una richiesta differita: non azzera il puntatore del chiamante e non impedisce da solo ulteriori aggiornamenti o disegni prima della rimozione.

Dati e comportamento di Object

Ogni oggetto possiede:

  • un RectF, cioè posizione e dimensione nel sistema di coordinate della scena;
  • un layer usato per l'ordine di disegno;
  • un identificatore numerico;
  • gli stati freezed e killed;
  • il tempo trascorso e una collezione di scheduler.

Il rettangolo appartiene a Object ed è espresso nelle coordinate della scena, non in pixel dello schermo. layer cambia l'ordine di disegno ma non la priorità di aggiornamento né la precisione di una collisione. contains(), intersectsRect() e intersectsLine() usano di default il rettangolo: le sottoclassi possono specializzarli per forme più ricche.

Le sottoclassi ridefiniscono soprattutto update(), le query geometriche e name(). Gli spostamenti devono passare da setPos() o setRect(), perché la scena usa la notifica objectMoved() per aggiornare le strutture spaziali. Il setSize() della classe base non invia questa notifica: con il quadtree attivo, un cambio di dimensione va applicato tramite setRect() oppure gestito esplicitamente dalla sottoclasse.

Scheduler

Scheduler esegue una funzione dopo un ritardo. Sia Scene sia Object espongono schedule():

schedule("remove", 2.0f, [this]() { kill(); });

L'identificatore consente di sovrascrivere un'azione; sugli oggetti, unschedule() permette anche di annullarla. Il parametro loop permette ripetizioni; -1 indica una ripetizione indefinita.

Lo scheduler è utile per animazioni logiche, cooldown, cambi di stato e azioni differite. Non crea thread: viene aggiornato nello stesso game loop.

Con loop = 0 l'azione viene eseguita una volta; con un valore positivo N, una volta più N ripetizioni; con -1, continua indefinitamente. Usa un ritardo positivo: lo scheduler considera attivo un timer solo finché il tempo residuo è maggiore di zero. Le azioni dell'oggetto avanzano quando viene chiamato Object::update(); quelle della scena avanzano in Scene::update(). Un oggetto congelato nella GameScene non aggiorna neppure i propri scheduler.

Query geometriche

La scena offre:

  • objects(rect) per i candidati che intersecano un'area;
  • objects(point) per gli oggetti che contengono un punto;
  • raycast(line) per tutte le intersezioni ordinate lungo un segmento;
  • raycastNearest(line) per la prima intersezione;
  • isEmpty(rect) per verificare se un'area è libera.

Le sottoclassi di Object possono ridefinire i test per usare collider più precisi del rettangolo base.

objects(rect) restituisce candidati che intersecano superficialmente l'area; con il quadtree acceso la ricerca passa all'indice spaziale. objects(point) usa poi contains() per verificare i candidati. raycast(line) interroga prima il rettangolo che racchiude il segmento, chiama intersectsLine() sui candidati e ordina i risultati per parametro di impatto tNear. raycastNearest() prende il primo risultato ordinato. isEmpty(rect) nella Scene base verifica gli oggetti presenti; una sottoclasse può restringere la definizione di «occupato», come fa RPGGameScene per gli oggetti statici.

Se vuoi aggiungere un nuovo tipo di collider, distingui sempre broad phase e narrow phase: la prima seleziona i candidati senza esaminare ogni coppia di oggetti, la seconda esegue il test geometrico preciso.

Sorgenti di riferimento

Prosegui con Rendering, coordinate e camera.

Clone this wiki locally