Skip to content

ActionRPG Movimento e Collisioni

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

ActionRPG: movimento e collisioni

ActionRPG non usa Box2D. Il core fornisce scene, oggetti e timestep; le classi di questo progetto definiscono movimento top-down, collider OBB, collision detection SAT e collision response. La gerarchia è simile a quella di CustomPlatformer, ma qui non ci sono gravità, salto o Swept AABB.

Responsabilità delle classi

Object -> RenderableObject -> MovableObject -> CollidableObject
                                              |-> DynamicObject -> Link, Enemy, NPC, Sword
                                              |-> StaticObject
                                              `-> Trigger -> Portal
Classe Cosa aggiunge
MovableObject RotatedRectF di riferimento, direzioni, velocità, forze di movimento e frenata.
CollidableObject Collider OBB locale, query dei candidati, SAT, notifiche e interfaccia di collision response.
DynamicObject Risposta ai contatti: separazione completa dagli statici e parziale dagli altri dinamici.
StaticObject Ostacolo immobile; non esegue autonomamente detection e response nel proprio update().
Trigger Collider attraversabile che osserva un oggetto e attiva una callback.

Link, Soldier, NPC e Sword aggiungono regole specifiche; la scena non contiene uno switch sul loro tipo a ogni frame.

Movimento top-down

MovableObject::update(dt) salva la velocità precedente, applica forza lungo X e Y secondo le direzioni, frena gli assi senza input, limita la velocità per componente e chiama setPos(pos() + vel * dt). Non applica gravità. Le frecce possono fornire contemporaneamente una direzione X e una Y; DynamicObject::move() aggiorna anche _facingDir, usata dagli sprite e dall'attacco.

MovableObject conserva un RotatedRectF (_rrect) e pubblica il suo bounding rectangle come rect() del RenderableObject. Il suo setPos() sposta _rrect e chiama RenderableObject::setRect(): quest'ultima notifica la scena, così il quadtree può aggiornare la posizione dell'oggetto. Cambiare soltanto un campo locale senza passare da setRect() lascerebbe l'indice spaziale obsoleto.

Collider, broad phase e narrow phase

CollidableObject crea per default un _collider rettangolare orientato nelle coordinate locali dell'oggetto. sceneCollider() lo sposta nel sistema della scena usando il centro di _rrect. Link e Soldier restringono il collider rispetto allo sprite; la spada cambia anche angolo e dimensioni durante l'animazione. rect() serve alle query spaziali e al culling, mentre sceneCollider() descrive la forma usata per il test preciso.

La broad phase di detectCollisions() chiede a Scene::objects(sceneCollider().boundingRect()) i candidati. In RPGGameScene questa chiamata usa il quadtree perché _useQuadtree = true. La narrow phase filtra i CollidableObject, esclude sé stesso e gli oggetti non collidibili, poi chiama checkCollisionSAT() sui vertici dei due OBB. In caso di overlap, SAT restituisce un asse per la correzione e la profondità di compenetrazione. La collision detection è discreta: verifica le posizioni dopo il movimento, senza un tempo d'impatto continuo.

setPos() -> aggiorna quadtree
objects(boundingRect) -> candidati (broad phase)
checkCollisionSAT(vertici) -> collisione precisa (narrow phase)
collision() sui due oggetti -> regole di gameplay
resolveCollisions() -> correzione delle posizioni

Notifiche e collision response

Prima che la scena distrugga un oggetto, CollidableObject::forgetObject() elimina i riferimenti a esso dai contatti memorizzati, anche se l'oggetto che li conserva è congelato: vedi il ciclo di vita degli oggetti.

Per ogni overlap rilevato, detectCollisions() chiama collision(other, true, axis) su entrambi gli oggetti con assi opposti. Conserva i contatti precedenti e invia collision(other, false, {}) quando un contatto scompare. begin = true viene passato a ogni update in cui persiste l'overlap, non soltanto al primo frame: una callback che deve scattare una sola volta richiede una guardia. Trigger::collision() verifica l'oggetto osservato e begin; Portal aggiunge uno stato per evitare il rimbalzo immediato tra due portali.

DynamicObject::resolveCollisions() sposta il dinamico dell'intera profondità contro uno StaticObject, oppure di un decimo della profondità contro un altro DynamicObject. Gli statici e i trigger non eseguono un proprio solver. Il flag _compenetrable è presente nelle classi, ma questa implementazione di resolveCollisions() non lo consulta: non attribuirgli effetti fisici che il codice non applica.

CollidableObject::intersectsRect() usa prima il bounding rectangle e poi SAT; intersectsLine() usa il collider orientato. Per questo anche query della scena e raycast di interazione possono tenere conto della geometria reale, oltre al rect() usato come prima selezione.

Debug e confronto

La visualizzazione dei collider sovrappone l'OBB allo sprite. È particolarmente utile per distinguere Link, Soldier e Sword dalle loro immagini. Confronta questo percorso con la CCD di CustomPlatformer: qui il quadtree accelera la selezione dei candidati, ma non trasforma SAT in detection continua.

Sorgenti

Prosegui con Pathfinding e NPC.

Clone this wiki locally