Come posso rilevare la direzione delle collisioni di oggetti rettangolari 2D?


11

Dopo questa domanda , ho bisogno di ulteriore aiuto.

Come posso scoprire da quale parte di un rettangolo proviene una collisione e reagire di conseguenza?

rettangoli in collisione con da tutti i lati

Le frecce blu sono i percorsi che alcuni oggetti circolari seguirebbero se prima e dopo si scontrassero con la scatola.

Come posso calcolarlo?

Risposte:


8

Poiché questo si basa sull'altra tua domanda, fornirò una soluzione per quando il rettangolo è allineato agli assi.

Innanzitutto, costruisci il rettangolo dell'oggetto corrente con i seguenti valori:

int boxLeft = box.X;
int boxRight = boxLeft + box.Width;
int boxTop = box.Y;
int boxBottom = boxTop + box.Height;

Successivamente, è necessario disporre della posizione dell'oggetto vecchio (che è possibile memorizzare su ciascun oggetto o semplicemente passare a una funzione) per creare il rettangolo dell'oggetto vecchio (quando non si è scontrato):

int oldBoxLeft = box.OldX;
int oldBoxRight = oldBoxLeft + box.Width;
int oldBoxTop = box.OldY;
int oldBoxBottom = oldBoxTop + box.Height;

Ora, per sapere da dove proviene la collisione, devi trovare il lato in cui la vecchia posizione non era nell'area di collisione e dove si trova la sua nuova posizione. Perché, quando ci pensi, questo è ciò che accade quando ti scontri: un lato che non si scontra entra in un altro rettangolo.

Ecco come potresti farlo (queste funzioni presuppongono che ci sia una collisione. Non dovrebbero essere chiamate se non ci sono collisioni):

 bool collidedFromLeft(Object otherObj)
{
    return oldBoxRight < otherObj.Left && // was not colliding
           boxRight >= otherObj.Left;
}

Scuotere e ripetere.

bool collidedFromRight(Object otherObj)
{
    return oldBoxLeft >= otherObj.Right && // was not colliding
           boxLeft < otherObj.Right;
}

bool collidedFromTop(Object otherObj)
{
    return oldBoxBottom < otherObj.Top && // was not colliding
           boxBottom >= otherObj.Top;
}

bool collidedFromBottom(Object otherObj)
{
    return oldBoxTop >= otherObj.Bottom && // was not colliding
           boxTop < otherObj.Bottom;
}

Ora, per l'uso effettivo con la risposta alla collisione dell'altra domanda:

if (collidedFromTop(otherObj) || collidedFromBottom(otherObj))
    obj.Velocity.Y = -obj.Velocity.Y;
if (collidedFromLeft(otherObj) || collidedFromRight(otherObj))
    obj.Velocity.X = -obj.Velocity.X;

Ancora una volta, questa potrebbe non essere la soluzione migliore, ma di solito vado per il rilevamento delle collisioni.


Ancora una volta, avevi ragione! ; D Grazie ... (inviami altre cartoline della tua bacheca la prossima volta ... ^ ___ ^)
NemoStein,

Ahhh purtroppo non sapevo per cosa avrei potuto usarlo .. forse la prossima volta!
Jesse Emond,

7

Poiché la domanda è parzialmente identica a questa domanda , userò alcune parti della mia risposta per provare a rispondere alla tua domanda.


Consente di definire un contesto e alcune variabili per rendere più comprensibile la seguente spiegazione. Il modulo di rappresentazione che useremo qui probabilmente non si adatta alla forma dei tuoi dati ma dovrebbe essere più semplice da comprendere in questo modo (in effetti puoi usare i seguenti metodi usando altri tipi di rappresentazioni una volta compreso il principio)

Pertanto, prenderemo in considerazione un riquadro vincolato allineato all'asse (o un riquadro associato orientato ) e un'entità in movimento .

  • La Bounding Box è composta da 4 lati e li definiremo come:
    Side1 = [x1, y1, x2, y2] (due punti [x1, y1] e [x2, y2])

  • L'entità in movimento è definita come un vettore di velocità (posizione + velocità):
    una posizione [posX, posY] e una velocità [speedX, speedY] .


È possibile determinare quale lato di un AABB / OBB è colpito da un vettore utilizzando il seguente metodo:

  • 1 / Trova i punti di intersezione tra le linee infinite che passano attraverso i quattro lati dell'AABB e la linea infinita che passa attraverso la posizione dell'entità (pre-collisione) che usano il vettore di velocità dell'entità come slop. (È possibile trovare un punto di collisione o un numero indefinito, che corrisponde a paralleli o linee sovrapposte)

  • 2 / Una volta che conosci i punti di intersezione (se esistono) puoi cercare quelli che si trovano nei limiti del segmento.

  • 3 / Infine, se ci sono ancora più punti nell'elenco (il vettore di velocità può passare attraverso più lati), è possibile cercare il punto più vicino dall'origine dell'entità usando le magnitudini del vettore dall'intersezione all'origine dell'entità.

Quindi è possibile determinare l'angolo di collisione utilizzando un semplice prodotto punto.

  • 4 / Trova l'angolo tra la collisione usando un punto prodotto del vettore entità (probabilmente una palla?) Con il vettore lato colpo.

----------

