Mathematik und ActionScript von Kurven Wurzeln

Im ersten Tutorial dieser Serie haben wir uns das Zeichnen von Kurven mit Gleichungen und AS3 angesehen. Nun werden wir uns mit dem Lösen dieser Gleichungen befassen, um die Wurzeln einer Kurve zu finden, dh die Stellen, an denen die Kurve eine gegebene Gerade kreuzt. Wir können dies verwenden, um Kollisionen mit gekrümmten Oberflächen vorherzusagen und "Tunneln" in Flash-Spielen zu vermeiden.


Schritt 1: Quadratische Wurzeln

Zuerst Zeit für eine schnelle mathematische Überarbeitung. In diesem Lernprogramm akzeptieren und wenden wir nur die Methoden an, die wir verwenden. Interessierte Leser können sich jedoch auf die Wikipedia-Seite zu quadratischen Gleichungen beziehen, um Informationen über die mathematischen Ableitungen zu erhalten.

\ (F (x) \) ist also eine quadratische Funktion. Wenn \ (f (x) \) äquivalent zu 0 ist, kann \ (x \) durch diese Formel erhalten werden:

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

\ (b ^ 2 - 4ac \) heißt das Diskriminant der Formel. Wenn die Diskriminante negativ ist, wird die Quadratwurzel der Diskriminante erzeugt imaginäre Wurzeln, was wir nicht planen können. Umgekehrt haben Sie, wenn die Diskriminierung positiv ist, reelle Zahlenwurzeln, und Sie können sie auf dem Bildschirm darstellen.


Schritt 2: Quadratische Wurzeln visualisieren

Was sind Wurzeln? In unserem Zusammenhang sind sie nichts weiter als Schnittpunkte zwischen der quadratischen Kurve und einer Linie. Nehmen wir zum Beispiel an, wir sind daran interessiert, den Schnittpunkt (die Schnittpunkte) der folgenden Gleichungen zu finden:

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

Dies ist ein typisches Szenario für die Suche nach dem Schnittpunkt (den Schnittpunkten) zwischen einer quadratischen Kurve und der x-Achse (da die x-Achse die Linie ist, wo) y == 0). Da der Schnittpunkt (die Schnittpunkte) definitionsgemäß von \ (f (x) \) und \ (g (x) \) geteilt wird, können wir schließen, dass \ (f (x) = g (x) \) für die Werte gilt von x nach dem wir suchen.

Es ist dann eine triviale Operation, bei der Sie einfach die Funktionen ersetzen und dann die Formel aus Schritt 1 anwenden, um die Wurzeln zu erhalten. Nun gibt es mehrere Möglichkeiten, die wir vorhersehen können, wie unten gezeigt.

(Wie Sie sehen können, bedeutet "imaginäre Wurzeln" für unsere Zwecke, dass die Kurve niemals die x-Achse kreuzt.)

Betrachten wir nun den Fall, in dem \ (g (x) \) mehr als nur eine banale horizontale Linie ist. Nehmen wir an, es ist eine geneigte Linie, \ (g (x) \ = \ mx \ + \ d \). Wenn wir nun beide Funktionen gleichsetzen, müssen wir eine kleine Vorberechnung durchführen, bevor die Formel effektiv angewendet werden kann.

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

Im Folgenden finden Sie eine interaktive Flash-Präsentation, in der Sie die roten und blauen Punkte ziehen können. Gelbe Punkte zeigen die Schnittpunkte an. Sie müssen möglicherweise die Kurve und die Linie so positionieren, dass sie sich schneiden, damit die gelben Punkte angezeigt werden.


Schritt 3: Zeichnen Sie dies mit ActionScript

