Repository navigation
Core Scene e Oggetti
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.
classDiagram
Scene <|-- GameScene
Scene <|-- UIScene
Scene <|-- OverlayScene
UIScene <|-- EditorScene
UIScene <|-- EditorUI
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,activeeblocking; - 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.
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.
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.
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.
Ogni oggetto possiede:
- un
RectF, cioè posizione e dimensione nel sistema di coordinate della scena; - un
layerusato per l'ordine di disegno; - un identificatore numerico;
- gli stati
freezedekilled; - 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 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.
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.
Prosegui con Rendering, coordinate e camera.
- 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