Hurtig Tips Kollisjonsdeteksjon mellom en sirkel og et linjesegment

Vi dekket kollisjonsdeteksjon mellom en uendelig linje og sirkel i vår tidligere Quick Tip. Problemet som oppstod var imidlertid at linjen strekker seg lenger enn det synlige linjesegmentet; Faktisk strekker den seg inn i et hyperplan. I denne Quick Tip skal vi begrense vår kollisjonsdeteksjon til en linje segmentet bare.


Endelig resultatforhåndsvisning

Vi skal arbeide mot dette resultatet:

Klikk på Restart-knappen for å plassere sirklene øverst på scenen.


Trinn 1: To tilnærminger

Det er mange tilnærminger for å begrense kollisjonsdeteksjon innenfor et linjesegment. Vi skal se på to tilnærminger denne gangen. Den første tilnærmingen er litt strengere matematisk enn den andre, men disse er begreper som, hvis du forstår vellykket, vil sikkert være til nytte for deg i fremtiden. Begge tilnærmingene manipulerer prikkproduktets karakteristikk for å være et mål på hvordan parallelle to givne vektorer er.

La oss ta en titt på den første tilnærmingen. Anta at A og B er vektorer. Hvis A og B er parallelle - eller i det minste peker i samme retning - vil punktproduktet mellom A og B gi et positivt tall. Hvis A og B peker rett overfor hverandre - eller i det minste peker mot motsatt retning - vil punktproduktet mellom A og B gi et negativt tall. Hvis A og B er ortogonale (danner 90 ° til hverandre), vil punktproduktet produsere 0.

Diagrammet nedenfor oppsummerer denne beskrivelsen.


Trinn 2: Forbind punktproduktet med forholdene

Vi må danne vektorer B og C fra begge ender av linjesegmentet slik at deres prikkprodukt med linjesegmentets vektor, A, kan bestemme om sirkelen er innenfor segmentet.

Se diagrammet under. Hvis sirkelen er innenfor segmentet, er verdien av punktproduktet mellom A og B positiv og at mellom A og C er negativ.

Diagrammet nedenfor viser hvordan punktproduktet endres avhengig av om sirkelen er utenfor eller innenfor linjesegmentet. Legg merke til forskjellene i verdien av prikkproduktet.

Vær også oppmerksom på at "innenfor linjesegmentet" ikke betyr at sirkelen nødvendigvis krysser linjesegmentet, bare at den faller innenfor de to tynne linjene på diagrammet over.

Så når kollisjon skjer mellom linje og sirkel, som vi har sett i forrige Quick Tip, må vi undersøke om sirkelen er plassert innenfor linjesegmentet. Hvis det er, vet vi at det er et ekte kryss.


Trinn 3: Implementering

Trinn 2 forklarte at konseptet vi bruker for å begrense kollisjonsdeteksjon, ligger innenfor linjesegmentet. Det er imidlertid fortsatt en feil i presisjonen. Du ser, det definerte området er litt tiltet; Vi bør sikte på å bruke området definert i henhold til diagrammet nedenfor.

Dette er enkelt: vi beregner bare D som den horisontale projeksjonen av A. Da, i stedet for å bruke A, bruker vi D til punktprodukt med B og C. Alle forholdene som forklart i trinn 2 står fortsatt, men i stedet for et skråstilt segment , vi har definert et vertikalt område.

Denne korreksjonen kan visuelt verdsettes hvis sirkelen er stor; hvis sirkelen var liten, ville senteret være så nær linjen at denne visuelle feilen ville være vanskelig å oppdage, slik at vi kunne komme seg bort med å bruke det litt skråstille området og spare oss selv litt prosessorkraft.

Likevel vil jeg prøve å gjøre ting på riktig måte. Du kan velge din tilnærming ved å endre tilstanden litt.


Trinn 4: Implementering

Den første Actionscript-snippet her setter opp vektor D (v_line_onX)

 // Att2: å få den horisontale vektoren var line_onX: Number = line.projectionOn (ny Vector2D (1, 0)); v_line_onX = ny Vector2D (1, 0); v_line_onX.setMagnitude (line_onX);

Merk: Vi bruker klasser fra mine tidligere opplæringsprogrammer her. Vector2D ble introdusert i Gravity in Action, men du trenger ikke å lese det for å bruke klassen, det er inkludert i kilde nedlasting.

Det andre Actionscript-kodestykket her setter opp B (c1_circle) og C (c2_circle) og kontrollerer kollisjonen og om sirkelen er inne i segmentet eller ikke.

 privat funksjon oppdatering (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.dotProdukt (c2_circle) < 0 ) //if collision happened, undo movement circles[i].y -= 2;   

Trinn 5: Resultatet

Her er resultatet for den første tilnærmingen. Klikk på knappen for å tilbakestille posisjoner i alle sirkler til toppen av scenen.


Trinn 6: Andre tilnærming

Den andre tilnærmingen er mye enklere. Jeg vil prøve å jobbe bakover fra slutten denne gangen.

Se diagrammet under. Linjesegmentet er fra c1 til c2. Det er klart det collide1 og collide3 er begge utenfor linjesegmentet, og det eneste collide2 er innenfor linjesegmentet.

La v1, v2 og v3 være vektorer fra c1 til respektive sirkler. Bare v2 og v3 er parallelle - eller i det minste peker i lignende retninger til linjevektoren (c1 til c2). Ved å sjekke for en positiv verdi i punktproduktet mellom linjevektoren og hver av disse vektorene fra c1 til de tilsvarende sirkelsentrene (v1, v2, v3), kan vi enkelt fastslå at collide1 er utenfor linjesegmentet. Med andre ord, c1. v1 .

Deretter skal vi utarbeide en metode for å fastslå at collide3 er utenfor linjesegmentet. Dette burde være enkelt. Det er åpenbart at v3s projeksjon langs linjevektoren vil overstige lengden på linjesegmentet. Vi skal bruke denne egenskapen til å luke sammen kollider3.

Så la meg oppsummere den andre tilnærmingen:

  • Først ser vi etter et skjæringspunkt mellom uendelig linje og sirkel.
  • Hvis det er skjæringspunkt, undersøk ytterligere følgende for å avgjøre om det skjer innenfor linjesegmentet:
    • Kontroller at en positiv verdi er produsert når vi tar punktproduktet fra vektoren fra c1 til sirkel og linjevektoren, og
    • Kontroller at størrelsen på projeksjonen av vektoren langs linjevektoren er kortere enn linjesegmentets lengde.

Trinn 7: Implementering

Her er ActionScript-implementeringen av det ovennevnte:

 privat funksjon oppdatering (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;   

Trinn 8: Resultatet

I hovedsak vil det gi det samme resultatet som det forrige, men siden det er noen linjer med kode kortere i den andre tilnærmingen, antar jeg det er bedre.

Konklusjon

Håper dette har hjulpet. Takk for at du leste. Neste opp, vi ser på kollisionsreaksjon.