AABB 2D e risoluzione di più collisioni


8

Ok, quindi questo è un problema che sto cercando di capire da un po 'di tempo. Il mio è un gioco platform in 2D con un mondo fatto di tessere (di solito) immobili e folletti mobili, che usano entrambi AABB per rappresentare le loro hitbox. Questo gioco NON è basato sulla griglia a causa di alcune complicazioni con lo spostamento di strati di tessere.

Riesco a rilevare le collisioni e capire facilmente la profondità della collisione. Uso il "metodo dell'asse più superficiale" per determinare in che modo risolvere una collisione tra lo sprite e la piastrella. Se lo sprite è più profondo in orizzontale che in verticale, la direzione da risolvere è su o giù. Se lo sprite è più profondo in verticale che in orizzontale, la direzione da risolvere è sinistra o destra.

Schema n. 1

Questo è abbastanza semplice e funziona abbastanza bene. Cioè, fino a quando uno sprite non si scontra con più di una tessera. Poiché, per loro natura, ogni collisione deve essere controllata separatamente, diverse collisioni possono avere una direzione diversa in cui risolvere. Ad esempio, se uno sprite sta cercando di attraversare una fila di tessere, per una trama si intersecano con la tessera successiva che la profondità orizzontale è inferiore alla profondità verticale. Mentre la collisione dice "risolvi a sinistra", verrà respinta e rimarrà bloccata in un angolo.

Diagramma n. 2

Ho riflettuto su questo problema, acceso e spento, per un po 'di tempo, e diverse soluzioni sono arrivate a me, ma tutte hanno dei difetti. Potrei contrassegnare alcuni lati come irraggiungibili, ma senza un motore basato su griglia, determinare "irraggiungibilità" è notevolmente complesso, soprattutto con lo spostamento di strati di tessere sempre una possibilità.

Un altro possibile metodo sarebbe quello di prevedere le collisioni prima che si verifichino e "ripensare" il movimento fino al punto della collisione, suppongo, ma non sono sicuro di come funzioni la matematica.

Sento che mi manca qualcosa di incredibilmente ovvio, soprattutto perché i giochi degli anni '80 hanno già risolto questo problema.


Potresti semplicemente cambiare la posizione del giocatore in base a quale tessera è arrivata per prima nel tuo check
Chachmu,

Risposte:


6

Il problema

Il problema risiede nel metodo di risoluzione delle collisioni. Il metodo è il seguente:

  1. Sposta il giocatore.
  2. Verificare la collisione.
  3. Determinare la profondità di collisione più breve.
  4. Risolvi la collisione.

Il problema è che può facilmente spostare il giocatore nella direzione sbagliata. Puoi vedere come ciò potrebbe accadere nell'immagine qui sotto:

Bug di collisione

Poiché il giocatore si sta muovendo verso il basso a destra, ed è sopra il terreno, ci si aspetterebbe che il giocatore atterri sopra il terreno (vicino alla casella verde). Invece, viene spinto fuori dal terreno a sinistra (rappresentato dal riquadro rosso). Questo può essere un problema se il giocatore sta provando a saltare da una piattaforma all'altra, perché il giocatore potrebbe finire per cadere a causa di un cattivo codice di collisione.

La soluzione

La soluzione a questo problema è in realtà piuttosto semplice. Invece di utilizzare il metodo sopra, risolvi la collisione in questo modo:

  1. Sposta il giocatore lungo l'asse X.
  2. Verifica la presenza di piastrelle in collisione.
  3. Risolvi la collisione X.
  4. Sposta il giocatore lungo l'asse Y.
  5. Verifica la presenza di piastrelle in collisione.
  6. Risolvi la collisione a Y.

Ora spero che tu non abbia buttato via il tuo codice di controllo di profondità, perché ne avrai ancora bisogno per i passaggi 3 e 6.

Per risolvere la collisione tra le tessere su uno dei due assi (dopo aver spostato il giocatore), devi prima ottenere la profondità della collisione. Quindi si prende la profondità della collisione e si sottrae dagli assi che si sta attualmente verificando per la collisione. Nota che la profondità dovrebbe essere negativa se ti sposti a sinistra, in modo che il giocatore si muova nella giusta direzione.

Usando questo metodo, non solo dovrai preoccuparti di bug di collisione come quello nello scenario nell'immagine sopra, ma questo metodo può anche gestire la collisione con più tessere.

Codice di esempio:

