Il Math e ActionScript di Curves Roots

Nel primo tutorial di questa serie, abbiamo dato un'occhiata alle curve di disegno usando equazioni e AS3. Ora, affronteremo la risoluzione di queste equazioni per trovare le radici di una curva, ovvero i punti in cui la curva attraversa una determinata linea retta. Possiamo usarlo per prevedere collisioni con superfici curve e per evitare il "tunneling" nei giochi Flash.


Step 1: Radici quadratiche

In primo luogo, è tempo per una rapida revisione matematica. In questo tutorial, accetteremo e applicheremo solo i metodi che useremo, ma i lettori interessati possono fare riferimento alla pagina di Wikipedia sulle equazioni quadratiche per informazioni sulle derivate matematiche.

Quindi \ (f (x) \) è una funzione quadratica. Se \ (f (x) \) è equivalente a 0, \ (x \) può essere ottenuto da questa formula:

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

\ (b ^ 2 - 4ac \) è chiamato il discriminante della formula. Se il discriminante è negativo, produrrà la radice quadrata del discriminante radici immaginarie, che non possiamo tracciare. Al contrario, se la discriminat è positiva, avrai radici di numeri reali e sarai in grado di tracciarle sullo schermo.


Passaggio 2: visualizzazione delle radici quadratiche

Quindi quali sono le radici? Bene nel nostro contesto non sono altro che punti di intersezione tra la curva quadratica e una linea. Ad esempio, supponiamo di essere interessati a trovare i punti di intersezione del seguente insieme di equazioni:

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

Questo è uno scenario tipico di ricerca dei punti di intersezione tra una curva quadratica e l'asse x (perché l'asse x è la linea in cui y == 0). Poiché per definizione i punti di intersezione sono condivisi da \ (f (x) \) e \ (g (x) \), possiamo concludere che \ (f (x) = g (x) \) per i valori di X che stiamo cercando.

È quindi un'operazione banale in cui si sostituiscono semplicemente le funzioni e quindi si applica la formula dal passaggio 1 per ottenere le radici. Ora ci sono diverse possibilità che possiamo anticipare come mostrato di seguito.

