Abbiamo coperto il rilevamento delle collisioni tra una linea infinita e un cerchio nel nostro precedente suggerimento rapido. Tuttavia, il problema emerso è che la linea si estende oltre il segmento di linea visibile; infatti, si estende in un iperpiano. In questo suggerimento, limiteremo il rilevamento delle collisioni a quello di una linea segmento solo.
Lavoreremo verso questo risultato:
Fai clic sul pulsante Riavvia per riposizionare i cerchi nella parte superiore dello stage.
Esistono numerosi approcci per limitare il rilevamento delle collisioni all'interno di un segmento di linea. Vedremo due approcci questa volta. Il primo approccio è un po 'più rigoroso matematicamente rispetto al secondo, ma questi sono concetti che, se riuscirai a coglierli con successo, sicuramente ti avvantaggeranno in futuro. Entrambi gli approcci manipolano la caratteristica del prodotto del punto di essere una misura di quanto siano paralleli due vettori dati.
Diamo un'occhiata al primo approccio. Supponiamo che A e B siano vettori. Se A e B sono paralleli, o almeno puntano nella stessa direzione, il prodotto con punti tra A e B produrrà un numero positivo. Se A e B puntano direttamente l'uno di fronte all'altro - o almeno puntano in direzioni opposte - il prodotto con punti tra A e B produrrà un numero negativo. Se A e B sono ortogonali (formando un angolo di 90 ° tra loro), il prodotto del punto produrrà 0.
Lo schema seguente riassume questa descrizione.
Dovremo formare i vettori B e C da entrambe le estremità del segmento di linea in modo che il loro prodotto punto con il vettore del segmento di linea, A, possa determinare se il cerchio si trova all'interno del segmento.
Osservare lo schema qui sotto. Se il cerchio si trova all'interno del segmento, il valore del prodotto punto tra A e B è positivo e quello tra A e C è negativo.
Lo schema seguente mostra come il prodotto del punto cambia a seconda che il cerchio si trovi al di là o all'interno del segmento di linea. Notare le differenze nel valore del prodotto punto.
Si noti inoltre che "all'interno del segmento di linea" non significa che il cerchio interseca necessariamente il segmento di linea, ma che rientra nelle due linee sottili del diagramma sopra.
Quindi, quando si verifica una collisione tra la linea e il cerchio, come abbiamo visto nel precedente Suggerimento rapido, dobbiamo esaminare ulteriormente se il cerchio è posizionato all'interno del segmento di linea. Se lo è, allora sappiamo per certo che esiste un vero incrocio.
La fase 2 ha spiegato il concetto che usiamo per limitare il rilevamento delle collisioni all'interno del segmento di linea. Tuttavia, c'è ancora un difetto nella precisione. Vedete, l'area definita è un po 'inclinata; dovremmo mirare a utilizzare l'area definita secondo lo schema seguente.
Questo è facile: calcoliamo semplicemente D come proiezione orizzontale di A. Quindi, invece di usare A, usiamo D per dotare il prodotto con B e C. Tutte le condizioni come spiegato nel Passaggio 2 rimangono valide, ma invece di un segmento inclinato , abbiamo definito un'area verticale.
Questa correzione può essere apprezzata visivamente se il cerchio è grande; se il cerchio fosse piccolo, il suo centro sarebbe così vicino alla linea che questo difetto visivo sarebbe difficile da rilevare, quindi potremmo farcela usando quell'area leggermente inclinata e risparmiarci un po 'di potenza di elaborazione.
Tuttavia, cercherò di fare le cose nel modo corretto. Puoi scegliere il tuo approccio modificando leggermente la condizione.
Il primo snippet Actionscript qui imposta il vettore D (v_line_onX
)
// Att2: ottenendo il vettore orizzontale var line_onX: Number = line.projectionOn (new Vector2D (1, 0)); v_line_onX = new Vector2D (1, 0); v_line_onX.setMagnitude (line_onX);
Nota: Stiamo usando le classi delle mie precedenti esercitazioni qui. Vector2D è stato introdotto in Gravity in Action, ma non è necessario leggerlo per utilizzare la classe, è incluso nel download sorgente.
Il secondo snippet Actionscript qui imposta B (c1_circle
) e C (c2_circle
) e controlla la collisione e se il cerchio si trova all'interno del segmento o meno.
aggiornamento della funzione privata (e: Event): void for (var i: int = 0; i < circles.length; i++) //calculating line's perpendicular distance to ball var c1_circle:Vector2D = new Vector2D(circles[i].x - x1, circles[i].y - y1); var c1_circle_onNormal:Number = c1_circle.projectionOn(leftNormal); //Att2: get vector from c2 to circle var c2_circle:Vector2D = new Vector2D(circles[i].x - x2, circles[i].y - y2); circles[i].y += 2; if ( c1_circle_onNormal <= circles[i].radius && v_line_onX.dotProduct(c1_circle) > 0 && v_line_onX.dotProduct (c2_circle) < 0 ) //if collision happened, undo movement circles[i].y -= 2;
Ecco il risultato per il primo approccio. Fai clic sul pulsante per ripristinare le posizioni di tutti i cerchi nella parte superiore dello stage.
Il secondo approccio è molto più semplice. Cercherò di tornare indietro alla fine questa volta.
Osservare lo schema qui sotto. Il segmento di linea va da c1 a c2. È chiaro collide1
e collide3
sono entrambi al di fuori del segmento di linea, e solo quello collide2
è all'interno del segmento di linea.
Sia v1, v2 e v3 siano vettori da c1 a rispettivi cerchi. Solo la v2 e la v3 sono parallele o almeno puntano in direzioni simili al vettore della linea (da c1 a c2). Controllando la presenza di un valore positivo nel prodotto punto tra il vettore linea e ciascuno di questi vettori da c1 ai centri cerchio corrispondenti (v1, v2, v3), possiamo facilmente determinare che collide1 è oltre il segmento di linea. In altre parole, c1. v1 .
Successivamente, creeremo un metodo per determinare che collide3 è al di fuori del segmento di linea. Questo dovrebbe essere facile. È ovvio che la proiezione di v3 lungo il vettore di linee supererà la lunghezza del segmento di linea. Useremo questa caratteristica per eliminare la collisione3.
Quindi, riassumo il secondo approccio:
Ecco l'implementazione di ActionScript di cui sopra:
aggiornamento della funzione privata (e: Event): void for (var i: int = 0; i < circles.length; i++) //calculating line's perpendicular distance to ball var c1_circle:Vector2D = new Vector2D(circles[i].x - x1, circles[i].y - y1); var c1_circle_onNormal:Number = c1_circle.projectionOn(leftNormal); //Att2: getting the relevant vectors var c1_circle_onLine:Number = c1_circle.projectionOn(line); circles[i].y += 2; if ( Math.abs(c1_circle_onNormal) <= circles[i].radius && line.dotProduct(c1_circle) > 0 && c1_circle_onLine < line.getMagnitude() ) //if collision happened, undo movement circles[i].y -= 2;
Essenzialmente, produrrà lo stesso risultato del precedente, ma dal momento che ci sono poche righe di codice più corte nel secondo approccio, immagino sia meglio.
Spero che questo abbia aiutato. Grazie per aver letto. Quindi, esamineremo la reazione di collisione.