Das vollständige Skript finden Sie in Demo1.as; hier erkläre ich nur einen entscheidenden Auszug aus dem Code. Schauen wir uns die AS3 an, um die Kurve und die Linie zu zeichnen:

 private Funktion neu zeichnen (): void var cmd: Vector. = neuer Vektor.; var coord: Vektor. = neuer Vektor.; // Kurve neu zeichnen; m1 = new Matrix3d ​​(Kurvenpunkte [0] .x * Kurvenpunkte [0] .x, Kurvenpunkte [0] .x, 1, 0, Kurvenpunkte [1] .x * Kurvenpunkte [1] .x, Kurvenpunkte [1] .x , 1, 0, Kurvenpunkte [2] .x * Kurvenpunkte [2] .x, Kurvenpunkte [2] .x, 1, 0, 0,0,0,1); m2 = new Matrix3d ​​(kurvenpunkte [0] .y, 0, 0, 0, kurvenpunkte [1] .y, 0, 0, 0, kurvenpunkte [2] .y, 0, 0, 0, 0,0,0, 1) m1.invert (); m2.append (m1); quadratische_Equation.definiert (m2.n11, m2.n21, m2.n31); für (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); 

Der Großteil von ActionScript zum Zeichnen der Kurve aus den Zeilen 80 ~ 104 ist weitgehend aus dem vorherigen Lernprogramm entlehnt. Daher werde ich nur etwas zum Code zum Zeichnen einer Linie erklären.

In der Flash-Präsentation oben gibt es zwei interaktive blaue Punkte. Jeder von ihnen hat Koordinaten, und mit beiden Punkten wird eine Linie gebildet. Da beide Punkte auf derselben Linie liegen, haben sie eine gemeinsame Steigung und einen y-Achsenabschnitt, um eine allgemeine Liniengleichung zu bilden:

\ [
y \ = \ mx \ + \ d, \\
m \ = \ Steigung, \ d \ = \ y-Achsenabschnitt
\]

Wir können eine kleine Algebra verwenden, um nach den beiden Unbekannten \ (m \) und \ (d \) zu suchen. Geben Sie die Koordinaten der beiden blauen Punkte als \ ((x_1, \ y_1) \) und \ ((x_2, \ y_2) \) an:

[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 =
ich
\ begin bmatrix m \\ d \ end bmatrix
[/Latex]

(Beachten Sie, dass sich eine Matrix mit hochgestelltem -1 auf das Inverse dieser Matrix bezieht.)

Daher werden \ (m \) und \ (d \) berechnet. Wir können jetzt die Linie zeichnen, indem wir die Koordinaten \ ((0, y_3) \) und \ ((stage.stageWidth, y_4) \) verbinden. Wie finden Sie \ (y_3 \) und \ (y_4 \)? Nun, da \ (m \), \ (x \) und \ (d \) bekannt sind, können wir einfach alle diese Werte in die allgemeine Gleichung der Zeile einfügen,

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

… Um diese \ (y \) s zu erhalten.


Schritt 4: Berechnen Sie die Quadratwurzeln

Zur Berechnung der Position der Schnittpunkte verwenden wir die Formel aus Schritt 1. Dies erfolgt in EqQuadratic.as wie die unten gezeigten Funktionen:

 / ** Schreibgeschützt * Diskriminanz der Gleichung * / public function diskriminant (): Number // B * B-4 * A * C return _B * _B - 4 * _A * _C;  / ** * Führt eine Berechnung durch, um die Wurzeln zu erhalten < 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);  

Weitere Details von EqQuadratic.as:

Funktion Art Eingabeparameter Funktionalität
EqQuadratisch Methode Null Klassenkonstruktor
definieren Methode Koeffizienten a, b und c der quadratischen Gleichung Instanziere die Koeffizientenwerte
fx_of Methode Wert von x Gibt \ (f (x) \) der angegebenen \ (x \) Eingabe zurück.
calcRoots Methode Null Führt eine Berechnung durch, um eine quadratische Wurzel zu erhalten
diff1 Methode \ (x \) Koordinate für die Differenzierung ersten Grades Differenziertes \ (f (x) \) gegebener \ (x \) im ersten Grad.
diff2 Methode \ (x \) Koordinate für die Differenzierung zweiten Grades Differenziertes \ (f (x) \) gegebener \ (x \) im zweiten Grad.
diskriminieren Eigenschaft, nur lesbar Null Gibt den Wert der Diskriminante zurück, \ (b ^ 2 - 4ac \)
roots_R Eigenschaft, nur lesbar Null Gibt einen Zahlvektor für Wurzeln einer reellen Zahl zurück. Elemente sind NaN, wenn keine echten Wurzeln existieren.
roots_i Eigenschaft, nur lesbar Null Gibt einen String-Vektor für Wurzeln einer imaginären Zahl zurück. Elemente sind null, wenn keine imaginären Wurzeln vorhanden sind.

Schritt 5: Zeichnen Sie dies mit ActionScript

Ein Beispiel dafür EqQuadratic.as ist in Demo1.as. Nach der Initiation von EqQuadratisch, Wir werden es verwenden, um die Wurzeln zu berechnen. Nachdem wir das Vorhandensein echter Wurzeln bestätigt haben, werden wir sie verwenden, um die gelben Punkte zu zeichnen.

Jetzt beziehen sich die Wurzeln nur auf die \ (x \) -Komponente der Koordinaten. Um die \ (y \) s zu erhalten, raten Sie mal was? Wieder setzen wir die Werte von \ (m \), \ (d \) (früher in Schritt 3 berechnet) und \ (x \) (von den Wurzeln) in die allgemeine Gleichung der Zeile, \ (y \ = \ mx \ + \ d \). Überprüfen Sie den entsprechenden Code in Zeile 135 und 136.

 private Funktion recalculate_reposition (): void quadratische_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); Var-Wurzeln: Vektor. = quadratische_Equation.roots_R; if (! isNaN (wurzeln [0]) &&! isNaN (wurzeln [1])) intersec_points [0] .x = wurzeln [0]; intersec_points [0] .y = n2.a * root [0] + n2.b intersec_points [1] .x = root [1]; intersec_points [1] .y = n2.a * wurzeln [1] + n2.b else intersec_points [0] .x = -100; intersec_points [0] .y = -100; intersec_points [1] .x = -100; intersec_points [1] .y = -100; 

