Repository navigation
Contatti BEGIN STAY END
Questa guida riguarda CustomPlatformer, CustomPlatformerSlopes e ActionRPG. Quando scrivi un nemico, un oggetto raccoglibile o una zona interattiva, implementa collision(): il sistema rileva il contatto e ti comunica con chi stai interagendo e in quale fase. Non occorre confrontare i collider o conservare la lista dei contatti nella tua entità.
enum class Contact { BEGIN, STAY, END };| Fase | Significato | Uso tipico |
|---|---|---|
Contact::BEGIN |
Il contatto è appena iniziato. | Raccogliere una moneta, colpire con la spada, attivare un interruttore. |
Contact::STAY |
Il contatto continua. | Mantenere un effetto, aggiornare il passeggero di una piattaforma, verificare un danno persistente. |
Contact::END |
Il contatto è terminato. | Spegnere un effetto, rimuovere un passeggero, riarmare un portale. |
Per un personaggio che entra in una zona, rimane al suo interno e poi esce, la sequenza concettuale è:
entra rimane nella zona esce
BEGIN STAY STAY STAY ... END
Se rientra, riceve un nuovo BEGIN. Un attraversamento molto breve può produrre BEGIN e poi END senza alcuno STAY: non basare un'azione di ingresso sull'arrivo di STAY.
BEGIN significa una volta per contatto, non una volta per tutta la partita. Per un oggetto raccoglibile che deve scomparire definitivamente usa anche kill(), come fa Coin.
Nei due platformer la firma è:
bool collision(CollidableObject* with, Contact contact,
Direction fromDir) override;-
withè l'altro oggetto: controllane il tipo oppure confrontalo con l'oggetto che stai osservando. -
contactindica la fase: scegli quali eventi hanno significato per la tua meccanica. -
fromDirè il lato del tuo oggetto interessato dal contatto. Un personaggio sopra una piattaforma riceveDOWN; la piattaforma riceveUP. SuENDvaleDirection::NONE. - Il valore restituito indica se hai gestito l'interazione. Restituire
falsenon annulla la collisione fisica e non impedisce la notifica all'altro oggetto. Per escludere una coppia usacollidableWith().
In ActionRPG l'ultimo parametro è const Vec2Df& normal, perché i collider possono essere ruotati. La normale va dal tuo oggetto verso l'altro; l'altro oggetto riceve quella opposta. Su END è il vettore nullo. Le tre fasi hanno lo stesso significato.
Questo è il comportamento della spada in CustomPlatformer:
bool Sword::collision(CollidableObject* with, Contact contact,
Direction fromDir)
{
Enemy* enemy = dynamic_cast<Enemy*>(with);
if (enemy && contact == Contact::BEGIN)
enemy->smash();
return true;
}Il colpo non si ripete mentre spada e nemico restano a contatto e non viene applicato quando si separano. Anche il Trigger generico esegue il proprio task soltanto su BEGIN, per l'oggetto osservato: restare dentro o uscire non lo riattiva.
Per una reazione valida sia all'ingresso sia durante la permanenza, il controllo è invece:
if (contact != Contact::END)
{
// Reazione valida su BEGIN e STAY.
}È la scelta dei nemici dei platformer per il contatto con Mario. Gli ulteriori controlli della meccanica, come invincibilità e tempo di protezione dopo un colpo, restano responsabilità dell'entità.
Questa classe può essere aggiunta a uno dei due platformer. Osserva un solo oggetto, passato al costruttore; gli altri non ne cambiano lo stato.
class AreaAttiva : public agp::StaticObject
{
agp::CollidableObject* _watched;
bool _active = false;
public:
AreaAttiva(agp::Scene* scene, const agp::RectF& rect,
agp::CollidableObject* watched)
: StaticObject(scene, rect), _watched(watched)
{
_compenetrable = true;
}
bool active() const { return _active; }
bool collision(agp::CollidableObject* with, agp::Contact contact,
agp::Direction fromDir) override
{
if (with != _watched)
return false;
if (contact == agp::Contact::BEGIN)
_active = true;
else if (contact == agp::Contact::END)
_active = false;
return true; // STAY mantiene lo stato già attivo.
}
};Includi StaticObject.h. La logica della porta o dell'effetto può leggere active(). Per un effetto che dipende dal tempo, applica la variazione nel tuo update(dt) mentre la zona è attiva, moltiplicandola per dt: non contare le chiamate a STAY come se fossero secondi. Se vuoi reagire direttamente a ogni permanenza, aggiungi un ramo contact == Contact::STAY.
| Entità | Reazione alle fasi |
|---|---|
Trigger nelle tre demo |
Esegue il task su BEGIN dell'oggetto osservato. |
Sword in CustomPlatformer |
Colpisce il nemico su BEGIN. |
Coin e raccolta del Mushroom in CustomPlatformerSlopes |
Assegnano la ricompensa su BEGIN, poi richiedono la rimozione. |
Block in CustomPlatformerSlopes |
Reagisce a BEGIN di Mario dal basso; il trasporto resta gestito dalla base cinematica. |
KinematicObject e Lift nei platformer |
Registrano/aggiornano il passeggero su BEGIN e STAY; lo rimuovono su END. |
Enemy in ActionRPG |
Si elimina su BEGIN con una Sword, mai su END. |
Portal in ActionRPG |
Avvia l'ingresso su BEGIN; ignora l'arrivo da un altro portale finché END non lo riarma. |
Gli eventi appartengono agli aggiornamenti della simulazione, non ai frame disegnati. BEGIN ed END vengono notificati ai due oggetti una sola volta per la transizione. Un oggetto che rileva i contatti riceve STAY nel proprio aggiornamento; gli oggetti passivi lo ricevono da chi li tocca. L'ordine degli aggiornamenti può quindi far arrivare BEGIN e uno STAY nello stesso passo della scena.
Un contatto può terminare per separazione, disabilitazione delle collisioni o rimozione dell'altro oggetto. La rimozione ripulisce i riferimenti ed emette END anche per un oggetto congelato. Non conservare il puntatore with dopo la rimozione dell'oggetto.
Nei platformer il CCD registra anche l'attraversamento rapido di un collider compenetrabile: non è necessario trovarsi ancora al suo interno alla fine dello spostamento. Se il contatto non continua, END arriva al controllo successivo. In ActionRPG la detection resta discreta: aggiungere Contact non impedisce di attraversare un oggetto sottile con uno spostamento troppo grande.
Normalmente no: scegli la classe base adatta e implementa collision().
_detectsContacts stabilisce chi rileva i contatti. È true per gli oggetti attivi; è già false in StaticObject, Trigger e, nei platformer, KinematicObject. Questi oggetti ricevono gli eventi dagli oggetti che li toccano. Un Lift si muove, ma è passivo rispetto alla generazione degli eventi.
Il flag non disabilita le collisioni e non rende l'oggetto attraversabile. Per la tua meccanica distingui:
-
collidableWith(other): decide se accettare quella coppia; devono accettarla entrambi gli oggetti. -
_collidable: abilita o disabilita le collisioni dell'oggetto. -
_compenetrable: distingue i collider attraversabili; nei platformer evita la risposta fisica. ActionRPG conserva una separazione morbida fra dinamici, descritta nella sua guida al movimento.
Sostituisci il parametro con Contact contact, poi scegli esplicitamente il comportamento:
| Intenzione | Condizione |
|---|---|
| Solo all'ingresso | contact == Contact::BEGIN |
| Solo durante la permanenza | contact == Contact::STAY |
| Ingresso e permanenza | contact != Contact::END |
| Solo all'uscita | contact == Contact::END |
Il vecchio begin == true poteva ripetersi a ogni aggiornamento: sostituirlo sempre con Contact::BEGIN cambierebbe le meccaniche che devono continuare durante il contatto. Non chiamare manualmente collision() per simulare una fase: lascia che sia il sistema a generare gli eventi.
Box2DPlatformer usa un'altra API: mantiene il parametro bool begin e gli eventi nativi di Box2D, senza aggiungere STAY. Vedi Corpi e contatti.
- 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