Skip to content

CustomPlatformer Movimento e Collisioni

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

CustomPlatformer: movimento e collisioni

Il motore fisico di questo esempio vive nel progetto, non nel core. GameScene fornisce il timestep e il contenitore degli oggetti; MovableObject integra velocità e posizione, mentre CollidableObject gestisce collision detection e collision response. Non c'è un Box2D world né un solver esterno.

Gerarchia e ruoli

Object -> RenderableObject -> MovableObject -> CollidableObject
                                              |-> DynamicObject
                                              |-> StaticObject
                                              `-> KinematicObject
Classe Aggiornamento e collisioni
MovableObject Applica gravità e accelerazione orizzontale, frena e aggiorna la posizione; non gestisce contatti.
CollidableObject Conserva un collider AABB e implementa collision detection, collision response e notifiche.
DynamicObject Corpo mosso dalla simulazione; può compenetrarsi con altri dinamici per consentire interazioni logiche.
StaticObject Non integra moto né cerca attivamente contatti nel proprio update(); è rilevato dagli altri.
KinematicObject Si muove secondo una regola propria e trasferisce lo spostamento agli oggetti dinamici che lo toccano.

Le classi concrete, come Mario, HammerBrother e Lift, scelgono parametri e regole di gioco. La scena usa il metodo Object::update() polimorfico: non conosce queste classi nel proprio ciclo di simulazione.

Un passo di simulazione

LevelLoader passa _dt = 1/100 s alla PlatformerGameScene. A ogni passo di GameScene::updateWorld(), gli oggetti non congelati ricevono update(_dt). Per Mario il percorso è:

Mario::update()
  -> CollidableObject::update()
       -> MovableObject::update()
            -> RenderableObject::update() -> Object::update()
            -> accelera, limita la velocità, sposta
       -> cerca e risolve le collisioni
  -> aggiorna stato, sprite e flip

MovableObject usa un'integrazione di Eulero semi-implicita: cambia prima la velocità e poi la posizione con la velocità aggiornata. Le grandezze sono espresse in unità della scena e secondi; l'asse Y positivo va verso il basso, quindi la gravità aumenta vel.y e il salto assegna un impulso negativo. velClip() limita le componenti massime e porta a zero valori minimi vicini all'arresto. Movimento, attrito e frenata in inversione di direzione agiscono sull'asse X.

Collider e ricerca dei candidati

_collider è un RectF locale all'oggetto, inizialmente grande quanto rect(). sceneCollider() lo trasla con rect().pos per i test nella scena. Mario restringe il proprio collider rispetto all'immagine; la spada ne modifica la forma durante l'animazione. Il rettangolo visivo e quello delle collisioni non devono quindi coincidere.

La broad phase di CollidableObject chiama Scene::objects(rect) per trovare candidati. Nel progetto _useQuadtree non viene attivato: questa query scandisce gli oggetti della scena e usa l'intersezione dei rettangoli di ingombro. La narrow phase esegue poi il test di collisione; collidableWith(other) decide se due tipi debbano interagire e collidable() permette di disabilitare temporaneamente un collider. Il tasto C mostra i collider, mentre R mostra i rettangoli degli oggetti attraverso i comandi di GameScene.

Modalità predefinita: CCD

CollidableObject nasce con _CCD = true. Dopo l'integrazione di MovableObject, il codice annulla temporaneamente lo spostamento e cerca oggetti nell'unione tra il collider attuale e quello alla posizione prevista. Per ciascun candidato usa DynamicRectVsRect() di utils/collisionUtils.h per stimare il tempo di contatto del rettangolo in movimento; ordina i contatti e corregge la velocità lungo la normale prima di applicare lo spostamento finale.

posizione iniziale -> posizione prevista -> area spazzata
                                  -> candidati -> tempi di contatto
                                  -> nuova velocità -> posizione finale

Questa continuous collision detection (CCD) riduce il passaggio attraverso ostacoli sottili quando un oggetto si muove rapidamente. Un oggetto compenetrable produce l'interazione logica senza la correzione fisica della velocità; il progetto mantiene separatamente i contatti compenetrabili per rilevarne la fine.

Modalità alternativa: AABB

setCCD(false) sceglie la collision detection discreta: detectCollisionsAABB() cerca le intersezioni correnti con checkCollisionAABB(), memorizza asse e profondità, quindi resolveCollisionsAABB() sposta l'oggetto fuori dalla compenetrazione. La correzione è completa contro statici e cinematici e più morbida contro dinamici. Questo percorso non prevede un tempo d'impatto nel corso del frame: confrontalo con il CCD per capire il compromesso tra i due metodi.

KinematicObject usa questa modalità per i dinamici appoggiati sopra una piattaforma: nella notifica di contatto disabilita temporaneamente il loro CCD e lo riabilita quando il contatto finisce. È una scelta specifica del progetto, non una regola generale del core.

Notifiche e limiti

Prima che la scena distrugga un oggetto, CollidableObject::forgetObject() elimina i riferimenti a esso dai contatti memorizzati; KinematicObject li elimina anche dagli oggetti trasportati. Questo avviene anche per gli oggetti congelati: vedi il ciclo di vita degli oggetti.

collision(with, begin, fromDir) separa la risposta di gioco (danno, trigger, suoni) dalla risposta fisica. Entrambi gli oggetti ricevono la notifica, con direzione opposta; detectDecollisions() confronta i contatti correnti e precedenti e invia begin = false quando un contatto termina. Chi implementa un'azione da eseguire solo all'ingresso deve controllare begin: una chiamata con true può avvenire in più passi di contatto, non va interpretata automaticamente come un evento unico.

Il modello usa un singolo collider rettangolare per oggetto. Non rappresenta nativamente pendenze o forme composte e non sostituisce i test logici delle classi concrete. Per un confronto con un solver completo, vedi la panoramica di Box2DPlatformer.

Sorgenti

Prosegui con Entità e gameplay.

Clone this wiki locally