Snabbtips Kollisionsdetektion mellan en cirkel och en linje

I min tidigare Quick Tip tittade vi på idén om kollisionsdetektering i allmänhet och specifikt att upptäcka kollisioner mellan ett par cirklar. I denna Snabba Tips ser vi på att upptäcka en kollision mellan en cirkel och en rad.


Det här är resultatet vi ska arbeta på. Klicka på knappen Omstart för att omplacera alla cirklar högst upp på scenen och se dem falla ner.

Observera att cirklarna kolliderar med raden även utanför segmentet som ritas. Min nästa Snabbtips visar hur du fixar det här.


Steg 1: Den allmänna idén

För att kontrollera om någon cirkel har kolliderat med en rad, måste vi kolla vinkelrät längd från raden till cirkeln. Observera diagrammet nedan.

Det framgår av diagrammet ovan att fall 3 och 4 ska upptäcka kollision mellan cirkeln och linjen. Så vi slutsatsen att om den vinkelräta längden (markerad i rött) är lika med eller mindre än cirkelns radie, hände en kollision på grund av att cirkeln rörde eller överlappar linjen. Frågan är hur vi beräknar den vinkelräta längden? Tja, vektorer kan hjälpa till att förenkla vårt problem.


Steg 2: Linje Normal

För att dra en linje på scenen behöver vi två koordinater (c1 och c2). Linjen dras från c1 till c2 kommer att bilda en vektor som pekar mot c2 (observera pilens riktning).

Därefter måste vi hitta linjens vanligt. Linjens normala är en annan linje som gör 90 ° med den ursprungliga linjen och skär med den vid en punkt. Trots linjens normala varelse än en annan linje kan normals vektorform identifieras vidare som vänster eller höger normal relativt linjens vektor. Den vänstra normala är linjärvektorn själv, roterad -90 °. Rätt normal är densamma men roteras 90 °. Kom ihåg att y-axeln i Flash-koordinatutrymmet är inverterad jämfört med y-axeln på ett typiskt diagram - så positiv rotation är medurs och negativ rotation är moturs.


Steg 3: Projektion på vänster Normal

Vänster normal används i vårt försök att beräkna den vinkelräta längden mellan cirkeln och linjen. Detaljer finns i diagrammet nedan. en avser en vektor som pekar från c1 till cirkeln. Den vinkelräta längden refererar faktiskt till vektor A: er utsprång till vänster normal. Vi härleder den här projiceringen genom att använda trigonometri: det är | A | Cosine (theta), var | A | refererar till storleken av vektorn en.

Det enklaste tillvägagångssättet är att utnyttja vektordrift, speciellt punktprodukten. Med utgångspunkt från ekvationen för prickprodukten, omarrangerar vi villkoren så att vi kommer fram till det andra uttrycket som visas nedan. Observera att den högra sidan av den andra ekvationen är det projekt som vi ville beräkna!

Observera också att vänster och höger sida av ekvationen kommer att resultera i samma resultat, även om de är olika i deras förhållningssätt. Så istället för att använda ekvations högra sida kan vi välja vänster sida av ekvationen. För att enkelt komma fram till slutresultatet är det fördelaktigt att använda vänster eftersom variabler lätt kan lösas. Om vi ​​insisterar på att använda ekvationsrätten, måste vi driva ActionScript genom rigoriskt matematiskt arbete vid beräkning av vinkel theta. Vi avslutar med diagrammet nedan.

(* Ytterligare anmärkning: Om cirkeln faller under linjärvektorn, kommer den vinkelräta längden beräknad från formeln i ovanstående diagram att ge ett negativt värde.)


Steg 4: Genomförande av Cirkel-Line Kollisionsdetektion

Nu när vi har förstått metoden matematiskt, låt oss fortsätta att implementera det i ActionScript. I denna första sektionen noteras att linjens vektor roteras -90 ° för att bilda vänster normal.

 // deklarerar koordinater x1 = 50; y1 = 100; x2 = 250; y2 = 150; // teckningslinje graphics.lineStyle (3); graphics.moveTo (x1, y1); graphics.lineTo (x2, y2) // forming line vectors line = ny Vector2D (x2 - x1, y2 - y1); leftNormal = line.rotate (Math.PI * -0.5);

I det här andra avsnittet har jag markerat de beräknade beräkningarna och villkoret för att kontrollera kollision mellan cirkel och linje.

 Uppdatering av privat funktion (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); circles[i].y += 2; //if collision happened, undo movement if (Math.abs(c1_circle_onNormal) <= circles[i].radius) circles[i].y -= 2;   

För dem som vill undersöka vidare, är följande utdrag av metoder som används i Vector.as

 / ** * Metod för att få projicering av aktuell vektor på en given axel * @param axel En axel där vektor projiceras på * @return Projektionslängden av nuvarande vektor på given axel * / public function projectionOn (axel: Vector2D): Antal return this.dotProduct (axis.normalise ())
 / ** * Metod för att utföra punktprodukt med annan vektor * @param vector2 En vektor för att utföra punktprodukt med aktuell vektor * @return Ett skalärt antal punktprodukt * / allmän funktion dotProdukt (vektor2: Vector2D): Nummer var komponentX: Nummer = this._vecX * vector2.x; var komponentY: Nummer = this._vecY * vector2.y; returnera komponentX + komponentY; 
 / ** * Metod för att erhålla vektor enhet av nuvarande vektor * @return En kopia av normaliserad vektor * / allmän funktion normalisera (): Vector2D returnera ny Vector2D (this._vecX / this.getMagnitude (), this._vecY / this. getMagnitude ())
 / ** * Metod för att få aktuell magnitud av vektor * @return Storlek av typ Nummer * / allmän funktion getMagnitude (): Number return Math.sqrt (_vecX * _vecX + _vecY * _vecY); 

Steg 5: Resultatet

Tryck på Starta om för att omplacera alla cirklar högst upp på scenen. Observera att kollisionen ligger mellan hela linje (inklusive avsnittet ej ritat) och cirklarna. För att begränsa kollisionen till linjesegmentet, håll dig uppdaterad för nästa Snabba Tips.

Slutsats

Tack för att du läser. Håll dig klar för nästa tips.