Cerchio all'interno della collisione del cerchio


9

In uno dei miei progetti ho un'area di gioco a forma di cerchio. All'interno di questo cerchio si sta muovendo un altro piccolo cerchio. Quello che voglio fare è impedire al piccolo cerchio di spostarsi fuori da quello più grande. Di seguito puoi vedere che nel fotogramma 2 il piccolo cerchio è parzialmente esterno, ho bisogno di un modo per spostarlo indietro appena prima che stia per spostarsi all'esterno. Come si può fare?

Esempio di base

Inoltre, ho bisogno del punto di collisione lungo l'arco del grande cerchio in modo da poter aggiornare la velocità del piccolo cerchio. Come si potrebbe fare per calcolare questo punto?

Quello che vorrei fare è prima di spostare il piccolo cerchio, prevedo la sua posizione successiva e se è fuori trovo il tempo di collisione tra t = 0 et t = 1 (t = 1 passo a tempo pieno). Se ho il tempo di collisione t, muovo solo il piccolo cerchio durante t invece di un passaggio a tempo pieno. Ma ancora una volta, il problema è che non so come rilevare in quel momento la collisione si verifica quando si tratta di due cerchi e uno all'interno dell'altro.

MODIFICARE:

Esempio di punto di collisione (verde) che voglio trovare. Forse l'immagine è un po 'fuori ma hai avuto l'idea.

inserisci qui la descrizione dell'immagine

Risposte:


10

Supponiamo che il cerchio grande abbia il centro Ae il raggio Re il cerchio piccolo abbia il centro Be il raggio che si rspostano verso la posizione C.

Esiste un modo elegante per risolvere questo problema, usando le somme di Minkovski (sottrazioni, in realtà): sostituire il disco di raggio Rcon un disco di raggio R-re il disco di raggio rcon un disco di raggio 0, cioè. un semplice punto situato a B. Il problema diventa un problema di intersezione cerchio-linea.

Devi solo controllare se la distanza ACè inferiore a R-r. Se lo è, i cerchi non si scontrano. Se è più grande, basta trovare il punto Dsu BCalla distanza R-rdi Ae questa è la nuova posizione del centro del cerchio piccolo. Ciò equivale a trovare ktale che:

  vec(BD) = k*vec(BC)
and
  norm(vec(AD)) = R-r

Sostituendo vec(AD)con si vec(AB) + vec(BD)ottiene:

