Math și ActionScript ale curbelor Rădăcini

În primul tutorial al acestei serii, am aruncat o privire la curbele de desen folosind ecuațiile și AS3. Acum, vom aborda rezolvarea acestor ecuații pentru a găsi rădăcinile unei curbe - adică locurile în care curba traversează o linie dreaptă dată. Putem folosi acest lucru pentru a anticipa coliziunile cu suprafețe curbe și pentru a evita "tunelul" în jocurile Flash.


Pasul 1: Rădăcinile patrate

În primul rând, timpul pentru o revizuire matematică rapidă. În acest tutorial, vom accepta și vom aplica metodele pe care le vom folosi, dar cititorii interesați se pot referi la pagina Wikipedia pe ecuațiile patratice pentru informații despre derivațiile matematice.

Deci \ (f (x) \) este o funcție patratică. Dacă \ (f (x) \) este echivalent cu 0, \ (x \) poate fi obținut prin următoarea formulă:

\ [Dată \ f (x) \ = \ ax ^ 2 + bx + c, \\]
\ f \ frac -b \ pm \ sqrt b ^ 2 - 4ac 2a \]

\ (b ^ 2 - 4ac \) se numește discriminantă de formula. Dacă discriminantul este negativ, rădăcina pătrată a discriminatorului va produce imaginația rădăcinilor, pe care nu putem complota. În schimb, dacă discriminarea este pozitivă, veți avea rădăcini cu număr real și veți putea să le complotați pe ecran.


Pasul 2: Vizualizarea rădăcinilor patrate

Deci, care sunt rădăcinile? Ei bine, în contextul nostru, ele nu sunt decât puncte de intersecție între curba patratică și o linie. De exemplu, să presupunem că suntem interesați să găsim punctul (punctele) de intersecție al următorului set de ecuații:

\ (
f (x) \ = \ ax ^ 2 + bx + c \\
g (x) \ = \ 0
\)

