In de eerste zelfstudie van deze serie hebben we tekencurven bekeken met behulp van vergelijkingen en AS3. Nu gaan we het oplossen van die vergelijkingen aanpakken om de wortels van een curve te vinden - dat wil zeggen, de plaatsen waar de curve een gegeven rechte lijn kruist. We kunnen dit gebruiken om botsingen met gebogen oppervlakken te voorspellen en om "tunneling" in Flash-spellen te voorkomen.
Eerst tijd voor wat snelle wiskundige revisie. In deze zelfstudie zullen we alleen de methoden accepteren en toepassen die we zullen gebruiken, maar geïnteresseerde lezers kunnen verwijzen naar Wikipedia's pagina over kwadratische vergelijkingen voor informatie over de wiskundige afleidingen..
Dus \ (f (x) \) is een kwadratische functie. Als \ (f (x) \) gelijk is aan 0, kan \ (x \) worden verkregen door deze formule:
\ [Gegeven \ f (x) \ = \ ax ^ 2 + bx + c, \ \]
\ [f (x) \ = \ 0, \ x = \ frac -b \ pm \ sqrt b ^ 2 - 4ac 2a \]
\ (b ^ 2 - 4ac \) wordt het discriminant van de formule. Als de discriminant negatief is, zal de vierkantswortel van de discriminant produceren denkbeeldige wortels, die we niet kunnen plotten. Omgekeerd, als de discriminator positief is, heb je echte nummerwortels en kun je ze op het scherm plotten.
Dus wat zijn roots? Welnu, in onze context zijn ze niets meer dan snijpunten tussen de kwadratische curve en een lijn. Stel dat we geïnteresseerd zijn om het snijpunt (de snijpunten) van de volgende reeks vergelijkingen te vinden:
\ (
f (x) \ = \ ax ^ 2 + bx + c \\
g (x) \ = \ 0
\)
Dit is een typisch scenario van het zoeken naar het snijpunt (de snijpunten) tussen een kwadratische curve en de x-as (omdat de x-as de lijn is waar y == 0
). Omdat per definitie het snijpunt (de kruispunten) wordt gedeeld door \ (f (x) \) en \ (g (x) \), kunnen we concluderen dat \ (f (x) = g (x) \) voor de waarden van X
waar we naar op zoek zijn.
Het is dan een triviale operatie waarbij u alleen de functies vervangt en vervolgens de formule uit stap 1 toepast om de wortels te verkrijgen. Nu zijn er verschillende mogelijkheden waarop we kunnen anticiperen, zoals hieronder weergegeven.
(Zoals u kunt zien, betekent 'imaginaire wortels' voor onze doeleinden dat de curve nooit de x-as kruist.)
Laten we nu eens kijken naar het geval waarin \ (g (x) \) meer is dan alleen een alledaagse horizontale lijn. Laten we zeggen dat het een schuine lijn is, \ (g (x) \ = \ mx \ + \ d \). Wanneer we nu beide functies gelijkstellen, moeten we een kleine voorcalculatie uitvoeren voordat de formule effectief kan worden toegepast.
\ [
ax ^ 2 \ + \ bx + c \ = \ mx \ + \ d \\
ax ^ 2 \ + \ (\ b \ - m) \ x + (c \ - \ d) \ = \ 0
\]
Ik heb hieronder een interactieve Flash-presentatie opgenomen, dus sleep de rode en blauwe stippen. Gele stippen geven de snijpunten aan. Mogelijk moet u de curve en lijn zo plaatsen dat ze elkaar snijden, zodat de gele stippen verschijnen.
Het volledige script is te vinden in Demo1.as
; hier zal ik een cruciaal uittreksel van de code uitleggen. Laten we naar de AS3 kijken voor het tekenen van de curve en lijn:
private function redraw (): void var cmd: Vector.= nieuwe Vector. ; var coord: Vector. = nieuwe Vector. ; // opnieuw tekenen curve; m1 = nieuwe Matrix3d (curve-punten [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-punten [2] .x * curve-punten [2] .x, curve-punten [2] .x, 1, 0, 0,0,0,1); m2 = nieuwe Matrix3d (curvepunten [0] .y, 0, 0, 0, curve_punten [1] .y, 0, 0, 0, curve_punten [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 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);
Het grootste deel van ActionScript voor het tekenen van de curve van regel 80 ~ 104 is grotendeels overgenomen van de vorige zelfstudie, dus ik zal een beetje uitleggen over de code voor het tekenen van een lijn.
In de bovenstaande Flash-presentatie zijn er twee interactieve blauwe stippen. Elk van deze heeft coördinaten en met beide punten wordt een lijn gevormd. Omdat beide punten op dezelfde lijn liggen, delen ze een gemeenschappelijke helling en y-snijpunt om een algemene lijnvergelijking te vormen:
\ [
y \ = \ mx \ + \ d, \\
m \ = \ slope, \ d \ = \ y-onderscheppen
\]
We kunnen een kleine algebra gebruiken om de twee onbekenden op te lossen, \ (m \) en \ (d \). Gegeven de coördinaten van de twee blauwe stippen als \ ((x_1, \ y_1) \) en \ ((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 =
ik
\ begin bmatrix m \\ d \ end bmatrix
[/latex]
(Merk op dat een matrix met superscript -1 verwijst naar de inverse van die matrix.)
Dus met behulp hiervan worden \ (m \) en \ (d \) berekend. We kunnen nu de lijn tekenen door lid te worden van de coördinaten \ ((0, y_3) \) en \ ((stage.stageWidth, y_4) \). Hoe vind je \ (y_3 \) en \ (y_4 \)? Goed nu dat \ (m \), \ (x \) en \ (d \) bekend zijn, kunnen we eenvoudig al deze waarden in de algemene regel van de lijn zetten,
\ (y \ = \ mx \ + \ d \)
... om die \ (y \) s te krijgen.
Om de positie van de kruisende punten te berekenen, gebruiken we de formule uit stap 1. Dit gebeurt in EqQuadratic.as
als de hieronder getoonde functies:
/ ** Alleen-lezen * Discriminant van vergelijking * / publieke functie wordt discriminant (): Nummer // B * B-4 * A * C retour _B * _B - 4 * _A * _C; / ** * Voert een berekening uit om wortels te verkrijgen * / public function calcRoots (): void var disc: Number = this.discriminant // handle imaginaire roots 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);
Verdere details van EqQuadratic.as
:
Functie | Type | Input Parameter | functionaliteit |
EqQuadratic | Methode | nul | Klasse constructor |
bepalen | Methode | Coëfficiënten a, b en c van kwadratische vergelijking | Instantiëren de coëfficiëntwaarden |
fx_of | Methode | Waarde van x | Retourneert \ (f (x) \) van gegeven \ (x \) invoer. |
calcRoots | Methode | nul | Voert een berekening uit om kwadratische wortel te verkrijgen |
diff1 | Methode | \ (x \) coördinaat voor de eerste graad differentiatie | Gedifferentieerd \ (f (x) \) van gegeven \ (x \) in de eerste graad. |
Diff2 | Methode | \ (x \) coördinaat voor de tweedegraads differentiatie | Gedifferentieerd \ (f (x) \) van gegeven \ (x \) in de tweede graad. |
discriminat | Eigenschap, alleen-lezen | nul | Retourneert de waarde van discriminant, \ (b ^ 2 - 4ac \) |
roots_R | Eigenschap, alleen-lezen | nul | Retourneert een getalvector voor de wortels van het reële getal. Elementen zijn NaN als er geen echte wortels bestaan. |
roots_i | Eigenschap, alleen-lezen | nul | Retourneert een tekenreeksvector voor de wortels van een denkbeeldig nummer. Elementen zijn nul als er geen denkbeeldige wortels bestaan. |
Een voorbeeld om dit te gebruiken EqQuadratic.as
is in Demo1.as
. Na de start van EqQuadratic
, we zullen het gebruiken om de wortels te berekenen. Nadat we de aanwezigheid van echte wortels hebben gevalideerd, zullen we ze gebruiken om de gele stippen uit te zetten.
Nu verwijzen de wortels naar alleen de component \ (x \) van de coördinaten. Om de \ (y \) s te verkrijgen, raad eens? Nogmaals, we plaatsen de waarden van \ (m \), \ (d \) (eerder berekend in Stap 3) en \ (x \) (vanaf de basis) in de algemene regel, \ (y \ = \ mx \ + \ d \). Bekijk de bijbehorende code in regel 135 en 136.
private function 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;
Kubische wortels, niet verrassend, zijn de snijpunten tussen een kubiek curve en een lijn. Maar een kubieke curve is een beetje anders dan een kwadratische curve, en in dit opzicht zijn de mogelijkheden waar kruispunten kunnen worden gevonden anders.
De afbeelding hieronder toont een kubieke curve die de x-as kruist:
Nogmaals, hier is een kleine Flash-presentatie om mee te experimenteren. Rode en blauwe stippen kunnen worden gesleept terwijl de gele alleen de snijpunten aangeven.
De algemene formule om een kubieke curve te vinden, werd ontdekt door Cardano. Hoewel ik wordt verleid om de details uit te werken, zal ik geïnteresseerde lezers gewoon wijzen op de volgende links:
Hoe dan ook, de EqCubic.as
klasse implementeert deze formule om wortels van kubieke functies op te lossen samen met andere wiskundige functies van hulpprogramma's. Over het algemeen alle attributen en methoden voor EqCubic.as
volg de beschrijving zoals ingediend in stap 4, omdat beide klassen EqQuadratic.as
en EqCubic.as
één gemeenschappelijke interface implementeren, IEquation.as
, behalve de onderstaande details.
Functie | Verschil |
bepalen | Een totaal van vier coëfficiënten (a, b, c, d) voor invoer voor kubieke vergelijking; slechts drie voor kwadratische vergelijking. |
roots_R , root_i | Het totaal van echte en imaginaire wortels is drie voor een kubieke vergelijking, maar twee voor een kwadratische vergelijking. |
Dit is de Actionscript-implementatie voor de Flash-presentatie van stap 5. De volledige code staat in Demo3.as
.
private function redraw (): void var cmd: Vector.= nieuwe Vector. ; var coord: Vector. = nieuwe Vector. ; // opnieuw tekenen curve; m1 = nieuwe Matrix3d (curve_punten [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_punten [1] .x, 1, curve_punten [2] .x * curve_points [2] .x * curve_points [2] .x, curve_points [2] .x * curve_points [2] .x, curve_points [2] .x, 1, curve_punten [3] .x * curve_points [3] .x * curve-punten [3] .x, curve-punten [3] .x * curve-punten [3] .x, curve-punten [3] .x, 1); m2 = nieuwe Matrix3d (curve_punten [0] .y, 0, 0, 0, curve_punten [1] .y, 0, 0, 0, curve_punten [2] .y, 0, 0, 0, curve_punten [3] .y , 0, 0, 0) m1.invert (); m2.append (m1); cubic_equation.define (m2.n11, m2.n21, m2.n31, m2.n41); for (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);
Nogmaals, de ActionScript-opdrachten voor het tekenen van een kubieke curve zijn precies hetzelfde als uitgelegd in mijn vorige artikel, terwijl Actionscript-opdrachten om de lijn te tekenen al zijn uitgelegd in stap 3 van deze.
Laten we nu verder gaan met het berekenen en positioneren van de kubische wortels:
persoonlijke functie 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; for (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;
Na het instantiëren cubic_equation
in de constructor gaan we verder met het definiëren van de coëfficiënten, berekenen we de wortels en slaan we de wortels op in een variabele.
Een kleine opmerking over de wortels: er zijn maximaal drie echte wortels voor een kubieke vergelijking, maar niet alle echte wortels zijn aanwezig in alle situaties, omdat sommige wortels denkbeeldig kunnen zijn. Dus wat gebeurt er als er maar één echte root is, bijvoorbeeld? Nou, een van de vele wortels die worden genoemd cubic_equation.roots_R
zal een reëel getal zijn, terwijl alle anderen geen nummer zullen zijn (NaN
). Bekijk hiervoor de gemarkeerde ActionScript-code.
Een geweldige toepassing van rekenwortels projecteert een botspunt op een gebogen oppervlak, zoals hieronder wordt weergegeven. Gebruik de linker en rechter pijltjestoetsen om het bewegende schip te besturen en druk op omhoog om te versnellen. U zult merken dat botsingspunten die in het verleden zouden zijn gebeurd, enigszins worden gedimd.
Het idee is vergelijkbaar met dat in mijn zelfstudie over het voorspellen van botspunten. In plaats van botsen met een rechte lijn, gebruiken we nu een gebogen lijn. Laten we de code eens bekijken.
Het onderstaande fragment wordt elk frame genoemd:
persoonlijke functie-update (e: Event): void // Steering links en rechts if (controle == 1) velo = velo.rotate (Math2.radianOf (-5)); else if (control == 2) velo = velo.rotate (Math2.radianOf (5)); // manipulating velocity var currVelo: Number = velo.getMagnitude (); if (verhogen == 0) currVelo - = 0,5; currVelo = Math.max (currVelo, 1); // ondergrens voor velocity else if (increase == 1) currVelo + = 0.5; currVelo = Math.min (currVelo, 5); // bovengrens voor velocity velo.setMagnitude (currVelo); // update velocity ship.x + = velo.x; ship.y + = velo.y; ship.rotation = Math2.degreeOf (velo.getAngle ()); // weerspiegelen wanneer het schip uit fase is als (ship.x <0 || ship.x > stage.stageWidth) velo.x * = -1; if (ship.y <0 || ship.y > stage.stageHeight) velo.y * = -1; terugtrekken(); herberekenen ();
De kerncode ligt in terugtrekken
en herberekenen
. Laten we eerst zien wat erin zit terugtrekken
. Het is dezelfde als die we in vorige demo's gebruikten. Een kleine opmerking over het tekenen van de lijn. We hebben in vorige demo's gezien dat er twee punten nodig zijn om de vergelijking te krijgen. Welnu, hier hebben we maar één schip. Dus om het tweede punt te krijgen, voegt u gewoon de snelheid van het schip toe aan zijn huidige positie. Ik heb de code voor het gemak gemarkeerd.
private function redraw (): void var cmd: Vector.= nieuwe Vector. ; var coord: Vector. = nieuwe Vector. ; // opnieuw tekenen curve; m1 = nieuwe 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 = nieuwe 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 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);
Nu voor herberekenen
, Ik heb een kleine vectorberekening gedaan om te controleren of het punt achter of voor het schip ligt. Bekijk de gemarkeerde code:
persoonlijke functie herberekenen (): void quadratic_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); var roots: Vector.= quadratic_equation.roots_R; for (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;
Een andere geweldige applicatie is niet zo voor de hand liggend als de eerste. Om een nauwkeuriger botsingsdetectie uit te voeren, in plaats van onze conclusie te baseren op de afstand tussen twee objecten, zullen we ze vangen tijd om te beïnvloeden. Waarom? Omdat "tunneling" kan gebeuren als we botsingdetectie op afstand gebruiken:
Overweeg een botsingsdetectiealgoritme dat is gebaseerd op afstand voor twee cirkels. Van de vier getoonde frames kende alleen frame 2.15 een geslaagde botsing tussen twee cirkels. Waarom? Omdat de huidige afstand tussen de centra van de grijze en de rode cirkels kleiner is dan de som van de stralen van beide cirkels.
(Lezers die geïnteresseerd zijn in meer informatie over dit onderwerp, kunnen dit artikel raadplegen.)
\ [afstand \ tussen \ cirkels \
Het probleem wordt veroorzaakt door de manier waarop Flash één frame per keer doorloopt, wat betekent dat alleen frames 1, 2 en 3 met succes worden vastgelegd en niet de momenten tussen de momentopnames van de tijd. Nu kwamen de grijze en rode cirkels niet samen in deze frames volgens een op afstand gebaseerde berekening, dus de rode cirkel tunnelt dwars door de grijze cirkel heen!
Om dit te verhelpen, hebben we een manier nodig om de botsing te zien die zich heeft voorgedaan tussen frames 2 en 3. We moeten de impacttijd berekenen tussen twee cirkels. Als we bijvoorbeeld die tijd controleren op impact bij minder dan 1 frame op frame 2, betekent dit dat zodra Flash 1 frame vooruitrijdt of er zelfs zeker een tunneling heeft plaatsgevonden.
\ [if \ time \ to \ impact, \ t, \ vervult \ 0 \
De vraag is, hoe berekenen we deze tijd?
Ik zal proberen mijn methode zo eenvoudig mogelijk te laten zien.
Gezien het bovenstaande scenario bevinden de twee grijze en rode cirkels zich momenteel op \ ((x_ grijs, \ y_ grijs) \) en \ ((x_ rood, \ y_ rood) \). Ze bewegen respectievelijk met \ (v_ grijs \) en \ (v_ rood \) en zetten een aanvaringspad in. We zijn geïnteresseerd om de tijd te berekenen, \ (t \), om posities te bereiken \ ((x '_ gray, \ y' _ gray) \) en \ ((x '_ red , \ y '_ red) \), aangegeven door de doorschijnende grijze en rode cirkels, waar de botsing plaatsvond.
\ [
displacement_ toekomst = 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 '_ red = x_ red + v_ red_x * t \ ... (eq. \ 3) \\
y '_ red = y_ red + v_ red_y * t \ ... (eq. \ 4)
\]
Merk op dat ik de horizontale en verticale componenten van \ (v_ grijs \) heb afgeleid in \ (v_ gray_x \) en \ (v_ gray_y \). Hetzelfde geldt voor de snelheid van de rode cirkel; bekijk deze snelle tip om te weten hoe deze componenten zijn afgeleid.
We missen nog steeds één relatie om al deze vergelijkingen samen te binden. Laten we de afbeelding hieronder bekijken.
De andere relatie gaat terug naar Pythagoras. Wanneer beide cirkels elkaar ontmoeten, is de afstand tussen beide middelpunten precies \ (rad_ grijs \) plus \ (rad_ red \).
\ [
Pythagoras '\ Theorem, \ z ^ 2 = x ^ 2 + y ^ 2 \\
(Rad_ grijze + rad_ red) ^ 2 = (x '_ grijze -x' _ red) ^ 2 + (y '_ grijze -y' _ red) ^ 2 \ ... (eq. \ 5) \\
\]
Hier vervang je vergelijkingen 1 ~ 4 in vergelijking 5. Ik begrijp het wiskundig nogal lastig, dus ik heb het gescheiden in stap 13. Je kunt het overslaan om bij stap 14 bij het resultaat te komen..
Eerst stellen we de volgende identiteiten vast.
\ [
Identiteit,\\
(a + b) ^ 2 = a ^ 2 + 2ab + b ^ 2 \ ... (id. \ 1) \\
(a-b) ^ 2 = a ^ 2-2ab + b ^ 2 \ ... (id. \ 2) \\
\]
Houd er altijd rekening mee dat alle wiskundige symbolen een constante vertegenwoordigen, behalve voor de tijd, \ (t \), die het onderwerp van interesse is.
\ (x_ grijs, \ v_ gray_x, \ y_ red, \) enzovoort worden alle in het scenario gedefinieerd.
Vervolgens proberen we ons probleem op de volgende termijn te verbreken:
\ [
(Rad_ grijze + rad_ red) ^ 2 = (x '_ grijze -x' _ red) ^ 2 + (y '_ grijze -y' _ red) ^ 2 \ \
Overweeg \ term \ (x '_ gray -x' _ red) ^ 2 \ en \ utilizing \ id. \ 2 \\
(x '_ gray -x' _ red) ^ 2 = (x '_ gray) ^ 2-2 (x' _ gray) (x '_ red) + (x' _ red) ^ 2 \\
\]
\ [
Overweeg \ termijn \ (x '_ grijs) ^ 2 \\
(X '_ grijs) ^ 2 \\
= (x_ grijs + v_ gray_x * t) ^ 2, \ gebruik \ id. \ 1 \\
= (X_ grijze ) ^ 2 + 2 (x_ grijze ) (v_ gray_x * t) + (V_ gray_x * t) ^ 2
\]
\ [
Overweeg \ termijn \ -2 (x '_ grijs) (x' _ rood) \\
-2 (x '_ grijs) (x' _ red) \\
= -2 (x_ grijze + v_ gray_x * t) (x_ red + v_ red_x * t) \\
= -2 [(x_ grijs) (x_ red) + (x_ grijze ) (v_ red_x * t) + (V_ gray_x * t) (x_ red) + (V_ gray_x * t) (V_ red_x * t)] \\
= -2 (x_ grijs) (x_ red) - 2 (x_ grijze ) (v_ red_x * t) -2 (V_ gray_x * t) (x_ red) - 2 ( V_ gray_x * t) (V_ red_x * t)
\]
\ [
Overweeg \ term \ (x '_ red) ^ 2 \\
(X '_ red) ^ 2 \\
= (x_ red + v_ red_x * t) ^ 2, \ gebruikt \ id. \ 1 \\
= (X_ rode) ^ 2 + 2 (x_ red) (v_ red_x * t) + (V_ red_x * t) ^ 2
\]
Nu in één oogopslag kunnen we gemakkelijk zien dat de hoogste macht van \ (t \) 2 is. Dus we hebben zelf een kwadratische vergelijking. Laten we alle coëfficiënten verzamelen die door deze drie termen worden bijgedragen op basis van hun bevoegdheden.
\ (T ^ 2 \) | \ (T \) | \ (T ^ 0 = 1 \) |
\ ((V_ gray_x ) ^ 2 \) | \ (2 (x_ grijze ) (v_ gray_x ) \) | \ ((X_ grijze ) ^ 2 \) |
\ (- 2 (v_ gray_x ) (v_ red_x ) \) | \ (- 2 (x_ grijs) (V_ red_x) - 2 (V_ gray_x) (x_ rode) \) | \ (- 2 (x_ grijs) (x_ rode) \) |
\ ((V_ red_x ) ^ 2 \) | \ (2 (x_ rode) (V_ red_x) \) | \ ((X_ rode) ^ 2 \) |
Laten we de coëfficiënten analyseren met \ (t ^ 2 \) en \ (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_ grijs) ^ 2-2 (x_ grijs) (x_ rood) + (x_ rood) ^ 2, \ recall \ id. \ 2 \\
(x_ grijs) ^ 2-2 (x_ grijs) (x_ rood) + (x_ rood) ^ 2 = (x_ grijs -x_ rood) ^ 2
\]
En dat van \ (t \).
\ [
Makkelijker maken\\
a = (x_ grijs), \ b = (v_ gray_x) \\
c = (v_ red_x), \ d = (x_ red) \\
2AB-2AC-2BD + 2dc \\
2 = [ab-ac-dc BD +] \\
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_ grijs -x_ red)
\]
Laten we samenvatten in termen van \ ((x '_ gray -x' _ red) ^ 2 \)
\ [
(X '_ grijs -x' _ red) ^ 2 \\
= (v_ gray_x -v_ red_x) ^ 2 * t ^ 2 + 2 (v_ gray_x -v_ red_x) (x_ gray -x_ red) * t + (x_ gray -x_ red) ^ 2
\]
Merk op dat dit slechts voor één term in \ (eq. \ 5 \) geschikt is. We zullen hetzelfde proces moeten uitvoeren voor een andere term \ ((y '_ gray -y' _ red) ^ 2 \). Omdat ze dezelfde algebraïsche vorm hebben, moet het resultaat ook hetzelfde zijn.
\ [
(Y '_ grijs -y' _ red) ^ 2 \\
= (v_ gray_y -v_ red_y) ^ 2 * t ^ 2 + 2 (v_ gray_y -v_ red_y) (y_ gray -y_ red) * t + (y_ grijs -y_ red) ^ 2
\]
Dus na herschikking in termen van \ (t \) zou \ (eq. \ 5 \) als volgt moeten zijn.
\ [
(Rad_ grijze + rad_ red) ^ 2 = (x '_ grijze -x' _ red) ^ 2 + (y '_ grijze -y' _ red) ^ 2 \ \
p = V_ gray_x -v_ red_x \\
q = x_ grijze -x_ red \\
r = V_ gray_y -v_ red_y \\
s = y_ grijs -y_ rode \\
(p ^ 2 + r ^ 2) * t ^ 2 + 2 (pq + rs) * t + (q ^ 2 + s ^ 2- (rad_ grijs + rad_ red) ^ 2) = 0
\]
Dus van de vorige stap, door middel van rigoureuze algebra kwamen we bij de volgende formule:
\ [
p = V_ gray_x -v_ red_x \\
q = x_ grijze -x_ red \\
r = V_ gray_y -v_ red_y \\
s = y_ grijs -y_ rode \\
(p ^ 2 + r ^ 2) * t ^ 2 + 2 (pq + rs) * t + (q ^ 2 + s ^ 2- (rad_ grijs + rad_ red) ^ 2) = 0
\]
Dit is nu een enorme kwadratische formule. We zullen proberen om de coëfficiënten te groeperen in die geaccepteerd door EqQuadratic
. Vergelijk de twee vormen:
\ [
ax ^ 2 + bx + c = 0 \\
(p ^ 2 + r ^ 2) * t ^ 2 + 2 (pq + rs) * t + (q ^ 2 + s ^ 2- (rad_ grijs + rad_ red) ^ 2) = 0 \\
a = p ^ 2 + r ^ 2) \\
b = 2 (pq + rs) \\
c = (q ^ 2 + s ^ 2- (rad_ grijs + rad_ red) ^ 2)
\]
Dus hier is een Flash-presentatie om het idee te demonstreren. Je ziet twee deeltjes op het podium, de ene grijs en de andere rood. Beide zijn verbonden met een pijl, die de grootte en richting van de snelheid aangeeft.
Om de snelheid van de deeltjes te wijzigen, drukt u op:
Om ten slotte de zichtbaarheid van de pijlen in te schakelen, drukt u op "V"
De kwadratische vergelijking heeft twee wortels. In deze context zijn we geïnteresseerd in de echte roots. Als de twee deeltjes echter niet op een aanvaringspad worden geplaatst (beide paden lopen parallel aan elkaar), dan worden imaginaire wortels geproduceerd in plaats van echte wortels. In dit geval blijven beide echte wortels behouden NaN
.
Als beide deeltjes op een aanvaringspad worden geplaatst, krijgen we twee echte wortels. Maar wat vertegenwoordigen deze twee wortels?
Herinner in stap 12 dat we de stelling van Pythagoras hebben gebruikt om te binden \ ((x '_ grijs, \ y' _ grijs) \) en \ ((x '_ rood, \ y' _ rood ) \) samen in een vergelijking. Welnu, er zijn twee situaties waarin de afstand tussen de middelpunten van twee cirkels precies de som is van beide radii: één voor botsing en één na botsing. Bekijk deze afbeelding:
Dus welke kiezen we? Uiteraard de eerste omdat we niet geïnteresseerd zijn in de instantie na een botsing. Dus we moeten altijd de mindere waarde van beide wortels kiezen en evalueren. Als de waarde positief is en minder dan 1, zal er een botsing plaatsvinden tijdens het volgende frame. Als de waarde negatief is, gebeurde de botsing in het verleden.
Laten we eens kijken naar de Actionscript geïmplementeerd voor dit voorbeeld. Eerst de variabelen.
// c1 is de grijze cirkel // c2 is de rode cirkel private var c1: Circle, c2: Circle; // v1 is de velocity van de grijze cirkel // v2 is de snelheid van de rode cirkel private var v1: Vector2D, v2: Vector2D, toggle: Boolean = true, usingV1: Boolean = true; // tri1 vormt de pijlpunt van de v1 // tri2 vormt de pijlpunt van de v2 private var tri1: Triangle, tri2: Triangle; private var container: Sprite; private var eq: EqQuadratic;
Dan de berekening van de wortels. U kunt de volgende ActionScript-code met de bovenstaande variabelen controleren.
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;
Hier is hoe je de wortels moet interpreteren:
// als er geen echte wortels beschikbaar zijn, staan ze niet op een aanvaringspad als (isNaN (roots [0]) && isNaN (roots [1])) t.text = "Particles not on collision path." else var time: Number = Math