Schritt 6: Kubikwurzeln

Kubische Wurzeln sind nicht überraschend die Schnittpunkte zwischen a kubisch Kurve und eine Linie. Eine kubische Kurve unterscheidet sich jedoch ein wenig von einer quadratischen Kurve, und in dieser Hinsicht sind die Möglichkeiten, wo sich Kreuzungen befinden könnten, unterschiedlich.

Das Bild unten zeigt eine kubische Kurve, die sich mit der x-Achse schneidet:

Hier noch einmal eine kleine Flash-Präsentation, mit der Sie experimentieren können. Rote und blaue Punkte können gezogen werden, während die gelben Punkte nur die Schnittpunkte anzeigen.


Schritt 7: Allgemeine Formel für kubische Wurzeln

Die allgemeine Formel zum Finden einer kubischen Kurve wurde von Cardano entdeckt. Obwohl ich versucht bin, auf die Details einzugehen, möchte ich interessierte Leser auf die folgenden Links verweisen:

  • Wikipedia
  • Wolfram und
  • Eine weitere benutzerfreundliche PDF-Datei.

Wie auch immer, die EqCubic.as class implementiert diese Formel, um die Wurzeln kubischer Funktionen zusammen mit anderen mathematischen Hilfsfunktionen aufzulösen. Im Allgemeinen alle Attribute und Methoden für EqCubic.as Folgen Sie der Beschreibung in Schritt 4, da beide Klassen EqQuadratic.as und EqCubic.as eine gemeinsame Schnittstelle implementieren, IEquation.as, mit Ausnahme der unten aufgeführten Details.

Funktion Unterschied
definieren Insgesamt vier Koeffizienten (a, b, c, d) zur Eingabe der kubischen Gleichung; gerade drei für quadratische Gleichung.
roots_R, root_i Die Summe der reellen und imaginären Wurzeln ist drei für eine kubische Gleichung, aber zwei für eine quadratische Gleichung.

Schritt 8: Zeichnen Sie dies mit ActionScript

