Matematikk og ActionScript av kurver Røtter

I den første opplæringen av denne serien tok vi en titt på tegningskurver ved hjelp av ligninger og AS3. Nå skal vi takle å løse disse ligningene for å finne røttene til en kurve - det vil si stedene hvor kurven krysser en gitt rett linje. Vi kan bruke dette til å forutsi kollisjoner med buede overflater, og for å unngå "tunneling" i Flash-spill.


Trinn 1: Kvadratiske røtter

Først, tid for litt rask matte revisjon. I denne veiledningen vil vi bare godta og anvende metodene vi skal bruke, men interesserte lesere kan se Wikipedias side om kvadratiske ligninger for informasjon om de matematiske avledelsene.

Så \ (f (x) \) er en kvadratisk funksjon. Hvis \ (f (x) \) tilsvarer 0, kan \ (x \) oppnås ved denne formelen:

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

\ (b ^ 2 - 4ac \) kalles diskriminant av formelen. Hvis diskriminanten er negativ, vil kvadratroten til diskriminanten produsere imaginære røtter, som vi ikke kan plotte. Omvendt, hvis diskrimineringen er positiv, vil du ha ekte nummerrøtter, og du vil kunne plotte dem på skjermen.


Trinn 2: Visualisering av kvadratiske røtter

Så hva er røtter? Vel i vår sammenheng er de ikke noe mer enn skjæringspunkter mellom den kvadratiske kurven og en linje. Anta for eksempel at vi er interessert i å finne krysspunktet (e) av følgende sett av ligninger:

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