Acesta este un scenariu tipic de căutare a punctului (punctelor) de intersecție între o curbă patratică și axa x (deoarece axa x este linia unde y == 0). Deoarece, prin definiție, punctele de intersecție sunt partajate de \ (f (x) \) și \ (g (x) \), putem concluziona că \ (f (x) = g (x) de X pe care îl căutăm.

Este o operațiune trivială în care înlocuiți funcțiile și apoi aplicați formula de la Pasul 1 pentru a obține rădăcinile. Acum există mai multe posibilități pe care le putem anticipa după cum se arată mai jos.

(După cum puteți vedea, "rădăcinile imaginare" înseamnă, pentru scopurile noastre, că curba nu trece niciodată axa x.)

Acum, să luăm în considerare cazul în care \ (g (x) \) este mai mult decât o linie orizontală orizontală. Să presupunem că este o linie înclinată, \ (g (x) \ = \ mx \ + \ d \). Acum, când echivalăm ambele funcții, va trebui să facem o mică prealabilă înainte ca formula să poată fi aplicată în mod eficient.

\ [
ax ^ 2 \ + \ bx + c \ = \ mx \ + \ d \\
ax ^ 2 \ + \ (\ b \ - m) \ x + (c \ - \ d) \ = \ 0
\]

Am inclus o prezentare Flash interactivă de mai jos, deci nu ezitați să trageți punctele roșii și albastre. Punctele galbene indică punctele de intersecție. Este posibil să fie necesar să poziționați curba și linia pentru a se intersecta pentru a se afișa puncte galbene.


Pasul 3: Plotarea cu ActionScript

Scriptul complet poate fi găsit în Demo1.as; aici voi explica un extras crucial al codului. Să ne uităm la AS3 pentru desenarea curbei și liniei:

 funcția privată redraw (): void var cmd: Vector. = Vector nou.; var coord: Vector. = Vector nou.; // curba redraw; m1 = noua Matrix3d ​​(curve_points [0] .x * curve_points [0] .x, curve_points [0] .x, 1, 0, curve_points [1] .x * curve_points [1] , 1, 0, curve_points [2] .x * curve_points [2] .x, curve_points [2] .x, 1, 0, 0,0, 1, 1); m2 = noua Matrix3d ​​(curve_points [0] .y, 0, 0, 0, curve_points [1] .y, 0, 0, 0, curve_points [2] .y, 0, 0, 0, 1) m1.invert (); m2.append (m1); quadratic_equation.define (m2.n11, m2.n21, m2.n31); pentru (var i: int = 0; i < stage.stageWidth; i+=2)  if (i == 0) cmd.push(1); else cmd.push(2); coord.push(i, quadratic_equation.fx_of(i));  //draw line n1 = new Matrix(); n1.a = line_points[0].x; n1.c = 1; n1.b = line_points[1].x; n1.d = 1; n2 = new Matrix(); n2.a = line_points[0].y; n2.c = 0; n2.b = line_points[1].y; n2.d = 0; n1.invert(); n2.concat(n1); var x:Number = stage.stageWidth //y = mx + c cmd.push(1); coord.push(0, n2.a * 0 + n2.b); cmd.push(2); coord.push(x, n2.a * x + n2.b); graphics.clear(); graphics.lineStyle(1); graphics.drawPath(cmd, coord); 

Majoritatea acțiunilor ActionScript pentru trasarea curbei de la linia 80 ~ 104 sunt în mare parte împrumutate din tutorialul anterior, așa că voi explica doar puțin despre codul pentru desenarea unei linii.

În prezentarea Flash de mai sus, există două puncte interactive albastre. Fiecare dintre acestea are coordonate, iar cu ambele puncte se formează o linie. Deoarece ambele puncte se află pe aceeași linie, aceștia împărtășesc o pantă comună și intersectarea y pentru a forma o ecuație generală de linie:

\ [
y \ = \ mx \ + \ d, \\
m \ = \ panta, \ d \ = \ y-intercepta
\]

Putem folosi o algebră mică pentru a rezolva cele două necunoscute, \ (m \) și \ (d \). Având coordonatele celor două puncte albastre ca \ ((x_1, \ y_1) \) și \ ((x_2, \ y_2) \):

[Latex]
y_1 = mx_1 + d \\
y_2 = mx_2 + d \\
[/ Latex]

[Latex]
\ begin bmatrix y_1 \\ y_2 \ end bmatrix =
\ begin bmatrix x_1 & 1 \\ x_2 & 1 \ end bmatrix
\ begin bmatrix m \\ d \ end bmatrix \\
[/ Latex]

[Latex]
\ begin bmatrix x_1 & 1 \\ x_2 & 1 \ end bmatrix ^ - 1
\ begin bmatrix y_1 \\ y_2 \ end bmatrix =
\ begin bmatrix x_1 & 1 \\ x_2 & 1 \ end bmatrix ^ - 1
\ begin bmatrix x_1 & 1 \\ x_2 & 1 \ end bmatrix
\ begin bmatrix m \\ d \ end bmatrix \\
[/ Latex]

[Latex]
\ begin bmatrix x_1 & 1 \\ x_2 & 1 \ end bmatrix ^ - 1
\ begin bmatrix y_1 \\ y_2 \ end bmatrix =
eu
\ begin bmatrix m \\ d \ end bmatrix
[/ Latex]

(Rețineți că o matrice cu superscript -1 se referă la inversarea acelei matrici.)

Folosind acest lucru, \ (m \) și \ (d \) sunt calculate. Acum putem trasa linia prin îmbinarea coordonatelor \ ((0, y_3) \) și \ ((stage.stageWidth, y_4) \). Cum găsiți \ (y_3 \) și \ (y_4 \)? Ei bine, acum că \ (m \), \ (x \) și \ (d \) sunt cunoscuți, putem pune pur și simplu toate aceste valori în ecuația generală a liniei,

\ (y \ = \ mx \ + \ d \)

... pentru a obține acele \ (y \) s.


Pasul 4: Calculați rădăcinile patrate

Pentru a calcula poziția punctelor intersectate, vom folosi formula de la pasul 1. Aceasta se face în EqQuadratic.as ca funcțiile prezentate mai jos:

 / ** Numai pentru citire * Discriminant al ecuației * / funcția publică devine discriminatorie (): Numărul // B * B-4 * A * C retur _B * _B - 4 * _A * _C;  / ** * Efectuează calculul pentru a obține rădăcini * / funcția publică calcRoots (): void var disc: Number = this.discriminant // manipula rădăcinile imaginare dacă (disc < 0)  disc *= -1; var component_real:Number = -_B / (2 * _A); var component_imaginary:Number = Math.sqrt(disc) / (2 * _A); _root_i[0] = (component_real + "+ i" + component_imaginary).toString(); _root_i[1] = (component_real + "- i" + component_imaginary).toString();  //handle real roots else  var sqrt:Number = Math.sqrt(disc); _root_R[0] = ( -_B + sqrt) / (2 * _A); _root_R[1] = ( -_B - sqrt) / (2 * _A);  

Detalii suplimentare despre EqQuadratic.as:

Funcţie Tip Parametrul de intrare Funcționalitate
EqQuadratic Metodă Zero Constructor de clase
defini Metodă Coeficienții a, b și c ai ecuației patrate Instanțiați valorile coeficienților
fx_of Metodă Valoarea x Returnează \ (f (x) \) datei \ (x \) date.
calcRoots Metodă Zero Efectuează calcul pentru obținerea rădăcinii patrate
DIFF1 Metodă \ (x \) coordonează pentru diferențierea de gradul întâi Diferențiată \ (f (x) \) a dat \ (x \) la primul grad.
DIFF2 Metodă \ (x \) se coordonează pentru diferențierea de gradul doi Diferențiată \ (f (x) \) a dat \ (x \) la gradul al doilea.
discriminat Proprietatea, doar pentru citire Zero Returnează valoarea discriminantului, \ (b ^ 2 - 4ac \)
roots_R Proprietatea, doar pentru citire Zero Returnează un vector de număr pentru rădăcinile unui număr real. Elementele sunt NaN dacă nu există rădăcini reale.
roots_i Proprietatea, doar pentru citire Zero Returnează un vector String pentru rădăcinile numărului imaginar. Elementele sunt nulă dacă nu există rădăcini imaginare.

Pasul 5: Plotarea cu ActionScript

Un exemplu de utilizare a acestui lucru EqQuadratic.as este in Demo1.as. După inițierea EqQuadratic, îl vom folosi pentru a calcula rădăcinile. Apoi, după validarea prezenței rădăcinilor reale, le vom folosi pentru a complota punctele galbene.

Acum, rădăcinile se referă numai la componenta \ (x \) a coordonatelor. Pentru a obține \ (y \), ghici ce? Din nou, în ecuația generală a liniei, \ (m \), \ (d \) (calculată anterior în pasul 3) și \ (x \) (din rădăcini) + \ d \). Verificați codul corespunzător în rândul 135 și 136.

 funcția privată recalculate_reposition (): void quadratic_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); Var rădăcini: Vector. = quadratic_equation.roots_R; dacă ! isNaN (rădăcini [0]) &&! isNaN (rădăcini [1])) intersec_points [0] .x = rădăcini [0]; intersec_points [0] .y = n2.a * rădăcini [0] + n2.b intersec_points [1] .x = rădăcini [1]; intersec_points [1] .y = n2.a * rădăcini [1] + n2.b altfel intersec_points [0] .x = -100; intersec_points [0] .y = -100; intersec_points [1] .x = -100; intersec_points [1] .y = -100; 