Hier ist die Actionscript-Implementierung für die Flash-Präsentation aus Schritt 5. Der vollständige Code steht in Demo3.as.

 private Funktion neu zeichnen (): void var cmd: Vector. = neuer Vektor.; var coord: Vektor. = neuer Vektor.; // Kurve neu zeichnen; m1 = neue Matrix3d ​​(Kurvenpunkte [0] .x * Kurvenpunkte [0] .x * Kurvenpunkte [0] .x, Kurvenpunkte [0] .x * Kurvenpunkte [0] .x, Kurvenpunkte [0] .x, 1, Kurvenpunkte) [1] .x * Kurvenpunkte [1] .x * Kurvenpunkte [1] .x, Kurvenpunkte [1] .x * Kurvenpunkte [1] .x, Kurvenpunkte [1] .x, 1, Kurvenpunkte [2] .x * Kurvenpunkte [2] .x * Kurvenpunkte [2] .x, Kurvenpunkte [2] .x * Kurvenpunkte [2] .x, Kurvenpunkte [2] .x, 1, Kurvenpunkte [3] .x * Kurvenpunkte [3] .x * Kurvenpunkte [3] .x, Kurvenpunkte [3] .x * Kurvenpunkte [3] .x, Kurvenpunkte [3] .x, 1); m2 = new Matrix3d ​​(kurvenpunkte [0] .y, 0, 0, 0, kurvenpunkte [1] .y, 0, 0, 0, kurvenpunkte [2] .y, 0, 0, 0, kurvenpunkte [3] .y 0, 0, 0) m1.invert (); m2.append (m1); cubic_equation.define (m2.n11, m2.n21, m2.n31, m2.n41); für (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); 

Die ActionScript-Befehle zum Zeichnen einer kubischen Kurve sind genau die gleichen wie in meinem vorherigen Artikel beschrieben, wohingegen ActionScript-Befehle zum Zeichnen der Linie bereits in Schritt 3 dieses Befehls erläutert werden.