Dette er et typisk scenario for å lete etter skjæringspunktet mellom en kvadratisk kurve og x-aksen (fordi x-aksen er linjen der y == 0). Siden krysspunktet (er) per definisjon deles av \ (f (x) \ og \ (g (x) \), kan vi konkludere med at \ (f (x) = g (x) \) for verdiene av x som vi leter etter.

Det er da en trivial operasjon der du bare erstatter funksjonene, og deretter bruker du formelen fra Trinn 1 for å få røttene. Nå er det flere muligheter vi kan forutse som vist nedenfor.

(Som du kan se, betyr "imaginære røtter" for vår hensikt at kurven aldri krysser x-aksen.)

La oss nå vurdere saken der \ (g (x) \) er mer enn bare en verdslig horisontal linje. La oss si at det er en skrå linje, \ (g (x) \ = \ mx \ + \ d \). Når vi likestiller begge funksjonene, må vi gjøre en liten forutregning før formelen kan brukes effektivt.

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

Jeg har tatt med en interaktiv Flash presentasjon under, så vær så snill å dra de røde og blå punktene. Gult prikker angir krysspunktene. Du må kanskje plassere kurven og linjen for å skjære hverandre for at de gule punktene skal vises.


Trinn 3: Plotting dette med ActionScript

Det fulle skriptet finnes i Demo1.as; her skal jeg bare forklare et viktig utdrag av koden. La oss se på AS3 for å tegne kurven og linjen:

 privat funksjon redraw (): void var cmd: Vector. = Ny Vector.; var koordinat: Vector. = Ny Vector.; // redraw kurve; m1 = ny Matrix3d ​​(kurvepoeng [0] .x * kurvepoeng [0] .x, kurvepoeng [0] .x, 1, 0, kurvepoeng [1] .x * kurvepoeng [1] .x, kurvepoeng [1] .x , 1, 0, kurvepoeng [2] .x * kurvepoeng [2] .x, kurvepoeng [2] .x, 1, 0, 0,0,0,1); m2 = ny Matrix3d ​​(kurvepoeng [0] .y, 0, 0, 0, kurvepoeng [1] .y, 0, 0, 0, kurvepoeng [2] .y, 0, 0, 0, 0,0,0, 1) m1.invert (); m2.append (m1); quadratic_equation.define (m2.n11, m2.n21, m2.n31); for (var jeg: 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); 

Hoveddelen av ActionScript for å tegne kurven fra linje 80 ~ 104 er stort sett lånt fra forrige veiledning, så jeg skal bare forklare litt om koden for å tegne en linje.

I Flash-presentasjonen ovenfor er det to interaktive blå prikker. Hver av disse har koordinater, og med begge prikkene dannes en linje. Siden begge punktene ligger på samme linje, deler de en felles helling og y-avskjæring for å danne en generell linjekvasjon:

\ [
y \ = \ mx \ + \ d, \\
m \ = \ skråning, \ d \ = \ y-intercept
\]

Vi kan bruke en liten algebra for å løse de to ukendighetene, \ (m \) og \ (d \). Gitt koordinatene til de to blå punktene som \ ((x_1, \ y_1) \) og \ ((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 og 1 \\ x_2 & 1 \ end bmatrix ^ - 1
\ begin bmatrix y_1 \\ y_2 \ end bmatrix =
\ begin bmatrix x_1 og 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 og 1 \\ x_2 & 1 \ end bmatrix ^ - 1
\ begin bmatrix y_1 \\ y_2 \ end bmatrix =
Jeg
\ begin bmatrix m \\ d \ end bmatrix
[/ Latex]

(Merk at en matrise med overordnet -1 refererer til invers av den matrisen.)

Ved å bruke dette beregnes \ (m \) og \ (d \). Vi kan nå tegne linjen ved å bli med koordinatene \ ((0, y_3) \) og \ ((scene.stageWidth, y_4) \). Hvordan finner du \ (y_3 \) og \ (y_4 \)? Vel nå som \ (m \), \ (x \) og \ (d \) er kjent, kan vi ganske enkelt sette alle disse verdiene inn i linjen generell ligning,

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

... for å få dem \ (y \) s.


Trinn 4: Beregn de kvadratiske røttene

For å beregne posisjonen til skjæringspunktene, skal vi bruke formelen fra trinn 1. Dette gjøres i EqQuadratic.as som funksjonene vist nedenfor:

 / ** skrivebeskyttet * diskriminerende av likning * / offentlig funksjon få diskriminator (): tall // B * B-4 * A * C retur _B * _B - 4 * _A * _C;  / ** * Utfører beregning for å skaffe røtter * / offentlig funksjon calcRoots (): void var disk: Number = this.discriminant // håndtere imaginære røtter hvis < 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);  

Ytterligere detaljer om EqQuadratic.as:

Funksjon Type Inngangsparameter funksjonalitet
EqQuadratic Metode Nil Klassekonstruktør
definere Metode Koeffisientene a, b og c av kvadratisk ligning Instansér koeffisientverdiene
fx_of Metode Verdi av x Returnerer \ (f (x) \) av gitt \ (x \) -inngangen.
calcRoots Metode Nil Utfører beregning for å oppnå kvadratisk rot
DIFF1 Metode \ (x \) koordinere for første graders differensiering Differensiert \ (f (x) \) av gitt \ (x \) i første grad.
DIFF2 Metode \ (x \) koordinere for andre grad differensiering Differensiert \ (f (x) \) av gitt \ (x \) i andre grad.
discriminat Eiendom, skrivebeskyttet Nil Returnerer verdien av diskriminant, \ (b ^ 2 - 4ac \)
roots_R Eiendom, skrivebeskyttet Nil Returnerer en tallvektor for røtter av ekte tall. Elementer er NaN hvis ingen reelle røtter eksisterer.
roots_i Eiendom, skrivebeskyttet Nil Returnerer en strengvektor for røtter av imaginært nummer. Elementer er null hvis det ikke finnes noen imaginære røtter.

Trinn 5: Plotting dette med ActionScript

Et eksempel på å utnytte dette EqQuadratic.as er i Demo1.as. Etter innvielsen av EqQuadratic, Vi skal bruke den til å beregne røttene. Deretter, etter å ha bekreftet tilstedeværelsen av ekte røtter, bruker vi dem til å plotte de gule punktene.

Nå refererer røttene til bare \ (x \) komponenten av koordinatene. For å få tak i \ (y \) s, gjett hva? Igjen legger vi verdiene av \ (m \), \ (d \) (beregnet tidligere i Trinn 3) og \ (x \) (fra røttene) til linjens generelle ligning, \ (y \ = \ mx \ + \ d \). Sjekk ut tilsvarende kode i linje 135 og 136.

 privat funksjon recalculate_reposition (): void quadratic_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); var rødder: Vector. = quadratic_equation.roots_R; hvis (! erNaN (røtter [0]) &&! isNaN (røtter [1])) intersec_points [0] .x = røtter [0]; intersec_points [0] .y = n2.a * røtter [0] + n2.b intersec_points [1] .x = røtter [1]; intersec_points [1] .y = n2.a * røtter [1] + n2.b ellers intersec_points [0] .x = -100; intersec_points [0] .y = -100; intersec_points [1] .x = -100; intersec_points [1] .y = -100; 

Trinn 6: Kubiske røtter

Kubiske røtter, ikke overraskende, er skjæringspunktene mellom a kubikk kurve og en linje. Men en kubisk kurve er litt annerledes enn en kvadratisk kurve, og i dette henseende kan mulighetene for hvor kryssene ligger, være forskjellige.

Bildet nedenfor viser en kubisk kurve som krysser med x-aksen:

Igjen, her er en liten Flash-presentasjon for å eksperimentere med. Røde og blå prikker kan trekkes mens de gule bare angir krysspunktene.


Trinn 7: Generell formel for kubiske røtter

Den generelle formelen for å finne en kubisk kurve ble oppdaget av Cardano. Selv om jeg er fristet til å utdybe detaljer, vil jeg bare vise interesserte lesere til følgende lenker:

  • Wikipedia
  • Wolfram og
  • En annen brukervennlig PDF-fil.

Uansett, den EqCubic.as klassen implementerer denne formelen for å løse røttene av kubiske funksjoner sammen med andre matematiske verktøyfunksjoner. Generelt alle attributter og metoder for EqCubic.as følg beskrivelsen som angitt i trinn 4, fordi begge klassene EqQuadratic.as og EqCubic.as implementere ett felles grensesnitt, IEquation.as, med unntak av detaljene som er oppført nedenfor.

Funksjon Forskjell
definere Totalt fire koeffisienter (a, b, c, d) til inngang for kubisk ligning; bare tre for kvadratisk ligning.
roots_R, root_i Summen av ekte og imaginære røtter er tre for en kubisk ligning, men to for en kvadratisk ligning.

Trinn 8: Plotting dette med ActionScript

Her er ActionScript-implementeringen for Flash-presentasjonen fra trinn 5. Den fulle koden er i Demo3.as.

 privat funksjon redraw (): void var cmd: Vector. = Ny Vector.; var koordinat: Vector. = Ny Vector.; // redraw kurve; m1 = ny Matrix3d ​​(kurvepoeng [0] .x * kurvepoeng [0] .x * kurvepoeng [0] .x, kurvepoeng [0] .x * kurvepoeng [0] .x, kurvepoeng [0] .x, 1, kurvepoeng [1] .x * kurvepoeng [1] .x * kurvepoeng [1] .x, kurvepoeng [1] .x * kurvepoeng [1] .x, kurvepoeng [1] .x, 1, kurvepoeng [2] .x * curve_points [2] .x * curve_points [2] * kurvepoeng [3] .x, kurvepoeng [3] .x * kurvepoeng [3] .x, kurvepoeng [3] .x, 1); m2 = ny Matrix3d ​​(kurvepoeng [0] .y, 0, 0, 0, kurvepoeng [1] .y, 0, 0, 0, kurvepoeng [2] .y, 0, 0, 0, kurvepoeng [3] .y , 0, 0, 0) m1.invert (); m2.append (m1); cubic_equation.define (m2.n11, m2.n21, m2.n31, m2.n41); for (var jeg: 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); 

Igjen, ActionScript-kommandoene for å tegne en kubisk kurve er nøyaktig det samme som forklart i min forrige artikkel, mens ActionScript-kommandoer for å tegne linjen allerede er forklart i trinn 3 i denne.

La oss nå fortsette å beregne og plassere de kubiske røttene:

 privat funksjon recalculate_reposition (): void cubic_equation.define (m2.n11, m2.n21, m2.n31 - n2.a, m2.n41 - n2.b); cubic_equation.calcRoots (); var rødder: Vector. = cubic_equation.roots_R; for (var jeg: 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;   

Etter instantiating cubic_equation i konstruktøren fortsetter vi å definere koeffisientene, beregne røttene og lagre røttene i en variabel.

En liten notat på røttene: det er maksimalt tre virkelige røtter for en kubisk ligning, men ikke alle ekte røtter er til stede i alle situasjoner som enkelte røtter kan være imaginære. Så hva skjer når det bare er en ekte rot, for eksempel? Vel, en av de mange røttene som heter fra cubic_equation.roots_R vil være et reelt tall, mens alle de andre vil ikke være et nummer (NaN). Sjekk ut den uthevede ActionScript for dette.


Trinn 9: Forutsi hvor et objekt vil kollidere med buet overflate

En god applikasjon av beregning av røtter, projiserer et kollisjonspunkt på buet overflate, som vist nedenfor. Bruk venstre og høyre piltast til å styre det bevegelige skipet, og trykk opp for å akselerere. Du vil merke at kollisjonspunkter som ville ha skjedd tidligere, er litt dimmet.


Trinn 10: Implementering

Ideen ligner den i min veiledning om å forutse kollisjonspunkter. I stedet for å kollidere med en rett linje bruker vi imidlertid en krummet linje. La oss sjekke koden.

Utsnittet nedenfor kalles hver ramme:

 privat funksjon oppdatering (e: Event): void // Styring til venstre og høyre hvis (kontroll == 1) velo = velo.rotate (Math2.radianOf (-5)); ellers hvis (kontroll == 2) velo = velo.rotate (Math2.radianOf (5)); // manipulere hastighet var currVelo: Number = velo.getMagnitude (); hvis (økning == 0) currVelo - = 0,5; currVelo = Math.max (currVelo, 1); // lavere bundet til hastighet ellers hvis (økning == 1) currVelo + = 0.5; currVelo = Math.min (currVelo, 5); // øvre grense for hastighet velo.setMagnitude (currVelo); // oppdateringshastighet ship.x + = velo.x; ship.y + = velo.y; ship.rotation = Math2.degreeOf (velo.getAngle ()); // reflektere når skipet er ute av scenen hvis (skip.x <0 || ship.x > stage.stageWidth) velo.x * = -1; hvis (ship.y <0 || ship.y > stage.stageHeight) velo.y * = -1; tegne (); omberegne (); 

Kjernekoden ligger i tegne og beregne. La oss først se hva som er i tegne. Det er det samme vi hadde brukt i tidligere demoer. En liten notat om å tegne linjen. Vi så i tidligere demoer at to punkter er nødvendige for å tegne, få ligningen. Vel, her har vi bare ett skip. For å få det andre punktet må du bare legge fartøyets hastighet til sin nåværende posisjon. Jeg har fremhevet koden for enkelhets skyld.

 privat funksjon redraw (): void var cmd: Vector. = Ny Vector.; var koordinat: Vector. = Ny Vector.; // redraw kurve; m1 = ny 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 = ny 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); for (var jeg: int = minX; jeg < 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); 

Nå for beregne, Jeg har gjort en liten vektorberegning for å sjekke om punktet er bak eller foran skipet. Sjekk ut den uthevede koden:

 privat funksjon omberegne (): void quadratic_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); var rødder: Vector. = quadratic_equation.roots_R; for (var jeg: 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øtter [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;   

Trinn 11: Tidsbasert kollisjonsdeteksjon

En annen stor applikasjon er ikke helt så åpenbar som den første. For å utføre en mer nøyaktig kollisjonsdeteksjon, i stedet for å basere vår konklusjon på avstanden mellom to objekter, vil vi uae deres tid til å påvirke. Hvorfor? Fordi "tunneling" kan skje hvis vi bruker avstandsbasert kollisjonsdeteksjon:

Overvei en kollisjonsdetekteringsalgoritme som er basert på avstand for to sirkler. Av de fire rammene som vises, har bare rammen 2.15 oppdaget kollisjon mellom to sirkler. Hvorfor? Fordi den nåværende avstanden mellom det grå og det røde sirkelsenteret er mindre enn summen av begge sirkelradier.

(Lesere som er interessert i flere detaljer om dette emnet, kan se denne artikkelen.)

\ [avstand \ mellom \ sirkler \

Problemet skyldes hvordan Flash fortsetter med en diskret ramme om gangen, noe som betyr at bare rammer 1, 2 og 3 vil bli tatt med fange, og ikke øyeblikkene i mellom øyeblikksbilder. Nå kolliderte ikke de grå og røde kretsene i disse rammene i henhold til en avstandsbasert beregning, så de røde sirkeltunnelene gjennom den grå!

For å fikse dette trenger vi en måte å se kollisjonen som skjedde mellom rammer 2 og 3. Vi må beregne tiden for å påvirke mellom to sirkler. Når vi for eksempel sjekker at tiden for å påvirke er mindre enn 1 ramme i ramme 2, betyr dette at når Flash fortsetter, vil en ramme fremover kollisjon eller selv tunneling definitivt ha funnet sted.

\ [hvis \ time \ to \ impact, \ t, \ oppfyller \ 0 \

Spørsmålet er hvordan beregner vi denne gangen?


Trinn 12: Forutregninger

Jeg prøver å vise metoden så enkelt som mulig.

Gitt scenariet ovenfor er de to grå og røde kretsene for tiden lokalisert på \ ((x_ grå, \ y_ grå) \) og \ ((x_ rød, \ y_ rød) \). De beveger seg på \ (v_ grå \) og \ (v_ rød \) og settes på en kollisjonsbane. Vi er interessert i å beregne tiden tatt, \ (t \) for at de skal nå posisjonene \ ((x '_ grå, \ y' _ grå) \ , \ y '_ rød) \), angitt av de gjennomsiktige grå og røde sirkler, der kollisjonen oppstod.

\ [
displacement_ future = displacement_ present + hastighet * tid \\
x '_ grå = x_ grå + v_ gray_x * t \ ... (eq. \ 1) \\
y '_ grå = y_ grå + v_ gray_y * t \ ... (eq. \ 2) \\
x '_ rød = x_ rød + v_ red_x * t \ ... (eq. \ 3) \\
y 'rød = y_ rød + v_ red_y * t \ ... (eq. \ 4)
\]

Merk at jeg har avledet de horisontale og vertikale komponentene i \ (v_ gray \) til \ (v_ gray_x \) og \ (v_ gray_y \). Det samme gjelder hastigheten til den røde sirkelen; sjekk ut denne Quick Tips for å bli kjent med hvordan disse komponentene er avledet.

Vi mangler fortsatt ett forhold til å binde alle disse ligningene sammen. La oss se bildet nedenfor.

Det andre forholdet går tilbake til Pythagoras. Når begge sirkler møtes, er avstanden mellom begge sentrene nøyaktig \ (rad_ gray \) pluss \ (rad_ red \).

\ [
Pythagoras \ Theorem, \ z ^ 2 = x ^ 2 + y ^ 2 \\
(Rad_ grå + rad_ rød) ^ 2 = (x '_ grå -x' _ rød) ^ 2 + (y '_ grå -y' _ rød) ^ 2 \ ... (eq. \ 5) \\
\]

Dette er hvor du erstatter ligninger 1 ~ 4 i ligning 5. Jeg forstår det ganske skremmende matematisk, så jeg har skilt det ut i trinn 13. Føl deg fri til å hoppe over det for å komme frem til resultatet i trinn 14.


Trinn 13 (Valgfritt): Matematisk Rigor

Først etablerer vi følgende identiteter.

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

Vær alltid oppmerksom på at alle matematiske symboler representerer en konstant bortsett fra tiden, \ (t \), som er emnet av interesse.

\ (x_ grå, \ v_ gray_x, \ y_ red, \) og så videre, er alle definert i scenariet.

Deretter prøver vi å bryte problemet vårt på sikt etter sikt:

\ [
(Rad_ grå + rad_ rød) ^ 2 = (x '_ grå -x' _ rød) ^ 2 + (y '_ grå -y' _ rød) ^ 2 \ \
Vurder \ term \ (x '_ grå -x' _ rød) ^ 2 \ og \ utnytte \ id. \ 2 \\
(x '_ grå -x' _ rød) ^ 2 = (x '_ grå) ^ 2-2 (x' _ grå) (x '_ rød) + _ rød) ^ 2 \\
\]
\ [
Vurder \ term \ (x '_ grå) ^ 2 \\
(X '_ grå) ^ 2 \\
= (x_ grå + v_ gray_x * t) ^ 2, \ bruk \ id. \ 1 \\
= (X_ grå) ^ 2 + 2 (X_ grå) (V ^ gray_x * t) + (V ^ gray_x * t) ^ 2
\]
\ [
Vurder \ term \ -2 (x '_ grå) (x' _ rød) \\
-2 (x '_ grå) (x' _ rød) \\
= -2 (X_ grå + V_ gray_x * t) (X_ rød + V_ red_x * t) \\
= -2 [(X_ grå) (X_ rød) + (X_ grå) (V ^ red_x * t) + (V ^ gray_x * t) (X_ rød) + (V ^ gray_x * t) (V ^ red_x * t)] \\
= -2 (X_ grå) (X_ rød) - 2 (X_ grå) (V ^ red_x * t) 2 (V ^ gray_x * t) (X_ rød) - 2 ( V_ gray_x * t) (V ^ red_x * t)
\]
\ [
Vurder \ term \ (x '_ rød) ^ 2 \\
(X '_ rød) ^ 2 \\
= (x_ rød + v_ red_x * t) ^ 2, \ bruk \ id. \ 1 \\
= (X_ rød) ^ 2 + 2 (X_ rød) (V ^ red_x * t) + (V ^ red_x * t) ^ 2
\]

Nå kan vi raskt se den høyeste kraften til \ (t \) er 2. Så vi har oss selv en kvadratisk ligning. La oss samle alle koeffisientene bidratt av disse tre betingelsene i henhold til deres krefter.

\ (T ^ 2 \) \ (T \) \ (T ^ 0 = 1 \)
\ ((V ^ gray_x) ^ 2 \) \ (2 (X_ grå) (V ^ gray_x) \) \ ((X_ grå) ^ 2 \)
\ (- 2 (V ^ gray_x) (V ^ red_x) \) \ (- 2 (X_ grå) (V ^ red_x) - 2 (V ^ gray_x) (X_ rød) \) \ (- 2 (X_ grå) (X_ rød) \)
\ ((V ^ red_x) ^ 2 \) \ (2 (X_ rød) (V ^ red_x) \) \ ((X_ rød) ^ 2 \)

La oss analysere koeffisientene med \ (t ^ 2 \) og \ (t ^ 0 \).

\ [
(v_ gray_x) ^ 2-2 (v_ gray_x) (v_ red_x) + (v_ red_x) ^ 2, \ tilbakekall \ 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_ grå) ^ 2-2 (x_ grå) (x_ rød) + (x_ rød) ^ 2, \ tilbakekall \ id. \ 2 \\
(x_ grå) ^ 2-2 (x_ grå) (x_ rød) + (x_ rød) ^ 2 = (x_ grå -x_ rød) ^ 2
\]

Og det av \ (t \).

\ [
Forenkle\\
a = (x_ grå), \ b = (v_ gray_x) \\
c = (v_ red_x), \ d = (x_ rød) \\
2AB-2AC-2 bd + 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_ grå -x_ rød)
\]

La oss oppsummere i løpet av \ ((x '_ grå -x' _ rød) ^ 2 \)

\ [
(X '_ grå -x' _ rød) ^ 2 \\
= v_ grå_x -v_ red_x) ^ 2 * t ^ 2 + 2 (v_ gray_x -v_ red_x) (x_ grå -x_ rød) * t + -x_ rød) ^ 2
\]

Vær oppmerksom på at dette bare gjelder for et begrep i \ (eq. \ 5 \). Vi må utføre samme prosess for en annen term \ ((y '_ grå -y' _ rød) ^ 2 \). Siden de har samme algebraiske form, bør resultatet også være det samme.
\ [
(Y '_ grå -y' _ rød) ^ 2 \\
= v_ grå_y -v_ red_y) ^ 2 * t ^ 2 + 2 (v_ gray_y -v_ red_y) (y_ grå -y_ rød) * t + -y_ rød) ^ 2
\]

Dermed etter omplassering i form av \ (t \), \ (eq. \ 5 \) bør det være som følger.

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


Trinn 14: Resultatet

Så fra det forrige trinnet, gjennom rigorøs algebra, kom vi til følgende formel:

\ [
p = V ^ gray_x -v_ red_x \\
q = X_ grå -x_ rød \\
r = V ^ gray_y -v_ red_y \\
s = y_ grå -y_ rød \\
(p ^ 2 + r ^ 2) * t ^ 2 + 2 (pq + rs) * t + (q ^ 2 + s ^ 2- (rad_ grå + rad_ rød) 2) = 0
\]

Nå er dette en stor kvadratisk formel. Vi prøver å gruppere koeffisientene inn i det som aksepteres av EqQuadratic. Sammenlign de to skjemaene:

\ [
økse ^ 2 + bx + c = 0 \\
(p ^ 2 + r ^ 2) * t ^ 2 + 2 (pq + rs) * t + (q ^ 2 + s ^ 2- (rad_ grå + rad_ rød) 2) = 0 \\
a = p ^ 2 + r ^ 2) \\
b = 2 (pq + rs) \\
c = (q ^ 2 + s ^ 2- (rad_ grå + rad_ rød) ^ 2)
\]


Trinn 15: Prøveimplementering

Så her er en Flash presentasjon for å demonstrere ideen. Du vil se to partikler på scenen, en grå og den andre røde. Begge er koblet til en pil, som angir størrelsen og retningen av hastigheten.

  • Klikk på "Neste" -knappen for å få fram en ramme i tide.
  • Klikk på "Tilbake" -knappen for å returnere en ramme i tide.

For å endre partikkelsens hastighet, trykk på:

  • "Opp" og "Ned" for å øke og redusere hastighetenes størrelse, henholdsvis.
  • "Venstre" og "høyre" for å rotere hastigheten.
  • "N" for å bytte hvilken sirkel du kontrollerer.

Til slutt, for å skifte synlighet av pilene, trykk "V"


Trinn 16: En kommentar på kvadratiske røtter

Det er to røtter til den kvadratiske ligningen. I denne sammenheng er vi interessert i de reelle røttene. Men hvis de to partiklene ikke er satt på kollisionsbanen (begge veiene er parallelle med hverandre), så vil imaginære røtter bli produsert i stedet for ekte røtter. I dette tilfellet vil begge reelle røttene forbli NaN.

Hvis begge partiklene settes på en kollisjonsbane, får vi to virkelige røtter. Men hva representerer disse to røttene?

Gjenta i trinn 12 at vi brukte Pythagoras teorem til å knytte \ ((x '_ grå, \ y' _ grå)) og \ ((x '_ rød, \ ) \) sammen i en ligning. Vel, det er to situasjoner hvor avstanden mellom to sirkels sentre er nøyaktig summen av begge radier: en før kollisjon og en etter kollisjon. Ta en titt på dette bildet:

Så hvilken velger vi? Åpenbart den første fordi vi ikke er interessert i forekomsten etter kollisjon. Så vi bør alltid velge den laveste verdien av begge røttene og evaluere den. Hvis verdien er positiv og mindre enn 1, vil det oppstå en kollisjon under neste ramme. Hvis verdien er negativ, skjedde kollisjonen tidligere.


Trinn 17: ActionScript forklart

La oss se på Actionscript implementert for dette eksemplet. Først, variablene.

 // c1 er den grå sirkelen // c2 er den røde sirkelen private var c1: Sirkel, c2: Sirkel; // v1 er den grå sirkelens hastighet // v2 er den røde sirkelens hastighet private var v1: Vector2D, v2: Vector2D, veksle: Boolean = true, usingV1: Boolean = true; // tri1 vil danne pilen av v1 // tri2 vil danne pilen til v2 private var tri1: Triangle, tri2: Triangle; privat var container: Sprite; privat var eq: eqQuadratic;

Deretter beregning av røtter. Du vil kanskje krysse gjennom følgende ActionScript med variablene ovenfor.

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

Slik tolker du røttene:

 // hvis ingen reelle røtter er tilgjengelige, så er de ikke på kollisjonssporet hvis (isNaN (røtter [0]) && isNaN (røtter [1])) t.text = "Partikler ikke på kollisionsbane."  ellers var tid: Nummer = Math.min (røtter [0], røtter [1]) var int_time: int = tid * 1000; tid = int_time / 1000; t.text = "Rammer for å påvirke:" + time.toString () + "\ n"; hvis (tid> 1) t.appendText ("Partikler lukker ...") ellers hvis (tid> 0 && tid < 1) t.appendText("Particles WILL collide next frame!") else if (time < 0) t.appendText("Collision had already happened."); 

Konklusjon

Så nå har vi studert kvadratiske og kubiske røtter i ActionScript, samt å se nærmere på to eksempler på hvordan vi kan bruke de kvadratiske røttene.

Takk for at du tok deg tid til å lese denne opplæringen. Gi en kommentar hvis du ser andre programmer av kvadratiske røtter (eller eventuelle feil!)