Pasul 6: Rădăcinile cubice

Rădăcinile rădăcinilor cubice, nu este surprinzător, sunt punctele de intersecție între a cub curba și o linie. Dar o curbă cubică este puțin diferită de o curbă patratică și, în acest sens, posibilitățile pentru unde pot fi localizate intersecțiile sunt diferite.

Imaginea de mai jos prezintă o curbă cubică care se intersectează cu axa x:

Din nou, iată o prezentare Flash cu care să experimentați. Punctele roșii și albastre pot fi trase, în timp ce cele galbene indică doar punctele de intersecție.


Pasul 7: Formula generală pentru rădăcinile cubice

Formula generală de a găsi o curbă cubică a fost descoperită de Cardano. Deși sunt tentat să detaliem detaliile, voi îndrepta cititorii interesați către următoarele linkuri:

  • Wikipedia
  • Wolfram și
  • Un alt fișier PDF util.

Oricum, EqCubic.as clasa implementează această formulă pentru a rezolva rădăcinile funcțiilor cubice împreună cu alte funcții matematice de utilitate. În general, toate atributele și metodele pentru EqCubic.as urmați descifrarea prezentată în pasul 4, deoarece ambele clase EqQuadratic.as și EqCubic.as să implementeze o interfață comună, IEquation.as, cu excepția detaliilor enumerate mai jos.

Funcţie Diferență
defini Un total de patru coeficienți (a, b, c, d) de intrare pentru ecuația cubică; doar trei pentru ecuația patratică.
roots_R, root_i Totalul rădăcinilor reale și imaginare este de trei pentru o ecuație cubică, dar două pentru o ecuație patratică.

Pasul 8: Plotarea cu ActionScript