Più dettagli:

  • 1 / Trova incroci

    • a / Determina le linee infinite (Ax + Bx = D) usando le loro forme parametriche (P (t) = Po + tD).

      Origine punto: Po = [posX, posY]
      Vettore di direzione: D = [speedX, speedY]

      A = Dy = speedY
      B = -Dx = -speedX
      D = (Po.x * Dy) - (Po.y * Dx) = (posX speedY) - (posY speedX)

      Ax + By = D <====> (speedY x) + (-speedX y) = (posX speedY) - (posY speedX)

      Ho usato i valori dei punti entità per illustrare il metodo, ma questo è esattamente lo stesso metodo per determinare le 4 linee infinite laterali del rettangolo di selezione (Usa Po = [x1, y1] e D = [x2-x1; y2-y1] anziché).

    • b / Successivamente, per trovare l'intersezione di due linee infinite possiamo risolvere il seguente sistema:

      A1x + B1x = D1 <== Linea che passa attraverso il punto entità con il vettore di velocità come inclinazione.
      A2x + B2x = D2 <== Una delle linee che passano attraverso i lati AABB.

      che fornisce le seguenti coordinate per l'intercettazione:

      Intercettazione x = (( B 2 * D 1) - ( B 1 * D 2)) / (( A 1 * B 2) - ( A 2 * B 1))
      Intercettazione y = (( A 1 * D 2) - ( A 2 * D 1)) / (( A 1 * B 2) - ( A 2 * B 1))

      Se il denominatore ((A1 * B2) - (A2 * B1)) è uguale a zero, entrambe le linee sono parallele o sovrapposte, altrimenti dovresti trovare un'intersezione.

  • 2 / Test per i limiti del segmento. Poiché ciò è semplice da verificare, non sono necessari ulteriori dettagli.

  • 3 / Cerca il punto più vicino. Se ci sono ancora più punti nell'elenco, possiamo trovare quale lato è il più vicino al punto di origine dell'entità.

    • a / Determina il vettore che va dal punto di intersezione al punto di origine dell'entità

      V = Po - Int = [Po.x - Int.x; Po.y - Int.y]

    • b / Calcola l'entità del vettore

      || V || = sqrt (V.x² + V.y²)

    • c / trova il più piccolo.
  • 4 / Ora che sai da quale parte verrà colpito, puoi determinare l'angolo usando un prodotto punto.

    • a / Let S = [x2-x1; y2-y1] è il vettore laterale che verrà colpito ed E = [speedX; speedY] è il vettore di velocità dell'entità.

      Usando la regola del prodotto vettoriale dot lo sappiamo

      S · E = Sx Ex + Sy Ey
      e
      S · E = || S || || E || cos θ

      Quindi possiamo determinare θ manipolando un po 'questa equazione ...

      cos θ = (S · E) / (|| S || || E ||)

      θ = acos ((S · E) / (|| S || || E ||))

      con

      S · E = Sx * Ex + Sy * Ey
      || S || = sqrt (Sx² + Sy²)
      || E || = sqrt (Ex² + Ey²)


Nota: come ho detto nell'altro thread di domande, questo probabilmente non è il modo più efficiente né il più semplice per farlo, questo è proprio quello che mi viene in mente, e alcune parti della matematica potrebbero forse aiutare.

Non ho verificato con un esempio OBB concreto (ho fatto con un AABB) ma dovrebbe funzionare anche.


6

Un modo semplice è risolvere la collisione, quindi tradurre la scatola di collisione dell'oggetto in movimento di un pixel in ogni direzione a sua volta e vedere quali provocano la collisione.

Se vuoi farlo "correttamente" e con forme di collisione ruotate o poligoni arbitrari, ti suggerisco di leggere il teorema sull'asse di separazione. Il software Metanet (la gente che ha realizzato il gioco N), ad esempio, ha un fantastico articolo di sviluppo su SAT . Discutono anche della fisica coinvolta.


2

Un modo sarebbe ruotare il mondo attorno al tuo rettangolo. "Il mondo" in questo caso sono solo gli oggetti a cui tieni: il rettangolo e la palla. Ruoti il ​​rettangolo attorno al suo centro fino a quando i suoi limiti sono allineati con gli assi x / y, quindi ruoti la palla della stessa quantità.

Il punto importante qui è che si ruota la palla attorno al centro del rettangolo, non proprio.

Quindi puoi facilmente verificare la collisione proprio come faresti con qualsiasi altro rettangolo non ruotato.


Un'altra opzione è quella di trattare il rettangolo come quattro segmenti di linea distinti e testare la collisione con ciascuno di essi separatamente. Ciò consente di verificare la collisione e capire da che parte è stato scontrato contemporaneamente.


1

Ho usato angoli fissi nei miei calcoli, ma questo dovrebbe aiutarti alcuni

void Bullet::Ricochet(C_Rect *r)
{
    C_Line Line;
    //the next two lines are because I detected 
    // a collision in my main loop so I need to take a step back.

    x = x + ceil(speed * ((double)fcos(itofix(angle)) / 65536));
    y = y + ceil(speed * ((double)fsin(itofix(angle)) / 65536));
    C_Point Prev(x,y);

    //the following checks our position to all the lines will give us
    // an answer which line we will hit due to no lines
    // with angles > 90 lines of a rect always shield the other lines.

    Line = r->Get_Closest_Line(Prev);    
    int langle = 0;
    if(!Line.Is_Horizontal())   //we need to rotate the line to a horizontal position
    {
        langle = Line.Get_Point1().Find_Fixed_Angle(Line.Get_Point2());
        angle = angle - langle;  //to give us the new angle of approach
    }
    //at this point the line is horizontal and the bullet is ready to be fixed.
    angle = 256 - angle;
    angle += langle;
}
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.