void move(velocity)
{
    top = player.y / TILE_HEIGHT;
    bottom = top + (player.height / TILE_HEIGHT);
    left = player.x / TILE_WIDTH;
    right = left + (player.width / TILE_WIDTH);

    // Check X

    player.x += velocity.x;
    player.updateAABB();
    for(int tx = left - 1; tx <= right + 1; tx++)
    {
        for(int ty = top - 1; ty <= bottom + 1; ty++)
        {
            aabb = world.getTileAABB(tx, ty);
            if(aabb.collidesWith(player.aabb))
            {
                depth = player.aabb.getXDepth(aabb);
                player.x -= depth;
            }
        }
    }

    // Now check Y

    player.y += velocity.y;
    player.updateAABB();
    for(int tx = left - 1; tx <= right + 1; tx++)
    {
        for(int ty = top - 1; ty <= bottom + 1; ty++)
        {
            aabb = world.getTileAABB(tx, ty);
            if(aabb.collidesWith(player.aabb))
            {
                depth = player.aabb.getYDepth(aabb);
                player.y -= depth;
            }
        }
    }

    player.updateAABB();
}

Intrigante, ma vedo ancora un problema. Nel mio secondo scenario, lo sprite si scontra con una fila di tessere. Se controllo prima le collisioni X, ci sarà un rilevamento errato in quello scenario e verrebbe comunque risolto in modo errato a sinistra.
Celarix

@Celarix Il secondo scenario non dovrebbe accadere perché non stai solo controllando prima l'asse X, ma prima lo stai muovendo. Lo sprite non si troverebbe mai in una fila di tessere, perché il test di collisione a Y del movimento precedente ti impedirebbe di scontrarti con una fila di tessere del genere. Assicurati solo che le collisioni vengano sempre risolte correttamente. Una volta ho avuto alcuni problemi causati dal fatto che stavo usando floats per memorizzare le mie coordinate. Quindi stava causando tremori. La soluzione era di arrotondare le corde quando ho finito di risolvere la collisione.
Lysol

Hai ragione, e penso che questa potrebbe essere la soluzione al mio problema di quasi due anni. (Sono uno sviluppatore lento.) Grazie mille!
Celarix

Ci sono alcuni problemi con la mia risposta. Funziona per la maggior parte delle situazioni, ma tieni presente che ci saranno risultati diversi a seconda che controlli prima la collisione X o prima la collisione Y. Tieni inoltre presente che il tunneling è un problema; questo fallirà con oggetti ad alta velocità poiché salteranno sulle tessere.
Lysol,

Penso di essere andato prima con X in modo che le piste funzionassero bene. Bullet-through-paper non è davvero un problema nel mio platform perché nulla si muove abbastanza velocemente da passare attraverso le tessere. Grazie per l'ulteriore contributo!
Celarix,

0

Stai pensando troppo al problema e stai combinando un paio di problemi. Ma va bene perché, come hai detto, questo è un problema molto risolto con molte grandi risposte là fuori.

Analizziamolo:

  1. Tilemaps . Il tuo primo esempio è di uno sprite che cammina attraverso un gruppo di tessere disposte orizzontalmente (o che scivolano lungo un muro di tessere disposte verticalmente, sono isomorfi). Una soluzione molto elegante a questo è semplicemente quella di non controllare i bordi delle piastrelle dove sappiamo che uno sprite non può arrivare, come bordi che sono "sotterranei" o bordi che delimitano un'altra piastrella completamente solida.

    Hai ragione sul fatto che lo sprite scenderà a causa della gravità, quindi si sposterà lateralmente, quindi rimarrà bloccato ... ma la risposta è non preoccuparsi dei bordi sinistro o destro delle piastrelle che sono sotterranee . In questo modo, la tua routine di risoluzione delle collisioni sposta lo sprite solo verticalmente, e lo sprite può andare per la sua strada allegra.

    Dai un'occhiata ai tutorial sulle tessere Metanet per una spiegazione dettagliata di questo. Nella tua domanda dici che non stai usando una tilemap tradizionale, ma va bene lo stesso: le tessere statiche sono nella tilemap e si aggiornano come sopra, mentre si spostano le piattaforme e l'aggiornamento come il n. 2 di seguito.

  2. Altri AABB . Incontrerai un problema solo se, in un singolo fotogramma, il tuo sprite può spostarsi di una distanza maggiore della larghezza / altezza della maggior parte degli AABB nel tuo gioco. Se non ci riesci, sei d'oro: risolvi le collisioni una per una e funzionerà perfettamente.

    Se gli AABB possono muoversi molto velocemente in un singolo fotogramma, allora dovresti "spazzare" il movimento quando controlli le collisioni: riduci il movimento in frazioni più piccole e controlla le collisioni ad ogni passo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.