Iată implementarea Actionscript pentru prezentarea Flash de la Pasul 5. Codul complet este în Demo3.as.

 funcția privată redraw (): void var cmd: Vector. = Vector nou.; var coord: Vector. = Vector nou.; // curba redraw; m1 = noua matrice3d (curve_points [0] .x * curve_points [0] .x * curve_points [0] .x, curve_points [0] .x * curve_points [0] .x, curve_points [0] .x, 1, curve_points [1] .x * curve_points [1] .x * curve_points [1] .x, curve_points [1] .x * curve_points [1] .x, curve_points [1] .x, 1, date curve [2] .x * curve_points [2] .x, curve_points [2] .x * curve_points [2] .x, curve_points [2] .x, 1, curve_points [3] .x * curve_points [3] * curve_points [3] .x, curve_points [3] .x * curve_points [3] .x, curve_points [3] .x, 1); m2 = noua Matrix3d ​​(curve_points [0] .y, 0, 0, 0, curve_points [1] .y, 0, 0, 0, curve_points [2] .y, 0, 0, 0, curve_points [3]. , 0, 0, 0) m1.invert (); m2.append (m1); cubic_equation.define (m2.n11, m2.n21, m2.n31, m2.n41); pentru (var i: int = 0; i < stage.stageWidth; i+=2)  if (i == 0) cmd.push(1); else cmd.push(2); coord.push(i, cubic_equation.fx_of(i));  //draw line n1 = new Matrix(); n1.a = line_points[0].x; n1.c = 1; n1.b = line_points[1].x; n1.d = 1; n2 = new Matrix(); n2.a = line_points[0].y; n2.c = 0; n2.b = line_points[1].y; n2.d = 0; n1.invert(); n2.concat(n1); var x:Number = stage.stageWidth //y = mx + c cmd.push(1); coord.push(0, n2.a * 0 + n2.b); cmd.push(2); coord.push(x, n2.a * x + n2.b); graphics.clear(); graphics.lineStyle(1); graphics.drawPath(cmd, coord); 

Din nou, comenzile ActionScript pentru a desena o curbă cubică sunt exact aceleași cu cele explicate în articolul meu anterior, în timp ce comenzile Actionscript pentru a desena linia sunt explicate deja în Pasul 3 al acestei.

Acum, să trecem la calcularea și poziționarea rădăcinilor cubice:

 funcția privată recalculate_reposition (): void cubic_equation.define (m2.n11, m2.n21, m2.n31 - n2.a, m2.n41 - n2.b); cubic_equation.calcRoots (); Var rădăcini: Vector. = cubic_equation.roots_R; pentru (var i: int = 0; i < roots.length; i++)  if (!isNaN(roots[i]))  intersec_points[i].x = roots[i]; intersec_points[i].y = n2.a * roots[i] + n2.b  else  intersec_points[i].x = -100; intersec_points[i].y = -100;   

După instanțiere cubic_equation în constructor vom continua să definim coeficienții, să calculam rădăcinile și să stocăm rădăcinile într-o variabilă.

O mică notă despre rădăcini: există maximum 3 rădăcini reale pentru o ecuație cubică, dar nu toate rădăcinile reale sunt prezente în toate situațiile, deoarece unele rădăcini pot fi imaginare. Deci, ce se întâmplă atunci când există o singură rădăcină reală, de exemplu? Ei bine, unul dintre mulțimea de rădăcini a sunat de la cubic_equation.roots_R va fi un număr real, în timp ce toate celelalte nu vor fi un număr (NaN). Consultați acțiunea ActionScript evidențiată pentru aceasta.


Pasul 9: Predicarea în cazul în care un obiect se va ciocni cu suprafața curbată

O aplicație excelentă a rădăcinilor de calcul este proiectarea unui punct de coliziune pe suprafața curbată, după cum se arată mai jos. Utilizați tastele săgeată stânga și dreapta pentru a direcționa nava în mișcare și apăsați în sus pentru a accelera. Veți observa că punctele de coliziune care s-ar fi întâmplat în trecut sunt ușor estompate.


Pasul 10: Implementarea

Ideea este similară cu cea din tutorialul meu despre prezicerea punctelor de coliziune. Cu toate acestea, în loc de a intra în coliziune cu o linie dreaptă, folosim acum a curb linia. Să verificăm codul.

Fragmentul de mai jos se numește fiecare cadru:

 actualizarea funcției private (e: Event): void // Direcție stânga și dreapta dacă (control == 1) velo = velo.rotate (Math2.radianOf (-5)); altfel dacă (control == 2) velo = velo.rotate (Math2.radianOf (5)); // manipularea vitezei var currVelo: Number = velo.getMagnitude (); dacă (crește == 0) currVelo - = 0.5; currVelo = Math.max (currVelo, 1); // limită inferioară pentru viteza altfel dacă (crește == 1) currVelo + = 0.5; currVelo = Math.min (currVelo, 5); // limita superioară pentru viteza velo.setMagnitude (currVelo); // viteza de actualizare ship.x + = velo.x; ship.y + = velo.y; ship.rotation = Math2.degreeOf (velo.getAngle ()); // reflectă când nava este în afara stadiului dacă (nava.x <0 || ship.x > stage.stageWidth) velo.x * = -1; dacă (ship.y <0 || ship.y > stage.stageHeight) velo.y * = -1; redraw (); recalcula (); 

