Snelle tip botsingsdetectie tussen een cirkel en een lijnsegment

We hebben botsingsdetectie tussen een oneindige lijn en cirkel gedekt in onze vorige Quick Tip. Het probleem dat ontstond was echter dat de lijn verder reikt dan het zichtbare lijnsegment; in feite strekt het zich uit tot een hyperplane. In deze Quick Tip beperken we onze botsingsdetectie tot die van een lijn segment enkel en alleen.


Eindresultaat voorbeeld

We zullen werken aan dit resultaat:

Klik op de knop Opnieuw opstarten om de cirkels boven in het werkgebied te verplaatsen.


Stap 1: twee benaderingen

Er zijn talloze manieren om botsingdetectie te beperken tot binnen een lijnsegment. We zullen deze keer naar twee benaderingen kijken. De eerste benadering is mathematisch een beetje rigoureuzer dan de tweede, maar dit zijn concepten die, als je ze met succes begrijpt, zeker in de toekomst van je zullen profiteren. Beide benaderingen manipuleren het kenmerk van het puntproduct dat het een maat is voor hoe parallel twee gegeven vectoren zijn.

Laten we de eerste aanpak eens bekijken. Stel dat A en B vectoren zijn. Als A en B evenwijdig zijn - of in elk geval in dezelfde richting wijzen - levert het puntproduct tussen A en B een positief getal op. Als A en B direct tegenover elkaar wijzen - of ten minste in tegenovergestelde richting wijzen - zal het puntproduct tussen A en B een negatief getal produceren. Als A en B orthogonaal zijn (vormen zich 90 ° ten opzichte van elkaar), dan produceert het puntproduct 0.

Het onderstaande schema vat deze beschrijving samen.


Stap 2: verband tussen puntproduct en voorwaarden

We moeten de vectoren B en C vanaf beide uiteinden van het lijnsegment vormen, zodat hun puntproduct met de vector van het lijnsegment, A, kan bepalen of de cirkel binnen het segment valt.

Bekijk het diagram hieronder. Als de cirkel zich binnen het segment bevindt, is de waarde van het puntproduct tussen A en B positief en tussen A en C negatief.

Het onderstaande diagram laat zien hoe het puntproduct verandert afhankelijk van of de cirkel zich buiten het lijnsegment bevindt of binnen het lijnsegment. Let op de verschillen in de waarde van het puntproduct.

Merk ook op dat "binnen het lijnsegment" niet betekent dat de cirkel noodzakelijkerwijs het lijnsegment snijdt, alleen dat het binnen de twee dunne lijnen op het diagram hierboven valt.

Dus wanneer een botsing plaatsvindt tussen lijn en cirkel, zoals we hebben gezien in de vorige Quick Tip, moeten we verder onderzoeken of de cirkel zich binnen het lijnsegment bevindt. Als dat zo is, weten we zeker dat er een echte kruising is.


Stap 3: Implementatie

In stap 2 werd uitgelegd welk concept we gebruiken om de botsingsdetectie te beperken tot het lijnsegment. Er is echter nog steeds een fout in de precisie. Zie je, het gedefinieerde gebied is een beetje gekanteld; we moeten ernaar streven om het gebied te gebruiken dat is gedefinieerd volgens het onderstaande schema.

Dit is eenvoudig: we berekenen D gewoon als de horizontale projectie van A. Dan gebruiken we D in plaats van A om het product met B en C te dotten. Alle voorwaarden zoals uitgelegd in stap 2 staan ​​nog steeds, maar in plaats van een gekanteld segment , we hebben een verticaal gebied gedefinieerd.

Deze correctie kan visueel worden gewaardeerd als de cirkel groot is; als de cirkel klein was, zou het midden ervan zo dicht bij de lijn zijn dat deze visuele fout moeilijk te detecteren zou zijn, zodat we weg zouden kunnen komen met gebruik van dat enigszins gekantelde gebied en onszelf wat verwerkingskracht besparen.

Toch zal ik proberen de dingen op de juiste manier te doen. U kunt uw nadering kiezen door de toestand enigszins aan te passen.


Stap 4: Implementatie

Het eerste Actionscript-fragment hier stelt vector D in (v_line_onX)

 // Att2: krijgt de horizontale vector var line_onX: Number = line.projectionOn (new Vector2D (1, 0)); v_line_onX = nieuwe Vector2D (1, 0); v_line_onX.setMagnitude (line_onX);

Notitie: We gebruiken hier lessen uit mijn vorige tutorials. Vector2D is geïntroduceerd in Gravity in Action, maar je hoeft dat niet te lezen om het te gebruiken, het is opgenomen in de brondownload.

Het tweede Actionscript-fragment hier stelt B in (c1_circle) en C (c2_circle) en controleert op de botsing en of de cirkel binnen het segment valt of niet.

 persoonlijke functie vernieuwen (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;   

Stap 5: Het resultaat

Dit is het resultaat voor de eerste benadering. Klik op de knop om de posities van alle cirkels naar de bovenkant van het podium te resetten.


Stap 6: Tweede benadering

De tweede benadering is veel eenvoudiger. Ik zal proberen deze keer achteruit te werken vanaf het einde.

Bekijk het diagram hieronder. Het lijnsegment is van c1 tot c2. Het is duidelijk dat collide1 en collide3 zijn beide buiten het lijnsegment en alleen dat collide2 bevindt zich binnen het lijnsegment.

Laat v1, v2 en v3 vectoren zijn van c1 naar respectieve cirkels. Alleen v2 en v3 zijn parallel - of wijzen minstens in dezelfde richting als de lijnvector (c1 tot c2). Door te controleren op een positieve waarde in het puntproduct tussen de lijnvector en elk van die vectoren van c1 naar de overeenkomstige cirkelcentra (v1, v2, v3), kunnen we gemakkelijk bepalen dat collide1 voorbij het lijnsegment ligt. Met andere woorden, c1. v1 .

Vervolgens zullen we een methode bedenken om te bepalen dat collide3 buiten het lijnsegment valt. Dit zou gemakkelijk moeten zijn. Het is duidelijk dat de projectie van v3 langs de lijnvector de lengte van het lijnsegment overschrijdt. We zullen dit kenmerk gebruiken om collide3 te verwijderen.

Dus laat me de tweede benadering samenvatten:

  • Eerst controleren we op een kruising tussen de oneindige lijn en de cirkel.
  • Als er een kruising is, onderzoekt u het volgende om te bepalen of dit gebeurt binnen het lijnsegment:
    • Controleer of een positieve waarde wordt geproduceerd wanneer we het puntproduct van de vector van c1 naar cirkel nemen en de lijnvector, en
    • Controleer of de grootte van de projectie van de vector langs de lijnvector korter is dan de lengte van het lijnsegment.

Stap 7: Implementatie

Dit is de ActionScript-implementatie van het bovenstaande:

 persoonlijke functie vernieuwen (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;   

Stap 8: Het resultaat

In wezen zal het hetzelfde resultaat opleveren als het vorige, maar aangezien er een paar regels code korter is in de tweede benadering, denk ik dat het beter is.

Conclusie

Ik hoop dat dit heeft geholpen. Bedankt voor het lezen. Vervolgens zullen we de botsingsreactie bekijken.