Sfat rapid detectarea coliziunii între un cerc și un segment de linie

Am acoperit detectarea coliziunilor între o linie infinită și un cerc în Sfatul nostru rapid anterior. Totuși, problema care a apărut a fost că linia se extinde mai departe decât segmentul de linie vizibilă; de fapt, se extinde într-un hyperplan. În acest sfat rapid, vom limita detectarea coliziunii noastre la cea a unei linii segment numai.


Rezultatul final al rezultatelor

Vom lucra la acest rezultat:

Faceți clic pe butonul Restart pentru a repoziționa cercurile din partea de sus a etapei.


Pasul 1: Două abordări

Există numeroase abordări pentru a limita detectarea coliziunilor într-un segment de linie. Vom analiza două abordări de data aceasta. Prima abordare este puțin mai riguroasă matematic decât cea de-a doua, dar acestea sunt concepte care, dacă înțelegeți cu succes, vă vor fi de folos în viitor. Ambele abordări manipulează caracteristica punctului produsului de a fi o măsură a cât de paralele sunt două vectori dat.

Să aruncăm o privire la prima abordare. Să presupunem că A și B sunt vectori. Dacă A și B sunt paralele - sau cel puțin îndreptate în aceeași direcție - produsul punct între A și B va produce un număr pozitiv. Dacă A și B sunt îndreptate direct unul față de celălalt - sau cel puțin indicând direcții opuse - produsul punct între A și B va produce un număr negativ. Dacă A și B sunt ortogonale (formând 90 ° între ele), atunci produsul punct va produce 0.

Diagrama de mai jos rezumă această descriere.


Pasul 2: Relaționați produsul Dot cu condițiile

Va trebui să formăm vectorii B și C de la ambele capete ale segmentului de linie, astfel încât produsul lor dot cu vectorul segmentului de linie, A, să poată determina dacă cercul se află în segmentul.

Respectați diagrama de mai jos. Dacă cercul se află în segment, atunci valoarea produsului punct între A și B este pozitivă și între A și C este negativă.

Diagrama de mai jos arată modul în care se modifică produsul punctat în funcție de faptul dacă cercul se află în afara sau în cadrul segmentului de linie. Observați diferențele în valoarea produsului dotat.

De asemenea, rețineți că "în cadrul segmentului de linie" nu înseamnă că cercul intersectează neapărat segmentul de linie, doar că se încadrează în cele două linii subțiri de pe diagrama de mai sus.

Deci, atunci când se produce o coliziune între linie și cerc, așa cum am văzut în Sfatul rapid anterior, trebuie să investigăm în continuare dacă cercul este poziționat în segmentul de linie. Dacă este, atunci știm sigur că există o intersecție reală.


Pasul 3: Implementarea

Pasul 2 a explicat conceptul pe care îl folosim pentru a restrânge detectarea coliziunilor în cadrul segmentului de linie. Cu toate acestea, există încă o defecțiune în precizie. Vedeți, zona definită este puțin înclinată; ar trebui să urmărim utilizarea zonei definite conform diagramei de mai jos.

Acest lucru este ușor: pur și simplu calculam D ca proiecție orizontală a lui A. Apoi, în loc să folosim A, vom folosi produsul D la punct cu B și C. Toate condițiile prezentate în Etapa 2 rămân în continuare, dar în loc de un segment înclinat , am definit o zonă verticală.

Această corecție poate fi apreciată vizual dacă cercul este mare; dacă cercul ar fi mic, centrul său ar fi atât de aproape de linia în care acest defect vizual ar fi greu de detectat, așa că am putea să scăpăm cu ajutorul acelei zone ușor înclinate și să ne salvăm niște putere de procesare.

Cu toate acestea, voi încerca să fac lucrurile corect. Puteți alege abordarea prin modificarea condiției ușor.


Pasul 4: Implementarea

Primul fragment Actionscript stabilește vectorul D (v_line_onX)

 // Att2: obținerea vectorului orizontal var line_onX: Number = line.projectionOn (new Vector2D (1, 0)); v_line_onX = Vector2D nou (1, 0); v_line_onX.setMagnitude (line_onX);

Notă: Folosim clase din tutorialele mele anterioare aici. Vector2D a fost introdus în Gravity in Action, dar nu trebuie să citești că să folosești clasa, este inclus în sursa de descărcare.

Al doilea fragment Actionscript stabilește B (c1_circle) și C (c2_circle) și verifică coliziunea și dacă cercul se află în interiorul segmentului sau nu.

 actualizarea funcției private (e: Event): void pentru (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;   

Pasul 5: Rezultatul

Iată rezultatul pentru prima abordare. Faceți clic pe buton pentru a reseta pozițiile tuturor cercurilor în partea de sus a etapei.


Pasul 6: A doua abordare

A doua abordare este mult mai simplă. Voi încerca să lucrez înapoi de la sfârșitul acestui moment.

Respectați diagrama de mai jos. Segmentul de linie este de la c1 la c2. E clar că asta collide1 și collide3 sunt atât în ​​afara segmentului de linie, cât și numai în acest caz collide2 este în segmentul de linie.

Fie v1, v2 și v3 vectori de la c1 la cercuri respective. Doar v2 și v3 sunt paralele - sau cel puțin îndreptate în direcții similare cu vectorul de linie (c1 - c2). Prin verificarea unei valori pozitive în produsul punct între vectorul de linie și fiecare dintre acei vectori de la c1 la centrele de cercuri corespunzătoare (v1, v2, v3), putem determina cu ușurință că coliziunea1 este dincolo de segmentul de linie. Cu alte cuvinte, c1. v1 .

Apoi, vom elabora o metodă pentru a determina că coliziunea 3 se află în afara segmentului de linie. Acest lucru ar trebui să fie ușor. Este evident că proiecția v3 de-a lungul vectorului de linie va depăși lungimea segmentului de linie. Vom folosi această caracteristică pentru a elimina coliziunea.

Așadar, permiteți-mi să rezumă a doua abordare:

  • Mai întâi verificăm o intersecție între linia infinită și cerc.
  • Dacă există o intersecție, investigați în continuare următoarele pentru a determina dacă se întâmplă în cadrul segmentului de linie:
    • Verificați dacă o valoare pozitivă este produsă atunci când luăm produsul punctului vectorului de la c1 la cerc și vectorul liniei și
    • Verificați dacă magnitudinea proiecției vectorului de-a lungul vectorului de linie este mai scurtă decât lungimea segmentului de linie.

Pasul 7: Implementarea

Iată implementarea ActionScript a celor de mai sus:

 actualizarea funcției private (e: Event): void pentru (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;   

Pasul 8: Rezultatul

În esență, va produce același rezultat ca și precedentul, dar deoarece există câteva linii de cod mai scurte în a doua abordare, cred că este mai bine.

Concluzie

Sper că acest lucru a ajutat. Vă mulțumim pentru lectură. În continuare, ne vom uita la reacția de coliziune.

Cod