AB² + k² BC² + 2k vec(AB).vec(BC) = (R-r

A condizione che la posizione iniziale fosse all'interno del grande cerchio, questa equazione quadratica kha una radice positiva. Ecco come risolvere l'equazione, in pseudocodice:

b = - vec(AB).vec(BC) / BC²    // dot product
c = (AB² - (R-r)²) / BC²
d = b*b - c
k = b - sqrt(d)
if (k < 0)
    k = b + sqrt(d)
if (k < 0)
    // no solution! we must be slightly out of the large circle

Con questo valore di k, il nuovo centro del piccolo cerchio è Dtale che BD = kBC.

Modifica : aggiungi la soluzione dell'equazione quadratica


Grazie, sembra elegante ma non sono sicuro di aver capito. Ad esempio: "trova il punto D su BC alla distanza Rr di A". Ho disegnato una foto per cercare di capire meglio. Quindi, se iniziamo da B (AX, AY- (Rr)) e C è dove finiremo con la velocità attuale. Il modo in cui capisco il testo citato: trova un punto D sul segmento di linea BC che è una distanza di Rr lontano da A. Ma il modo in cui lo vedo nella foto che ho disegnato è che solo esattamente B è Rr lontano da A. Tutto gli altri punti saranno> Rr lontano da A. Cosa mi sto perdendo?
dbostream,

@dbostream Non ti stai perdendo nulla. Se i due cerchi sono già in contatto, non c'è alcuna vera collisione da rilevare : la collisione avviene in B, e k=0. Ora, se si desidera risolvere la collisione , non l'ho trattato nella mia risposta perché richiederebbe la conoscenza delle proprietà fisiche degli oggetti. Cosa dovrebbe succedere? Il cerchio interno dovrebbe rimbalzare dentro? O rotolare? Spazzare?
sam hocevar,

Voglio che il piccolo cerchio inizi a scorrere lungo l'arco del grande cerchio. Quindi se non sbaglio voglio il punto di collisione sull'arco del grande cerchio in modo da poter usare la sua normale per aggiornare la velocità.
dbostream,

@dbostream se il movimento dovesse essere limitato in questo modo, allora ti suggerisco di seguire quel vincolo il più presto possibile: se la velocità è V, fai avanzare il cerchio interno V*tlungo la circonferenza del R-rcerchio. Ciò significa una rotazione dei V*t/(R-r)radianti angolari attorno al punto A. E il vettore di velocità può essere ruotato allo stesso modo. Non c'è bisogno di conoscere il normale (che è comunque sempre orientato verso il centro del cerchio) o di aggiornare la velocità in qualsiasi altro modo.
Sam Hocevar,

Devo ancora spostare il piccolo cerchio sul punto di collisione prima di ruotare. E quando ho provato a ruotare la posizione usando una matrice di rotazione, la nuova posizione non era esattamente (ma quasi) a distanza dal centro del grande cerchio, ma quella piccola differenza era sufficiente per far fallire il mio test di collisione nella corsa successiva. Devo ruotare la posizione per trovare quella nuova, non è possibile usare operazioni vettoriali come puoi se qualcosa si scontra con una parete dritta?
dbostream,

4

Supponi che il cerchio grande sia il cerchio A e il cerchio piccolo sia il cerchio B.

Verifica se B è all'interno di A:

distance = sqrt((B.x - A.x)^2 + (B.y - A.y)^2))
if(distance > A.Radius + B.Radius) { // B is outside A }

Se nel frame n-1B era all'interno di A e nel frame nB è all'esterno di A e il tempo tra i frame non era troppo grande (ovvero B non si muoveva troppo velocemente) possiamo approssimare il punto di collisione semplicemente trovando le coordinate cartesiane di B relative ad A:

collision.X = B.X - A.X;
collision.Y = B.Y - A.Y;

Possiamo quindi convertire questi punti in un angolo:

collision.Normalize(); //not 100% certain if this step is necessary     
radians = atan2(collision.Y, collision.X)

Se vuoi sapere più esattamente ciò che tB è fuori da A per la prima volta, potresti fare un'intersezione raggio-cerchio in ogni fotogramma e poi confrontare se la distanza da B al punto di collisione è maggiore quindi la distanza che B può percorrere dato che è velocità attuale. In tal caso, puoi calcolare l'ora esatta della collisione.


Grazie, ma è davvero corretto sparare un raggio dal centro del piccolo cerchio quando si esegue quel test di intersezione? Non finiremo con lo scenario nel mezzo di questa immagine ? Intendo che il primo punto sull'arco del piccolo cerchio che si scontra con il grande cerchio non è necessariamente quello sull'arco nella direzione della velocità. Penso di aver bisogno di qualcosa di simile nello scenario in basso dell'immagine a cui mi sono collegato. Ho aggiunto una nuova foto nel primo post che mostra un esempio di ciò che penso di aver bisogno.
dbostream,

Hmm suppongo che lo scenario sia possibile. Magari prova con un nuovo cerchio C che ha B.Radius + il movimento massimo di B in questo fotogramma, controlla se si scontra con A e poi allena il punto su C che è più lontano da A. (Non ho provato questo btw)
Roy T.

3
Usando meno parole: if (distance (A, B))> (Ra-Rb) si verifica una collisione e basta spostare il piccolo cerchio per ottenere una distanza uguale a Ra-Rb. Altrimenti muovi normalmente il piccolo cerchio. A proposito @dbostream stai usando qualcosa di simile a una forma semplificata di Contatti speculativi, prova a cercarlo.
Darkwings,

@Darkwings +1 hai perfettamente ragione, e questo lo rende molto più semplice!
Roy T.

Sembra semplice perché ho rimosso tutta la geometria di base necessaria. Invece di chiamarlo "collisione" avresti potuto chiamarlo boundAB poiché è quello che è in realtà: il vettore libero AB associato a (0,0). Una volta normalizzato, otterrai sia l'equazione per il fascio parallelo di AB delle linee rette sia un utile vettore unitario. Quindi puoi moltiplicare quel vettore di unità per qualsiasi distanza D e aggiungere i parametri appena trovati ad A per trovare il punto di collisione di cui hai bisogno: C (Ascia + Dx, Ay + Dy). Ora sembra più complicato ma è la stessa cosa: P
Darkwings,

0

Lascia (Xa, Ya) la posizione del cerchio grande e il suo raggio R, e (Xb, Yb) la posizione del cerchio più piccolo e il suo raggio r.

Puoi verificare se questi due cerchi si scontrano se

DistanceTest = sqrt(((Xa - Xb) ^ 2) + ((Ya - Yb) ^ 2)) >= (R - r)

Per scoprire la posizione della collisione, trova il momento esatto in cui i cerchi si scontrano, usando una ricerca binaria ma con un numero fisso di passi. A seconda di come è fatto il tuo gioco, puoi ottimizzare questa parte del codice (ho fornito questa soluzione per essere indipendente da come si comporta la pallina. Se ha un'accelerazione costante o una velocità costante, questa parte del codice può essere ottimizzata e sostituito con una semplice formula).

left = 0 //the frame with no collision
right = 1 //the frame after collision
steps = 8 //or some other value, depending on how accurate you want it to be
while (steps > 0)
    checktime = left + (right - left) / 2
    if DistanceTest(checktime) is inside circle //if at the moment in the middle of the interval [left;right] the small circle is still inside the bigger one
        then left = checktime //the collision is after this moment of time
        else right = checktime //the collision is before
    steps -= 1
finaltime = left + (right - left) / 2 // the moment of time will be somewhere in between, so we take the moment in the middle of interval to have a small overall error

Una volta che conosci il tempo di collisione, calcola le posizioni dei due cerchi nel momento finale e il punto di collisione finale è

CollisionX = (Xb - Xa)*R/(R-r) + Xa
CollisionY = (Yb - Ya)*R/(R-r) + Ya

0

Ho implementato una demo di una palla che rimbalza in un cerchio su jsfiddle usando l'algoritmo descritto da Sam Hocevar :

http://jsfiddle.net/klenwell/3ZdXf/

Ecco il javascript che identifica il punto di contatto:

find_contact_point: function(world, ball) {
    // see https://gamedev.stackexchange.com/a/29658
    var A = world.point();
    var B = ball.point().subtract(ball.velocity());
    var C = ball.point();
    var R = world.r;
    var r = ball.r;

    var AB = B.subtract(A);
    var BC = C.subtract(B);
    var AB_len = AB.get_length();
    var BC_len = BC.get_length();

    var b = AB.dot(BC) / Math.pow(BC_len, 2) * -1;
    var c = (Math.pow(AB_len, 2) - Math.pow(R - r, 2)) / Math.pow(BC_len, 2);
    var d = b * b - c;
    var k = b - Math.sqrt(d);

    if ( k < 0 ) {
        k = b + Math.sqrt(d);
    }

    var BD = C.subtract(B);
    var BD_len = BC_len * k;
    BD.set_length(BD_len);

    var D = B.add(BD);
    return D;
}
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.