Lassen Sie uns nun die kubischen Wurzeln berechnen und positionieren:

 private Funktion recalculate_reposition (): void cubic_equation.define (m2.n11, m2.n21, m2.n31 - n2.a, m2.n41 - n2.b); cubic_equation.calcRoots (); Var-Wurzeln: Vektor. = cubic_equation.roots_R; für (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;   

Nach dem Instantiieren cubic_equation Im Konstruktor definieren wir seine Koeffizienten, berechnen die Wurzeln und speichern die Wurzeln in einer Variablen.

Eine kleine Anmerkung zu den Wurzeln: Es gibt maximal drei echte Wurzeln für eine kubische Gleichung, aber nicht alle realen Wurzeln sind in jeder Situation vorhanden, da einige Wurzeln imaginär sein können. Was passiert also, wenn es zum Beispiel nur eine echte Wurzel gibt? Nun, eines der Wurzelreihen, die von aufgerufen werden cubic_equation.roots_R wird eine reelle Zahl sein, während alle anderen keine Zahl sein werden (NaN). Sehen Sie sich dazu das hervorgehobene ActionScript an.


Schritt 9: Voraussagen, wo ein Objekt mit der gekrümmten Oberfläche kollidieren wird

Eine große Anwendung zum Berechnen von Wurzeln ist das Projizieren eines Kollisionspunkts auf eine gekrümmte Oberfläche, wie unten gezeigt. Verwenden Sie die linke und rechte Pfeiltaste, um das sich bewegende Schiff zu steuern, und drücken Sie zum Beschleunigen nach oben. Sie werden bemerken, dass Kollisionspunkte, die in der Vergangenheit passiert wären, leicht abgeblendet sind.


Schritt 10: Implementierung

Die Idee ähnelt der in meinem Tutorial zur Vorhersage von Kollisionspunkten. Anstatt mit einer geraden Linie zu kollidieren, verwenden wir jetzt eine gebogen Linie. Schauen wir uns den Code an.

Das folgende Snippet wird jeden Frame genannt:

 Aktualisierung der privaten Funktion (e: Event): void // Nach links und rechts lenken, wenn (Steuerung == 1) velo = velo.rotate (Math2.radianOf (-5)); sonst if (control == 2) velo = velo.rotate (Math2.radianOf (5)); // Geschwindigkeit manipulieren var currVelo: Number = velo.getMagnitude (); if (Zunahme == 0) currVelo - = 0,5; currVelo = Math.max (currVelo, 1); // untere Geschwindigkeitsbegrenzung else if (Zunahme == 1) currVelo + = 0,5; currVelo = Math.min (currVelo, 5); // obere Grenze für die Geschwindigkeit velo.setMagnitude (currVelo); // Aktualisierungsgeschwindigkeit ship.x + = velo.x; ship.y + = velo.y; ship.rotation = Math2 ° Grad (velo.getAngle ()); // darüber nachdenken, wann das Schiff nicht in Betrieb ist if (ship.x <0 || ship.x > stage.stageWidth) velo.x * = -1; wenn (ship.y <0 || ship.y > stage.stageHeight) velo.y * = -1; neu zeichnen (); neu berechnen (); 

Der Kerncode liegt in neu zeichnen und neu berechnen. Mal sehen, was drin ist neu zeichnen. Es ist das gleiche, das wir in früheren Demos verwendet hatten. Eine kleine Notiz zum Zeichnen der Linie. In früheren Demos haben wir gesehen, dass zwei Punkte benötigt werden, um die Gleichung zu erhalten. Hier haben wir nur ein Schiff. Um den zweiten Punkt zu erhalten, addieren Sie einfach die Geschwindigkeit des Schiffes zu seiner aktuellen Position. Ich habe den Code für die Bequemlichkeit hervorgehoben.

 private Funktion neu zeichnen (): void var cmd: Vector. = neuer Vektor.; var coord: Vektor. = neuer Vektor.; // Kurve neu zeichnen; m1 = neue 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); quadratische_Equation.definiert (m2.n11, m2.n21, m2.n31); minX = Math.min (w1.x, w2.x, w3.x); maxX = Math.max (w1.x, w2.x, w3.x); für (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); 

Jetzt für neu berechnen, Ich habe eine kleine Vektorberechnung durchgeführt, um zu überprüfen, ob der Punkt hinter oder vor dem Schiff liegt. Überprüfen Sie den hervorgehobenen Code:

 private Funktion recalculate (): void quadratisch_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); Var-Wurzeln: Vektor. = quadratische_Equation.roots_R; für (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 && Wurzeln [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;   

Schritt 11: Zeitbasierte Kollisionserkennung

Eine andere großartige Anwendung ist nicht so offensichtlich wie die erste. Um eine genauere Kollisionserkennung durchzuführen, anstatt unsere Schlussfolgerung auf die Entfernung zwischen zwei Objekten zu stützen, werden wir dies tun Zeit zu wirken. Warum? Weil "Tunneln" passieren kann, wenn die entfernungsbasierte Kollisionserkennung verwendet wird:

Betrachten Sie einen Kollisionserkennungsalgorithmus, der auf der Entfernung für zwei Kreise basiert. Von den vier gezeigten Frames hat nur Frame 2.15 erfolgreich eine Kollision zwischen zwei Kreisen erkannt. Warum? Weil der aktuelle Abstand zwischen den Mittelpunkten des grauen und des roten Kreises geringer ist als die Summe der Radien beider Kreise.

(Leser, die sich für weitere Details zu diesem Thema interessieren, können auf diesen Artikel verweisen.)

\ [Abstand \ zwischen \ Kreis \

Das Problem wird dadurch verursacht, dass Flash jeweils nur um ein einzelnes Bild fortgesetzt wird. Dies bedeutet, dass nur die Bilder 1, 2 und 3 erfolgreich erfasst werden und nicht die Momente zwischen den Momentaufnahmen. Nun kollidierten die grauen und roten Kreise in diesen Frames nicht gemäß einer entfernungsbasierten Berechnung, so dass der rote Kreis durch den grauen Tunnel hindurchführt!

Um dies zu beheben, benötigen wir eine Möglichkeit, die aufgetretene Kollision zu sehen zwischen Bilder 2 und 3. Wir müssen die Zeit berechnen, um zwischen zwei Kreisen zu wirken. Wenn wir zum Beispiel überprüfen, dass die Zeit bis zum Aufprall bei Bild 2 weniger als 1 Bild beträgt, bedeutet dies, dass bei einem Flash-Vorgang 1 Bildvorwärtskollision oder sogar Tunneln definitiv stattgefunden hat.

\ [wenn \ Zeit \ auf \ Auswirkungen, \ t, \ erfüllt \ 0 \

Die Frage ist, wie wir diese Zeit berechnen?


Schritt 12: Vorberechnungen

Ich werde versuchen, meine Methode so einfach wie möglich zu zeigen.

In diesem Szenario befinden sich die beiden grauen und roten Kreise derzeit unter \ ((x_ grau, \ y_ grau) \) und \ ((x_ rot, \ y_ rot) \). Sie bewegen sich jeweils um \ (v_ grau \) und \ (v_ rot \) und setzen einen Kollisionspfad. Wir sind daran interessiert, die benötigte Zeit zu berechnen, \ (t \), um die Positionen zu erreichen \ ((x '_ grau, \ y' _ grau) \) und \ ((x '_ rot) , \ y '_ red) \), angezeigt durch die durchscheinenden grauen und roten Kreise, in denen die Kollision stattgefunden hat.

\ [
Verdrängung_ Zukunft = Verschiebung_ Gegenwart + Geschwindigkeit * Zeit \\
x '_ grau = x_ grau + v_ gray_x * t \… (Gleichung \ 1) \\
y '_ gray = y_ gray + v_ gray_y * t \… (Gleichung \ 2) \\
x '_ red = x_ red + v_ red_x * t \… (Eq. \ 3) \\
y '_ red = y_ red + v_ red_y * t \… (Äq. \ 4)
\]

Beachten Sie, dass ich die horizontalen und vertikalen Komponenten von \ (v_ gray \) in \ (v_ gray_x \) und \ (v_ gray_y \) abgeleitet habe. Gleiches gilt für die Geschwindigkeit des roten Kreises. Sehen Sie sich diesen Quick Tip an, um zu erfahren, wie diese Komponenten abgeleitet werden.

Es fehlt immer noch eine Beziehung, um all diese Gleichungen miteinander zu verbinden. Lassen Sie uns das Bild unten sehen.

Die andere Beziehung geht auf Pythagoras zurück. Wenn sich beide Kreise treffen, ist der Abstand zwischen beiden Zentren genau \ (rad_ gray \) plus \ (rad_ red \).

\ [
Pythagoras 'Satz, \ z ^ 2 = x ^ 2 + y ^ 2 \\
(rad_ grau + rad_ rot) ^ 2 = (x '_ grau -x' _ rot) ^ 2+ (y '_ grau -y' _ rot) ^ 2 \ … (Gl. \ 5) \\
\]

Hier setzen Sie die Gleichungen 1 bis 4 in die Gleichung 5 ein. Ich verstehe das mathematisch ziemlich beängstigend, also habe ich es in Schritt 13 getrennt. Fühlen Sie sich frei, um es zu überspringen, um zu dem Ergebnis in Schritt 14 zu gelangen.


Schritt 13 (optional): Mathematische Rigor

Zunächst stellen wir die folgenden Identitäten fest.

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

Denken Sie immer daran, dass alle mathematischen Symbole eine Konstante darstellen, mit Ausnahme der Zeit \ (t \), die Gegenstand von Interesse ist.

\ (x_ grau, \ v_ grau_x, \ y_ rot, \) usw. sind alle im Szenario definiert.

Als Nächstes versuchen wir, unser Problem Begriff für Begriff aufzulösen:

\ [
(rad_ grau + rad_ rot) ^ 2 = (x '_ grau -x' _ rot) ^ 2+ (y '_ grau -y' _ rot) ^ 2 \ \
Betrachten Sie \ term \ (x '_ grey -x' _ red) ^ 2 \ und \ using \ id. \ 2 \\
(x '_ grau -x' _ rot) ^ 2 = (x '_ grau) ^ 2-2 (x' _ grau) (x '_ rot) + (x' _ red) ^ 2 \\
\]
\ [
Betrachten Sie \ term \ (x '_ gray) ^ 2 \\
(x '_ grau) ^ 2 \\
= (x_ grau + v_ grau_x * t) ^ 2, \ use \ id. \ 1 \\
= (x_ grau) ^ 2 + 2 (x_ grau) (v_ gray_x * t) + (v_ gray_x * t) ^ 2
\]
\ [
Betrachten Sie \ term \ -2 (x '_ gray) (x' _ red) \\
-2 (x '_ grau) (x' _ rot) \\
= -2 (x_ grau + v_ grau_x * t) (x_ rot + v_ rot_x * t) \\
= -2 [(x_ grau) (x_ rot) + (x_ grau) (v_ red_x * t) + (v_ gray_x * t) (x_ rot) + (v_ gray_x * t) (v_ red_x * t)] \\
= -2 (x_ grau) (x_ rot) - 2 (x_ grau) (v_ red_x * t) -2 (v_ gray_x * t) (x_ rot) - 2 ( v_ gray_x * t) (v_ red_x * t)
\]
\ [
Betrachten Sie \ term \ (x '_ red) ^ 2 \\
(x '_ red) ^ 2 \\
= (x_ red + v_ red_x * t) ^ 2, \ use \ id. \ 1 \\
= (x_ red) ^ 2 + 2 (x_ red) (v_ red_x * t) + (v_ red_x * t) ^ 2
\]

Auf einen Blick können wir leicht erkennen, dass die höchste Potenz von \ (t \) 2 ist. Wir haben also eine quadratische Gleichung. Lassen Sie uns alle Koeffizienten zusammenstellen, die von diesen drei Begriffen nach ihren Stärken beigetragen werden.

\ (t ^ 2 \) \ (t \) \ (t ^ 0 = 1 \)
\ ((v_ gray_x) ^ 2 \) \ (2 (x_ grau) (v_ grau_x) \) \ ((x_ grau) ^ 2 \)
\ (- 2 (v_ gray_x) (v_ red_x) \) \ (- 2 (x_ grau) (v_ red_x) - 2 (v_ gray_x) (x_ rot) \) \ (- 2 (x_ grau) (x_ rot) \)
\ ((v_ red_x) ^ 2 \) \ (2 (x_ red) (v_ red_x) \) \ ((x_ red) ^ 2 \)

Analysieren wir die Koeffizienten mit \ (t ^ 2 \) und \ (t ^ 0 \).

\ [
(v_ gray_x) ^ 2-2 (v_ gray_x) (v_ red_x) + (v_ red_x) ^ 2, \ call \ 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_ grau) ^ 2-2 (x_ grau) (x_ rot) + (x_ rot) ^ 2, \ r \ u00fcck \ id. \ 2 \\
(x_ grau) ^ 2-2 (x_ grau) (x_ rot) + (x_ rot) ^ 2 = (x_ grau -x_ rot) ^ 2
\]

Und das von \ (t \).

\ [
Vereinfachen\\
a = (x_ grau), \ b = (v_ gray_x) \\
c = (v_ red_x), \ d = (x_ red) \\
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_ grau_x -v_ rot_x) (x_ grau -x_ rot)
\]

Lassen Sie uns zusammen mit \ ((x '_ grey -x' _ red) ^ 2 \) zusammenfassen.

\ [
(x '_ grau -x' _ rot) ^ 2 \\
= (v_ gray_x -v_ red_x) ^ 2 * t ^ 2 + 2 (v_ gray_x -v_ red_x) (x_ grau -x_ rot) * t + (x_ grau) -x_ rot) ^ 2
\]

Beachten Sie, dass dies nur einen Begriff in \ (Gl. \ 5 \) berücksichtigt. Wir müssen den gleichen Vorgang für einen anderen Begriff \ ((y '_ grey -y' _ red) ^ 2 \) ausführen. Da sie dieselbe algebraische Form haben, sollte das Ergebnis auch das gleiche sein.
\ [
(y '_ grau -y' _ rot) ^ 2 \\
= (v_ gray_y -v_ red_y) ^ 2 * t ^ 2 + 2 (v_ gray_y -v_ red_y) (y_ grau -y_ rot) * t + (y_ grau) -y_ rot) ^ 2
\]

Nach der Umlagerung in Form von \ (t \) sollte \ (Gl. \ 5 \) wie folgt sein.

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


Schritt 14: Das Ergebnis

Aus dem vorherigen Schritt sind wir durch rigorose Algebra zu folgender Formel gelangt:

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

Das ist jetzt eine riesige quadratische Formel. Wir werden versuchen, die Koeffizienten in die von akzeptierten zu gruppieren EqQuadratisch. Vergleichen Sie die beiden Formulare:

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


Schritt 15: Beispielimplementierung

Hier ist eine Flash-Präsentation, um die Idee zu demonstrieren. Sie werden zwei Partikel auf der Bühne sehen, einen grauen und einen roten. Beide sind mit einem Pfeil verbunden, der die Größe und Richtung der Geschwindigkeit anzeigt.

  • Klicken Sie auf die Schaltfläche "Weiter", um ein Bild in der Zeit fortzusetzen.
  • Klicken Sie auf die Schaltfläche "Zurück", um ein Bild zeitlich zurückzusetzen.

Um die Geschwindigkeit der Partikel zu ändern, drücken Sie:

  • "Up" und "Down" zum Erhöhen bzw. Verringern der Geschwindigkeit.
  • "Links" und "Rechts", um die Geschwindigkeit zu drehen.
  • "N", um den Kreis zu wechseln, den Sie steuern.

Um die Sichtbarkeit der Pfeile umzuschalten, drücken Sie "V".


Schritt 16: Eine Anmerkung zu den Quadratwurzeln

Die quadratische Gleichung hat zwei Wurzeln. In diesem Zusammenhang interessieren uns die wirklichen Wurzeln. Wenn sich die beiden Teilchen jedoch nicht auf dem Kollisionspfad befinden (beide Pfade verlaufen parallel zueinander), werden imaginäre Wurzeln anstelle von echten Wurzeln erzeugt. In diesem Fall bleiben beide echten Wurzeln NaN.

Wenn sich beide Teilchen auf einem Kollisionspfad befinden, erhalten wir zwei echte Wurzeln. Aber was repräsentieren diese beiden Wurzeln??

Man erinnere sich in Schritt 12, dass wir aus Pythagoras 'Satz Gebrauch machten, um \ ((x' _ grau, \ y '_ grau) \) und \ ((x' _ rot, \ y '_ rot) zu verbinden ) \) zusammen in eine Gleichung. Nun, es gibt zwei Situationen, in denen der Abstand zwischen den Mittelpunkten der Kreise genau die Summe der beiden Radien ist: eine vor der Kollision und eine nach der Kollision. Schauen Sie sich dieses Bild an:

Welches wählen wir? Natürlich der erste, weil wir uns nach der Kollision nicht für die Instanz interessieren. Wir sollten also immer den geringeren Wert beider Wurzeln wählen und bewerten. Ist der Wert positiv und kleiner als 1, kommt es während des nächsten Frames zu einer Kollision. Wenn der Wert negativ ist, ist die Kollision in der Vergangenheit passiert.


Schritt 17: Der ActionScript-Code

Schauen wir uns das für dieses Beispiel implementierte Actionscript an. Zuerst die Variablen.

 // c1 ist der graue Kreis // c2 ist der rote Kreis private var c1: Kreis, c2: Kreis; // v1 ist die Geschwindigkeit des grauen Kreises // v2 ist die Geschwindigkeit des roten Kreises private var v1: Vector2D, v2: Vector2D, Toggle: Boolean = true, usingV1: Boolean = true; // tri1 bildet die Pfeilspitze von v1 // tri2 bildet die Pfeilspitze von v2 private var tri1: Dreieck, tri2: Dreieck; Privater Var-Container: Sprite; private var Eq: EqQuadratic;

Dann die Berechnung der Wurzeln. Sie können den folgenden ActionScript-Code mit de