Codul de bază se află în redesenare și recalcula. Să vedem mai întâi ce este redesenare. Este același lucru pe care îl folosisem în demosurile anterioare. O notă mică despre desenarea liniei. Am văzut în demonstrațiile anterioare că sunt necesare două puncte pentru a atrage ecuația. Ei bine, aici avem doar o navă. Deci, pentru a obține al doilea punct, trebuie doar să adăugați viteza navei în poziția actuală. Am subliniat codul pentru comoditate.

 funcția privată redraw (): void var cmd: Vector. = Vector nou.; var coord: Vector. = Vector nou.; // curba redraw; m1 = noul Matrix3d ​​(w1.x * w1.x, w1.x, 1, 0, w2.x * w2.x, w2.x, 1, 0, w3.x * w3.x, w3.x, 1 , 0,0,0,1); m2 = noua Matrix3d ​​(w1.y, 0, 0, 0, w2.y, 0, 0, 0, w3.y, 0, 0, 0, 0,0,0,1) m1.invert (); m2.append (m1); quadratic_equation.define (m2.n11, m2.n21, m2.n31); minX = Math.min (w1.x, w2.x, w3.x); maxX = Math.max (w1.x, w2.x, w3.x); pentru (var i: int = minX; i < maxX; i+=2)  if (i == minX) cmd.push(1); else cmd.push(2); coord.push(i, quadratic_equation.fx_of(i));  n1 = new Matrix(); n1.a = ship.x; n1.c = 1; n1.b = ship.x + velo.x; n1.d = 1; n2 = new Matrix(); n2.a = ship.y; n2.c = 0; n2.b = ship.y + velo.y; n2.d = 0; n1.invert(); n2.concat(n1); var x:Number = stage.stageWidth //y = mx + c cmd.push(1); coord.push(0, n2.a * 0 + n2.b); cmd.push(2); coord.push(x, n2.a * x + n2.b); graphics.clear(); graphics.lineStyle(1); graphics.drawPath(cmd, coord); 

Acum pentru recalcula, Am făcut un mic vector de calcul pentru a verifica dacă punctul este în spatele sau în fața navei. Verificați codul evidențiat:

 funcția privată recalculate (): void quadratic_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); Var rădăcini: Vector. = quadratic_equation.roots_R; pentru (var i: int = 0; i < roots.length; i++)  var reposition:Sprite = getChildByName("c" + i) as Sprite //conditions: //real root, value of x within the range if (!isNaN(roots[i]) && roots[i] > minX && rădăcini [i] < maxX)  reposition.x = roots[i]; reposition.y = n2.a * roots[i] + n2.b; //discriminating between future and already happened collision point var vec:Vector2D = new Vector2D(reposition.x - ship.x, reposition.y - ship.y); if (velo.dotProduct(vec) < 0) reposition.alpha = 0.4; else reposition.alpha = 1  else  reposition.x = -100; reposition.y = -100;   

Pasul 11: Detecția coliziunilor în funcție de timp

O altă aplicație excelentă nu este la fel de evidentă ca prima. Pentru a efectua o detecție mai accentuată a coliziunii, în loc să ne bazăm concluzia asupra distanței dintre două obiecte, le vom folosi timpul de impact. De ce? Deoarece "tunelul" se poate întâmpla dacă folosim detecția coliziunilor pe distanțe:

Luați în considerare un algoritm de detecție a coliziunii bazat pe distanța pentru două cercuri. Dintre cele patru cadre afișate, numai cadrul 2.15 a detectat cu succes o coliziune între două cercuri. De ce? Deoarece distanța curentă dintre centrele de cer și roșu este mai mică decât suma celor două raze ale cercurilor.

(Cititorii interesați de mai multe detalii pe această temă se pot referi la acest articol.)