(Come puoi vedere, "radici immaginarie" significa, per i nostri scopi, che la curva non attraversa mai l'asse x).

Consideriamo ora il caso in cui \ (g (x) \) è più di una semplice linea orizzontale. Diciamo che è una linea obliqua, \ (g (x) \ = \ mx \ + \ d \). Ora, quando identifichiamo entrambe le funzioni, dovremo fare un piccolo calcolo preliminare prima che la formula possa essere applicata efficacemente.

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

Ho incluso una presentazione Flash interattiva qui sotto, quindi sentiti libero di trascinare i punti rosso e blu. I punti gialli indicano i punti di intersezione. Potrebbe essere necessario posizionare la curva e la linea per intersecarsi tra loro in modo che appaiano i punti gialli.


Passaggio 3: tracciare questo con ActionScript

Il copione completo può essere trovato in Demo1.as; qui spiegherò semplicemente un estratto cruciale del codice. Diamo un'occhiata all'AS3 per disegnare la curva e la linea:

 funzione privata ridisegna (): void var cmd: Vector. = nuovo vettore.; Var Coord: Vector. = nuovo vettore.; // ridisegna la curva; m1 = new Matrix3d ​​(curve_points [0] .x * curve_points [0] .x, curve_points [0] .x, 1, 0, curve_points [1] .x * curve_points [1] .x, curve_points [1] .x , 1, 0, curve_points [2] .x * curve_points [2] .x, curve_points [2] .x, 1, 0, 0,0,0,1); m2 = new Matrix3d ​​(curve_points [0] .y, 0, 0, 0, curve_points [1] .y, 0, 0, 0, curve_points [2] .y, 0, 0, 0, 0,0,0, 1) m1.invert (); m2.append (m1); quadratic_equation.define (m2.n11, m2.n21, m2.n31); per (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); 

Il grosso di ActionScript per disegnare la curva dalla riga 80 ~ 104 è ampiamente preso in prestito dal precedente tutorial, quindi spiegherò un po 'il codice per disegnare una linea.

Nella presentazione Flash sopra, ci sono due punti blu interattivi. Ognuno di questi ha coordinate e con entrambi i punti si forma una linea. Poiché entrambi i punti si trovano sulla stessa riga, condividono una pendenza comune e l'intercetta y per formare un'equazione di linea generale:

\ [
y \ = \ mx \ + \ d, \\
m \ = \ slope, \ d \ = \ y-intercetta
\]

Possiamo usare un po 'di algebra per risolvere le due incognite, \ (m \) e \ (d \). Dati le coordinate dei due punti blu come \ ((x_1, \ y_1) \) e \ ((x_2, \ y_2) \):

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

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

[Lattice]
\ 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 \\
[/ Lattice]

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

(Si noti che una matrice con apice -1 si riferisce all'inverso di quella matrice).

Quindi, usando questo, vengono calcolati \ (m \) e \ (d \). Ora possiamo disegnare la linea unendo le coordinate \ ((0, y_3) \) e \ ((stage.stageWidth, y_4) \). Come trovi \ (y_3 \) e \ (y_4 \)? Bene, ora che \ (m \), \ (x \) e \ (d \) sono noti, possiamo semplicemente mettere tutti questi valori nella riga equazione generale,

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

... per ottenere quei \ (y \) s.


Passaggio 4: Calcola le radici quadratiche

Per calcolare la posizione dei punti di intersezione, useremo la formula del punto 1. Ciò è fatto in EqQuadratic.as come le funzioni mostrate di seguito:

 / ** Sola lettura * Discriminante dell'equazione * / funzione pubblica get discriminant (): Number // B * B-4 * A * C return _B * _B - 4 * _A * _C;  / ** * Esegue il calcolo per ottenere root * / public function calcRoots (): void var disc: Number = this.discriminant // gestisce le radici immaginarie if (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);  

Ulteriori dettagli di EqQuadratic.as:

Funzione genere Parametro di input Funzionalità
EqQuadratic Metodo zero Costruttore di classe
definire Metodo Coefficienti a, bec dell'equazione quadratica Istanziare i valori dei coefficienti
fx_of Metodo Valore di x Restituisce \ (f (x) \) del dato input \ (x \).
calcRoots Metodo zero Esegue il calcolo per ottenere la radice quadratica
DIFF1 Metodo \ (x \) coordinate per la differenziazione di primo grado Differenziato \ (f (x) \) di dato \ (x \) al primo grado.
diff2 Metodo \ (x \) coordinate per la differenziazione di secondo grado Differenziato \ (f (x) \) di dato \ (x \) al secondo grado.
discriminat Proprietà, sola lettura zero Restituisce il valore di discriminante, \ (b ^ 2 - 4ac \)
roots_R Proprietà, sola lettura zero Restituisce un vettore numerico per le radici del numero reale. Gli elementi sono NaN se non esistono radici reali.
roots_i Proprietà, sola lettura zero Restituisce un vettore String per le radici del numero immaginario. Gli elementi sono nulli se non esistono radici immaginarie.

Passaggio 5: tracciare questo con ActionScript

Un esempio di utilizzo di questo EqQuadratic.as è in Demo1.as. Dopo l'inizio di EqQuadratic, lo useremo per calcolare le radici. Quindi, dopo aver convalidato la presenza di radici reali, li useremo per tracciare i punti gialli.

Ora le radici si riferiscono solo al componente \ (x \) delle coordinate. Per ottenere il \ (y \) s, indovina cosa? Ancora una volta, inseriamo i valori di \ (m \), \ (d \) (calcolati in precedenza nel Passaggio 3) e \ (x \) (dalle radici) nell'equazione generale della linea, \ (y \ = \ mx \ + \ d \). Controlla il codice corrispondente nella riga 135 e 136.

 funzione privata recalculate_reposition (): void quadratic_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); var roots: Vector. = quadratic_equation.roots_R; if (! isNaN (roots [0]) &&! isNaN (roots [1])) intersec_points [0] .x = roots [0]; intersec_points [0] .y = n2.a * roots [0] + n2.b intersec_points [1] .x = roots [1]; intersec_points [1] .y = n2.a * roots [1] + n2.b else intersec_points [0] .x = -100; intersec_points [0] .y = -100; intersec_points [1] .x = -100; intersec_points [1] .y = -100; 

Passaggio 6: radici cubiche

Le radici cubiche, non a caso, sono i punti di intersezione tra a cubo curva e una linea. Ma una curva cubica è un po 'diversa da una curva quadratica, e in questo senso le possibilità di individuare le intersezioni sono diverse.

L'immagine sotto mostra una curva cubica che si interseca con l'asse x:

Ancora una volta, ecco una piccola presentazione Flash con cui sperimentare. I punti rossi e blu possono essere trascinati mentre quelli gialli indicano solo i punti di intersezione.


Passaggio 7: Formula generale per le radici cubiche

Cardano ha scoperto la formula generale per trovare una curva cubica. Anche se sono indotto ad approfondire i dettagli, indicherò i lettori interessati ai seguenti link:

  • Wikipedia
  • Wolfram e
  • Un altro file PDF utile.

Comunque, il EqCubic.as class implementa questa formula per risolvere le radici delle funzioni cubiche insieme ad altre funzioni di utilità matematica. Generalmente tutti gli attributi e i metodi per EqCubic.as segui la descrizione come presentata al punto 4, perché entrambe le classi EqQuadratic.as e EqCubic.as implementare un'interfaccia comune, IEquation.as, ad eccezione dei dettagli elencati di seguito.

Funzione Differenza
definire Un totale di quattro coefficienti (a, b, c, d) da introdurre per l'equazione cubica; solo tre per equazione quadratica.
roots_R, root_i Il totale delle radici reali e immaginarie è tre per un'equazione cubica, ma due per un'equazione quadratica.

Passaggio 8: tracciare questo con ActionScript

Ecco l'implementazione Actionscript per la presentazione Flash dal passaggio 5. Il codice completo è in Demo3.as.

 funzione privata ridisegna (): void var cmd: Vector. = nuovo vettore.; Var Coord: Vector. = nuovo vettore.; // ridisegna la curva; m1 = new Matrix3d ​​(curve_points [0] .x * curve_points [0] .x * curve_points [0] .x, curve_points [0] .x * curve_points [0] .x, punti_cri [0] .x, 1, punti_cri curva [1] .x * curve_points [1] .x * curve_points [1] .x, curve_points [1] .x * curve_points [1] .x, curve_points [1] .x, 1, curve_points [2] .x * curve_points [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] .x * curve_points [3] .x, curve_points [3] .x * curve_points [3] .x, curve_points [3] .x, 1); m2 = new 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] .y , 0, 0, 0) m1.invert (); m2.append (m1); cubic_equation.define (m2.n11, m2.n21, m2.n31, m2.n41); per (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); 

Ancora una volta, i comandi ActionScript per disegnare una curva cubica sono esattamente gli stessi spiegati nel mio articolo precedente, mentre i comandi Actionscript per disegnare la linea sono già spiegati nel Passaggio 3 di questo.

Passiamo ora al calcolo e al posizionamento delle radici cubiche:

 funzione privata recalculate_reposition (): void cubic_equation.define (m2.n11, m2.n21, m2.n31 - n2.a, m2.n41 - n2.b); cubic_equation.calcRoots (); var roots: Vector. = cubic_equation.roots_R; per (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;   

Dopo l'istanziazione cubic_equation nel costruttore procediamo a definire i suoi coefficienti, calcolare le radici e memorizzare le radici in una variabile.

Una piccola nota sulle radici: ci sono un massimo di tre radici reali per un'equazione cubica, ma non tutte le radici reali sono presenti in tutte le situazioni poiché alcune radici possono essere immaginarie. Quindi cosa succede quando c'è solo una vera radice, per esempio? Bene, una delle serie di radici chiamate da cubic_equation.roots_R sarà un numero reale, mentre tutti gli altri saranno Not a Number (NaN). Controlla l'ActionScript evidenziato per questo.


Passo 9: Prevedere dove un oggetto si scontrerà con una superficie curva

Una grande applicazione del calcolo delle radici sta proiettando un punto di collisione sulla superficie curva, come mostrato di seguito. Usa i tasti freccia sinistra e destra per guidare la nave in movimento e premi verso l'alto per accelerare. Noterai che i punti di collisione che sarebbero accaduti in passato sono leggermente attenuati.


Passaggio 10: implementazione

L'idea è simile a quella del mio tutorial sulla previsione dei punti di collisione. Tuttavia, invece di scontrarsi con una linea retta, ora usiamo un curvo linea. Controlliamo il codice.

Il frammento di seguito è chiamato ogni frame:

 aggiornamento della funzione privata (e: Event): void // Direzione sinistra e destra if (control == 1) velo = velo.rotate (Math2.radianOf (-5)); else if (control == 2) velo = velo.rotate (Math2.radianOf (5)); // manipulating velocity var currVelo: Number = velo.getMagnitude (); se (aumento == 0) currVelo - = 0.5; currVelo = Math.max (currVelo, 1); // lower bound for velocity else if (increase == 1) currVelo + = 0.5; currVelo = Math.min (currVelo, 5); // upper bound for velocity velo.setMagnitude (currVelo); // update velocity ship.x + = velo.x; ship.y + = velo.y; ship.rotation = Math2.degreeOf (velo.getAngle ()); // riflette quando la nave è fuori dal palco se (ship.x <0 || ship.x > stage.stageWidth) velo.x * = -1; se (ship.y <0 || ship.y > stage.stageHeight) velo.y * = -1; ridisegnare (); ricalcolare (); 

Il codice di base si trova in ridisegnare e ricalcolare. Vediamo innanzitutto cosa c'è dentro ridisegnare. È lo stesso che avevamo usato nelle demo precedenti. Una piccola nota per tracciare la linea. Abbiamo visto in precedenti demo che sono necessari due punti per disegnare ottenere l'equazione. Bene, qui abbiamo solo una nave. Quindi, per ottenere il secondo punto, aggiungi la velocità della nave alla sua posizione corrente. Ho evidenziato il codice per comodità.

 funzione privata ridisegna (): void var cmd: Vector. = nuovo vettore.; Var Coord: Vector. = nuovo vettore.; // ridisegna la curva; m1 = new 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,0,1); m2 = new 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); per (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); 

Ora per ricalcolare, Ho fatto un piccolo calcolo vettoriale per verificare se il punto è dietro o davanti alla nave. Controlla il codice evidenziato:

 funzione privata ricalcolare (): void quadratic_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); var roots: Vector. = quadratic_equation.roots_R; per (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 && roots [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;   

Passaggio 11: rilevamento collisioni temporali

Un'altra grande applicazione non è così ovvia come la prima. Per eseguire un rilevamento di collisione più accurato, invece di basare la nostra conclusione sulla distanza tra due oggetti, ne faremo il loro tempo di impatto. Perché? Perché "tunneling" può accadere se utilizziamo il rilevamento delle collisioni a distanza:

Prendi in considerazione un algoritmo di rilevamento delle collisioni basato sulla distanza per due cerchi. Dei quattro fotogrammi mostrati, solo il fotogramma 2.15 ha rilevato correttamente la collisione tra due cerchi. Perché? Poiché la distanza corrente tra i centri dei cerchi grigio e rosso è inferiore alla somma dei raggi di entrambi i cerchi.

(I lettori interessati a maggiori dettagli su questo argomento possono fare riferimento a questo articolo.)

\ [distanza \ tra \ cerchi \

Il problema è causato dal modo in cui Flash procede per un fotogramma discreto alla volta, il che significa che verranno acquisiti solo i fotogrammi 1, 2 e 3 e non i momenti tra le istantanee di tempo. Ora i cerchi grigi e rossi non si sono scontrati in questi fotogrammi secondo un calcolo basato sulla distanza, quindi il tunnel del cerchio rosso passa attraverso quello grigio!

Per risolvere questo problema, abbiamo bisogno di un modo per vedere la collisione che si è verificata fra fotogrammi 2 e 3. Abbiamo bisogno di calcolare il tempo di impatto tra due cerchi. Ad esempio, una volta che controlliamo che il tempo di impatto è inferiore a 1 fotogramma al fotogramma 2, ciò significa che una volta che Flash avrà portato a termine 1 collisione in avanti o addirittura il tunneling avrà avuto luogo.

\ [se \ time \ to \ impact, \ t, \ soddisfa \ 0 \

La domanda è, come calcoliamo questa volta?


Passaggio 12: calcoli preliminari

Proverò a mostrare il mio metodo nel modo più semplice possibile.

Dato lo scenario sopra, le due cerchie grigie e rosse si trovano attualmente in \ ((x_ grigio, \ y_ grigio) \) e \ ((x_ rosso, \ y_ rosso) \). Si stanno spostando rispettivamente su \ (v_ gray \) e \ (v_ red \) e si impostano su un percorso di collisione. Siamo interessati a calcolare il tempo impiegato, \ (t \), affinché raggiungano le posizioni \ ((x '_ grigio, \ y' _ grigio) \) e \ ((x '_ rosso , \ y '_ rosso) \), indicato dai cerchi grigi e rossi traslucidi, dove si è verificata la collisione.

\ [
displacement_ future = displacement_ present + velocity * time \\
x '_ gray = x_ gray + v_ gray_x * t \ ... (eq. \ 1) \\
y '_ gray = y_ gray + v_ gray_y * t \ ... (eq. \ 2) \\
x '_ rosso = x_ rosso + v_ rosso_x * t \ ... (eq. \ 3) \\
y '_ rosso = y_ rosso + v_ rosso_y * t \ ... (eq. \ 4)
\]

Nota che ho derivato le componenti orizzontale e verticale di \ (v_ gray \) in \ (v_ gray_x \) e \ (v_ gray_y \). Lo stesso vale per la velocità del cerchio rosso; controlla questo suggerimento rapido per sapere come vengono derivati ​​questi componenti.

Manca ancora una relazione per unire tutte queste equazioni insieme. Vediamo l'immagine qui sotto.

L'altra relazione risale a Pitagora. Quando entrambi i cerchi si incontrano, la distanza tra i due centri è esattamente \ (rad_ grigio \) più \ (rad_ rosso \).

\ [
\ Teorema di Pitagora, \ z ^ 2 = x ^ 2 + y ^ 2 \\
(Rad_ grigio + rad_ red) ^ 2 = (x '_ grigio -x' _ red) ^ 2 + (y '_ grigio -y' _ red) ^ 2 \ ... (eq. \ 5) \\
\]

Qui è dove si sostituiscono le equazioni 1 ~ 4 nell'equazione 5. Capisco che è piuttosto scoraggiante matematicamente, quindi l'ho separato nel passaggio 13. Sentiti libero di saltarlo per arrivare al risultato al punto 14.


Step 13 (Opzionale): Rigore matematico

Innanzitutto, stabiliamo le seguenti identità.

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

In ogni momento, tieni presente che tutti i simboli matematici rappresentano una costante ad eccezione del tempo, \ (t \), che è l'argomento di interesse.

\ (x_ gray, \ v_ gray_x, \ y_ red, \) e così via sono tutti definiti nello scenario.

Successivamente, proveremo a interrompere il nostro problema a lungo termine per termine:

\ [
(Rad_ grigio + rad_ red) ^ 2 = (x '_ grigio -x' _ red) ^ 2 + (y '_ grigio -y' _ red) ^ 2 \ \
Considera \ term \ (x '_ gray -x' _ rosso) ^ 2 \ e \ utilizza \ id. \ 2 \\
(x '_ grigio -x' _ rosso) ^ 2 = (x '_ grigio) ^ 2-2 (x' _ grigio) (x '_ rosso) + (x' _ red) ^ 2 \\
\]
\ [
Considera \ term \ (x '_ gray) ^ 2 \\
(X '_ grigio) ^ 2 \\
= (x_ gray + v_ gray_x * t) ^ 2, \ utilize \ id. \ 1 \\
= (X_ grigio) ^ 2 + 2 (x_ grigio) (V_ gray_x * t) + (V_ gray_x * t) ^ 2
\]
\ [
Considera \ term \ -2 (x '_ grigio) (x' _ rosso) \\
-2 (x '_ grigio) (x' _ red) \\
= -2 (x_ grigio + V_ gray_x * t) (x_ red + V_ red_x * t) \\
= -2 [(x_ grigio) (x_ red) + (x_ grigio) (V_ red_x * t) + (V_ gray_x * t) (x_ red) + (v_ gray_x * t) (V_ red_x * t)] \\
= -2 (x_ grigio) (x_ red) - 2 (x_ grigio) (V_ red_x * t) -2 (V_ gray_x * t) (x_ red) - 2 ( v_ gray_x * t) (V_ red_x * t)
\]
\ [
Considera \ term \ (x '_ rosso) ^ 2 \\
(X '_ red) ^ 2 \\
= (x_ rosso + v_ red_x * t) ^ 2, \ utilize \ id. \ 1 \\
= (X_ red) ^ 2 + 2 (x_ red) (V_ red_x * t) + (V_ red_x * t) ^ 2
\]

Ora, a colpo d'occhio, possiamo facilmente vedere che la massima potenza di \ (t \) è 2. Quindi abbiamo un'equazione quadratica. Raccogliamo tutti i coefficienti forniti da questi tre termini in base ai loro poteri.

\ (T ^ 2 \) \ (T \) \ (T ^ 0 = 1 \)
\ ((V_ gray_x) ^ 2 \) \ (2 (x_ grigio) (V_ gray_x) \) \ ((X_ grigio) ^ 2 \)
\ (- 2 (V_ gray_x) (V_ red_x) \) \ (- 2 (x_ grigio) (V_ red_x) - 2 (V_ gray_x) (x_ red) \) \ (- 2 (x_ grigio) (x_ red) \)
\ ((V_ red_x) ^ 2 \) \ (2 (x_ red) (V_ red_x) \) \ ((X_ red) ^ 2 \)

Analizziamo i coefficienti con \ (t ^ 2 \) e \ (t ^ 0 \).

\ [
(v_ gray_x) ^ 2-2 (v_ gray_x) (v_ red_x) + (v_ red_x) ^ 2, \ recall \ id. \ 2 \\
(v_ gray_x) ^ 2-2 (v_ gray_x) (v_ red_x) + (v_ red_x) ^ 2 = (v_ gray_x -v_ red_x) ^ 2
\]
\ [
(x_ grigio) ^ 2-2 (x_ grigio) (x_ rosso) + (x_ rosso) ^ 2, \ richiama \ id. \ 2 \\
(x_ grigio) ^ 2-2 (x_ grigio) (x_ rosso) + (x_ rosso) ^ 2 = (x_ grigio -x_ rosso) ^ 2
\]

E quello di \ (t \).

\ [
Semplificare\\
a = (x_ grigio), \ b = (v_ gray_x) \\
c = (v_ red_x), \ d = (x_ rosso) \\
2 bis ter-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_ gray_x -v_ red_x) (x_ grigio -x_ rosso)
\]

Riassumiamo in termini di \ ((x '_ grigio -x' _ rosso) ^ 2 \)

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

Si noti che questo fa riferimento solo a un termine in \ (eq. \ 5 \). Dovremo eseguire lo stesso processo per un altro termine \ ((y '_ grigio -y' _ rosso) ^ 2 \). Poiché hanno la stessa forma algebrica, anche il risultato dovrebbe essere lo stesso.
\ [
(Y '_ grigio -y' _ red) ^ 2 \\
= (v_ gray_y -v_ red_y) ^ 2 * t ^ 2 + 2 (v_ gray_y -v_ red_y) (y_ grigio -y_ rosso) * t + (y_ grigio -y_ red) ^ 2
\]

Quindi dopo il riarrangiamento in termini di \ (t \), \ (eq. \ 5 \) dovrebbe essere come segue.

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


Passaggio 14: il risultato

Quindi, dal passaggio precedente, attraverso un'algebra rigogliosa siamo arrivati ​​alla seguente formula:

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

Questa è una grande formula quadratica. Cercheremo di raggruppare i coefficienti in quello accettato da EqQuadratic. Confronta le due forme:

\ [
ax ^ 2 + bx + c = 0 \\
(p ^ 2 + r ^ 2) * t ^ 2 + 2 (pq + rs) * t + (q ^ 2 + s ^ 2- (rad_ grigio + rad_ rosso) ^ 2) = 0 \\
a = p ^ 2 + r ^ 2) \\
b = 2 (pq + rs) \\
c = (q ^ 2 + s ^ 2- (rad_ gray + rad_ rosso) ^ 2)
\]


Passaggio 15: implementazione del campione

Quindi, ecco una presentazione Flash per dimostrare l'idea. Vedrai due particelle sul palco, una grigia e l'altra rossa. Entrambi sono collegati a una freccia, che indica la grandezza e la direzione della velocità.

  • Fare clic sul pulsante "Avanti" per avanzare di un fotogramma nel tempo.
  • Fare clic sul pulsante "Indietro" per ripristinare un fotogramma in tempo.

Per modificare la velocità delle particelle, premere:

  • "Su" e "giù" per aumentare o diminuire la magnitudine della velocità, rispettivamente.
  • "Sinistra" e "destra" per ruotare la velocità.
  • "N" per cambiare il cerchio che controlli.

Infine, per attivare la visibilità delle frecce, premi "V"


Step 16: Una nota sulle radici quadratiche

Ci sono due radici all'equazione quadratica. In questo contesto, siamo interessati alle radici reali. Tuttavia se le due particelle non sono impostate sul percorso di collisione (entrambi i percorsi sono paralleli tra loro), allora verranno generate radici immaginarie invece di radici reali. In questo caso, rimarranno entrambe le radici reali NaN.

Se entrambe le particelle sono impostate su un percorso di collisione, otterremo due vere radici. Ma cosa rappresentano queste due radici?

Ricordiamo al punto 12 che abbiamo usato il Teorema di Pitagora per legare \ ((x '_ grigio, \ y' _ grigio) \) e \ ((x '_ rosso, \ y' _ rosso ) \) insieme in un'equazione. Bene, ci sono due situazioni in cui la distanza tra i centri di due cerchi è esattamente la somma di entrambi i raggi: uno prima della collisione e uno dopo la collisione. Dai un'occhiata a questa immagine:

Quindi quale scegliere? Ovviamente il primo perché non siamo interessati all'istanza dopo la collisione. Quindi dovremmo sempre scegliere il valore minore di entrambe le radici e valutarlo. Se il valore è positivo e inferiore a 1, si verificherà una collisione durante il fotogramma successivo. Se il valore è negativo, la collisione è avvenuta in passato.


Passaggio 17: viene spiegato l'ActionScript

Diamo un'occhiata al Actionscript implementato per questo esempio. Innanzitutto, le variabili.

 // c1 è il cerchio grigio // c2 è il cerchio rosso private var c1: Circle, c2: Circle; // v1 è la velocità del cerchio grigio // v2 è la velocità privata del cerchio rosso var v1: Vector2D, v2: Vector2D, commuta: Boolean = true, usingV1: Boolean = true; // tri1 formerà la punta di freccia della v1 // tri2 formerà la punta della freccia della v2 private var tri1: Triangle, tri2: Triangle; contenitore var privato: Sprite; private var eq: EqQuadratic;

Quindi il calcolo delle radici. Potresti voler controllare il seguente codice ActionScript con le variabili sopra.

 var p: Number = v1.x - v2.x; var q: Number = c1.x - c2.x; var r: Number = v1.y - v2.y; var s: Number = c1.y - c2.y; var a: Number = p * p + r * r; var b: Number = 2 * (p * q + r * s); var c: Number = q * q + s * s - (c1.radius + c2.radius) * (c1.radius + c2.radius); eq.define (a, b, c); eq.calcRoots (); var roots: Vector. = eq.roots_R;

Ecco come devi interpretare le radici:

 // se non sono disponibili radici reali, allora non sono sul percorso di collisione se (isNaN (roots [0]) && isNaN (roots [1])) t.text = "Particelle non sul percorso di collisione".  else var time: Number = Math.min (roots [0], roots [1]) var