\ [distanță \ între cercuri \

Problema este cauzată de modul în care Flash rulează cu un singur cadru discret la un moment dat, ceea ce înseamnă că numai cadrele 1, 2 și 3 vor fi capturate cu succes, și nu momentele dintre aceste instantanee de timp. Acum, cercurile roșii și roșii nu s-au ciocnit în aceste cadre, în funcție de calculul bazat pe distanțe, astfel încât tunelurile din cercul roșu să treacă prin gri!

Pentru a rezolva acest lucru, avem nevoie de o modalitate de a vedea coliziunea care a avut loc între cadrele 2 și 3. Trebuie să calculam timpul de impact între două cercuri. De exemplu, odată ce am verificat dacă timpul de impact este mai mic de 1 cadru la cadrul 2, aceasta înseamnă că, odată ce Flash-ul avansează cu 1 cadru înainte, sau chiar tunelul va fi avut loc cu siguranță.

\ [dacă \ time \ to \ impact, \ t, \ îndeplinește \ 0 \

Întrebarea este, cum calculam acest timp?


Pasul 12: Precalculări

Voi încerca să-mi arate metoda cât mai simplu posibil.

Având în vedere scenariul de mai sus, cele două cercuri roșii și roșii sunt situate în prezent la \ ((x_ gray, \ y_ gray) \) și \ ((x_ roșu, \ y_ roșu)). Se deplasează la \ (v_ gray \) și \ (v_ red \), respectiv se fixează pe o cale de coliziune. Suntem interesați să calculam timpul necesar, \ (t \), pentru ca ei să ajungă la poziții \ ((x '_ gri, \ y' _ gri) și \ ((x ' , \ y '_ roșu)), indicată de cercurile gri și roșii translucide, unde a avut loc coliziunea.

\ [
displacement_ future = displacement_ present + viteza * timp \\
x '_ gray = x_ gri + v_ gri_x * t \ ... (eq. \ 1) \\
y '_ gri = y_ gri + v_ gri_y * t \ ... (eq. \ 2) \\
x '_ red = x_ roșu + v_ red_x * t \ ... (eq. \ 3) \\
y '(roșu) = y_ roșu + v_ red_y * t \ ... (eq. \ 4)
\]

Rețineți că am derivat componentele orizontale și verticale ale \ (v_ gray \) în \ (v_ gray_x \) și \ (v_ gray_y \). Același lucru se întâmplă și cu viteza cercului roșu; consultați acest sfat rapid pentru a afla cum sunt derivate aceste componente.

Încă nu avem o relație care să lege toate aceste ecuații împreună. Să vedem imaginea de mai jos.

Cealaltă relație se întoarce la Pythagoras. Când ambele cercuri se întâlnesc, distanța dintre ambele centre este exact \ (rad_ gri \) plus \ (rad_ roșu).

\ [
Pythagoras '\ Teoremă, \ z ^ 2 = x ^ 2 + y ^ 2 \\
(Rad_ gri + rad_ red) ^ 2 = (x '_ gri -x' _ red) ^ 2+ (y '_ gri -y' _ red) ^ 2 \ ... (eq. \ 5) \\
\]

Aici înlocuiți ecuațiile 1 ~ 4 în ecuația 5. Înțeleg destul de descurajant matematic, așa că l-am separat în pasul 13. Simțiți-vă liber să o săriți pentru a ajunge la rezultatul de la pasul 14.


Pasul 13 (opțional): Rigor matematic

În primul rând, stabilim următoarele identități.

\ [
Identitate,\\
(a + b) ^ 2 = a ^ 2 + 2ab + b ^ 2 \ ... (id. \ 1) \\
(a-b) ^ 2 = a ^ 2-2ab + b ^ 2 \ ... (id. \ 2) \\
\]

În orice moment, rețineți că toate simbolurile matematice reprezintă o constantă, cu excepția timpului, \ (t), care este subiectul interesului.

\ (grila), \ v_ gray_x, \ y_ roșu, etc.) și toate celelalte sunt definite în scenariu.

În continuare, vom încerca să ne depășim termenul după termen:

\ [
(Rad_ gri + rad_ red) ^ 2 = (x '_ gri -x' _ red) ^ 2+ (y '_ gri -y' _ red) ^ 2 \ \
Luați în considerare \ term \ (x '_ gray -x' _ roșu) ^ 2 \ și \ folosind \ id. \ 2 \\
(x '_ x x x x _ red) ^ 2 \\
\]
\ [
Luați în considerare \ term \ (x '_ gray) ^ 2 \\
(X '_ gri) ^ 2 \\
= (x_ gri + v_ gray_x * t) ^ 2, \ utilizează \ id. \ 1 \\
= (X_ gri) ^ 2 + 2 (X_ gri) (v_ gray_x * t) + (v_ gray_x * t) ^ 2
\]
\ [
Luați în considerare \ term \ -2 (x '_ gray) (x' _ roșu) \\
-2 (x '_ gri) (x' _ red) \\
= -2 (X_ gri + v_ gray_x * t) (X_ red + v_ red_x * t) \\
= -2 [(X_ gri) (X_ red) + (X_ gri) (v_ red_x * t) + (v_ gray_x * t) (X_ red) + (v_ gray_x * t) (v_ red_x * t)] \\
= -2 (X_ gri) (X_ red) - 2 (X_ gri) (v_ red_x * t) -2 (v_ gray_x * t) (X_ red) - 2 ( v_ gray_x * t) (v_ red_x * t)
\]
\ [
Luați în considerare \ term \ (x '_ red ^ 2 \\
(X '_ red) ^ 2 \\
= (x_ roșu + v_ red_x * t) ^ 2, \ utilizează \ id. \ 1 \\
= (X_ red) ^ 2 + 2 (X_ red) (v_ red_x * t) + (v_ red_x * t) ^ 2
\]

Acum, dintr-o privire, putem vedea cu ușurință că puterea cea mai mare a lui \ (t) este 2. Deci avem o ecuație patratică. Să strângem toți coeficienții contribuția acestor trei termeni în funcție de puterile lor.

\ (T ^ 2 \) \ (T \) \ (T ^ 0 = 1 \)
\ ((V_ gray_x) ^ 2 \) \ (2 (X_ gri) (v_ gray_x) \) \ ((X_ gri) ^ 2 \)
\ (- 2 (v_ gray_x) (v_ red_x) \) \ (- 2 (X_ gri) (v_ red_x) - 2 (v_ gray_x) (X_ red) \) \ (- 2 (X_ gri) (X_ red) \)
\ ((V_ red_x) ^ 2 \) \ (2 (X_ red) (v_ red_x) \) \ ((X_ red) ^ 2 \)

Să analizăm coeficienții cu \ (t ^ 2) și \ (t ^ 0 \).

\ [
(v_ gray_x) ^ 2-2 (v_ gray_x) (v_ red_x) + (v_ red_x) ^ 2, \ recall \
(v_ gray_x) ^ 2-2 (v_ gray_x) (v_ red_x) + (v_ red_x) ^
\]
\ [
(x_ gray) ^ 2-2 (x_ gray) (x_ roșu) + (x_ roșu) ^ 2, \ recall \
(x_ red) + (x_ roșu) ^ 2 = (x_ gri -x_ roșu) ^ 2
\]

Și cea a lui \ (t \.

\ [
Simplifica\\
a = (x_ gri), \ b = (v_ gri_x) \\
c = (v_ red_x), \ d = (x_ roșu) \\
2ab-2AC-2bd + 2dc \\
= 2 [ab-ac-bd + dc] \\
= 2 [a (b-c) -d (b-c)] \\
= 2 [(b-c) (a-d)] \\
Resubstitute \\
2 [(b-c) (a-d)] = 2 (v_ gri_x -v_ red_x) (x_ gri -x_ roșu)
\]

Să rezumăm în termen de \ ((x '_ gri -x' _ roșu) ^ 2 \)

\ [
(X '_ gri -x' _ red) ^ 2 \\
= v_ gray_x -v_ red_x) ^ 2 * t ^ 2 + 2 (v_ gri_x -v_ red_x) (x_ -x_ red) ^ 2
\]

Rețineți că aceasta acoperă numai un termen în \ (eq. \ 5 \). Va trebui să efectuăm același proces pentru un alt termen \ ((y '_ gray -y' _ red) ^ 2 \). Deoarece au aceeași formă algebrică, rezultatul trebuie să fie același.
\ [
(Y '_ gri -y' _ red) ^ 2 \\
= (v_ gray_y -v_ red_y) ^ 2 * t ^ 2 + 2 (v_ gri_y -v_ red_y -y_ red) ^ 2
\]

Astfel, după rearanjare în termeni de \ (t \), \ (eq. \ 5 \) ar trebui să fie după cum urmează.

\ [
(Rad_ gri + rad_ red) ^ 2 = (x '_ gri -x' _ red) ^ 2+ (y '_ gri -y' _ red) ^ 2 \ \
p = v_ gray_x -v_ red_x \\
q = X_ gri -x_ red \\
r = v_ gray_y -v_ red_y \\
s = y_ gri -y_ red \\
(p + 2 + r ^ 2) * t ^ 2 + 2 (pq + rs) * t + (q ^ 2 + s ^
\]


Pasul 14: Rezultatul

Deci, din pasul anterior, prin algebra riguroasă am ajuns la următoarea formulă:

\ [
p = v_ gray_x -v_ red_x \\
q = X_ gri -x_ red \\
r = v_ gray_y -v_ red_y \\
s = y_ gri -y_ red \\
(p + 2 + r ^ 2) * t ^ 2 + 2 (pq + rs) * t + (q ^ 2 + s ^
\]

Acum, aceasta este o formulă uriașă. Vom încerca să grupăm coeficienții în ceea ce acceptăm EqQuadratic. Comparați cele două forme:

\ [
ax ^ 2 + bx + c = 0 \\
(p + 2 + r ^ 2) * t ^ 2 + 2 (pq + rs) * t + (q ^ 2 + s ^
a = p ^ 2 + r ^ 2) \\
b = 2 (pq + rs) \\
c = (q ^ 2 + s ^ 2- (rad_ gri + rad_ roșu) ^ 2)
\]


Pasul 15: Implementarea eșantionului

Deci aici este o prezentare Flash pentru a demonstra ideea. Veți vedea două particule pe scenă, una gri și cealaltă roșie. Ambele sunt conectate la o săgeată, indicând amploarea și direcția vitezei.

  • Faceți clic pe butonul "Următorul" pentru a progresa un cadru în timp.
  • Faceți clic pe butonul "Înapoi" pentru a reveni la un cadru în timp.

Pentru a modifica viteza particulelor, apăsați:

  • "Sus" și "în jos" pentru a mări și a micșora mărimea vitezei, respectiv.
  • "Stânga" și "dreapta" pentru a roti viteza.
  • "N" pentru a comuta cercul pe care îl controlezi.

În cele din urmă, pentru a comuta vizibilitatea săgeților, apăsați "V"


Pasul 16: O notă privind rădăcinile patrate

Există două rădăcini la ecuația patratică. În acest context, suntem interesați de rădăcinile reale. Cu toate acestea, dacă cele două particule nu sunt setate pe calea de coliziune (ambele căi sunt paralele una cu cealaltă), atunci rădăcinile imaginare vor fi produse în loc de rădăcini reale. În acest caz, ambele rădăcini reale vor rămâne NaN.

Dacă ambele particule sunt setate pe o cale de coliziune, vom obține două rădăcini reale. Dar ce reprezintă aceste două rădăcini?

Reamintim la pasul 12 că am făcut uz de teorema lui Pythagoras pentru a lega \ ((x '_ gri, \ y' _ gray) \) și \ (x red ) împreună într-o ecuație. Există două situații în care distanța dintre centrele celor două cercuri este exact suma celor două raze: una înainte de ciocnire și una după coliziune. Aruncati o privire la aceasta imagine:

Deci, care dintre noi alegem? Evident, primul pentru că nu suntem interesați de instanță după coliziune. Deci, ar trebui să alegem întotdeauna valoarea mai mică a celor două rădăcini și să o evaluăm. Dacă valoarea este pozitivă și mai mică de 1, o coliziune se va întâmpla în timpul următorului cadru. Dacă valoarea este negativă, coliziunea sa produs în trecut.


Pasul 17: Explicarea ActionScript

Să examinăm ActionScript implementat pentru acest exemplu. În primul rând, variabilele.

 // c1 este cercul gri // c2 este cercul roșu privat var c1: Circle, c2: Circle; // v1 este viteza cercului gri // v2 este viteza cercului rosu privat var v1: Vector2D, v2: Vector2D, comuta: Boolean = true, folosindV1: Boolean = true; // tri1 va forma capul săgeții v1 // tri2 va forma capul săgeții v2 privat var tri1: triunghi, tri2: triunghi; container privat var: Sprite; private var eq: EqQuadratic;

Apoi calcularea rădăcinilor. Poate doriți să verificați următorul ActionScript cu variabilele de mai sus.

 var p: Număr = v1.x - v2.x; var q: număr = c1.x - c2.x; var r: Numărul = v1.y - v2.y; var s: Numărul = c1.y - c2.y; var a: Numărul = p * p + r * r; var b: Numărul = 2 * (p * q + r * s); var c: număr = q * q + s * s - (c.radius + c2.radius) * (c.radius + c2.radius); eq.defin (a, b, c); eq.calcRoots (); Var rădăcini: Vector. = eq.roots_R;

Iată cum ar trebui să interpretați rădăcinile:

 // dacă nu există rădăcini reale, atunci ele nu sunt pe cale de coliziune dacă (isNaN (rădăcini [0]) && isNaN (rădăcini [1])) t.text = "Particulele nu sunt pe calea de coliziune".  altfel var time: Number = Math.min (rădăcini [0], rădăcini [1]) var int_time: int = time * 1000; time = int_time / 1000; t.text = "Cadre de impact:" + time.toString () + "\ n"; dacă (timp> 1) t.appendText ("Particulele se închid ...") altfel dacă (timp> 0 && timp < 1) t.appendText("Particles WILL collide next frame!") else if (time < 0) t.appendText("Collision had already happened."); 

Concluzie

Deci, acum am studiat rădăcini cvadrice și cubice în ActionScript, precum și o privire atentă asupra a două exemple despre modul în care putem folosi rădăcinile patrate.

Vă mulțumim că ați avut timp să citiți acest tutorial. Lăsați un comentariu dacă vedeți alte aplicații ale rădăcinilor patrate (sau orice erori!)

Cod