Mathematik und ActionScript von Kurven Farbverläufe und Normalen

Wir haben das Zeichnen von Kurven und das Finden ihrer quadratischen und kubischen Wurzeln sowie praktische Anwendungen für die Verwendung von quadratischen Wurzeln in Spielen in Angriff genommen. Jetzt, wie versprochen, schauen wir uns die Anträge zum Finden an kubisch Wurzeln sowie die Farbverläufe und Normalen von Kurven, z. B. Objekte von gekrümmten Flächen abprallen zu lassen. Lass uns gehen!


Beispiel

Werfen wir einen Blick auf eine praktische Anwendung dieser Mathematik:

In dieser Demo springt das "Schiff" von den Kanten der SWF und der Kurve ab. Der gelbe Punkt steht für den nächsten Punkt des Schiffes, der auf der Kurve liegt. Sie können die Form der Kurve durch Ziehen der roten Punkte anpassen und die Bewegung des Schiffes mit den Pfeiltasten anpassen.


Schritt 1: Kürzeste Entfernung zu einer Kurve

Betrachten wir das Szenario, in dem sich ein Punkt in der Nähe einer quadratischen Kurve befindet. Wie berechnen Sie den kürzesten Abstand zwischen dem Punkt und der Kurve??

Fangen wir mit dem Satz von Pythagoras an.

\ [
Sei \ der \ Punkt \ (x_p, \ y_p) \\
und \ den \ nächstgelegenen \ Punkt \ auf \ der \ Kurve \ (x_c, \ y_c) \\ aufrufen
Dann:\\
z ^ 2 = x ^ 2 + y ^ 2 \\
z ^ 2 = (x_c-x_p) ^ 2 + (y_c-y_p) ^ 2 \\
Gegeben \ y_c = ax_c ^ 2 + bx_c + c, \\
z ^ 2 = (x_c-x_p) ^ 2 + [(ax_c ^ 2 + bx_c + c) -y_p] ^ 2
\]

Sie können sehen, dass wir \ (y_c \) durch die quadratische Gleichung ersetzt haben. Auf einen Blick sehen wir, dass die höchste Leistung 4 ist. Wir haben also eine viertel Gleichung. Alles, was wir tun müssen, ist ein Minimum in dieser Gleichung zu finden, um uns die kürzeste Entfernung von einem Punkt zu einer quadratischen Kurve zu geben.

Aber vorher müssen wir Steigungen in einer Kurve verstehen ...


Schritt 2: Verlauf einer Kurve

Bevor wir uns mit dem Problem der Minimierung einer Quarzgleichung beschäftigen, versuchen wir, die Verläufe einer Kurve zu verstehen. Eine gerade Linie hat nur eine Steigung. Der Gradient einer quadratischen Kurve hängt jedoch davon ab, auf welchen Punkt der Kurve wir uns beziehen. Schauen Sie sich die Flash-Präsentation unten an:

Ziehen Sie die roten Punkte, um die quadratische Kurve zu ändern. Sie können auch mit dem Schieberegler spielen, um die Position des blauen Punkts entlang x zu ändern. Wenn sich der blaue Punkt ändert, wird auch der Verlauf gezeichnet.


Schritt 3: Gradient durch Kalkül

Dies ist, wo Kalkül nützlich sein wird. Sie haben möglicherweise vermutet, dass die Differenzierung einer quadratischen Gleichung den Gradienten der Kurve ergibt.

\ [
f (x) = ax ^ 2 + bx + c \\
\ frac df (x) dx = 2ax + b
\]

\ (\ Frac df (x) dx \) ist also der Gradient einer quadratischen Kurve und hängt von der \ (x \) - Koordinate ab. Gut, dass wir eine Methode haben, um damit umzugehen: diff1 (x: Zahl) gibt den Wert nach einer einzigen Differenzierung zurück.

Um den Gradienten zu zeichnen, benötigen wir eine Gleichung (\ (y = mx + c \)), um die Linie darzustellen. Die Koordinate des blauen Punktes \ ((x_p, y_p) \) wird in \ (x \) und \ (y \) eingesetzt, und der durch Differenzierung ermittelte Gradient der Linie wird in \ (m \) gehen. Somit kann der y-Achsenabschnitt der Linie \ (c \) durch Algebra-Arbeit berechnet werden.

Schauen Sie sich die AS3 an:

 var x: Number = s.value var y: Number = quadratische_Auswahl.fx_of (s.value) point.x = x; point.y = y; / ** * y = mx + c; * c = y - mx; <== use this to find c */ var m:Number = quadratic_equation.diff1(x); var c:Number = y - m * x; graphics.clear(); graphics.lineStyle(1, 0xff0000); graphics.moveTo(0, c); graphics.lineTo(stage.stageWidth, m * stage.stageWidth + c);

Schritt 4: Koordinatensysteme

Denken Sie immer an die umgekehrte y-Achse des Flash-Koordinatenraums, wie in der Abbildung unten gezeigt. Auf den ersten Blick erscheint das Diagramm auf der rechten Seite wie ein negativer Farbverlauf - aufgrund der invertierten Y-Achse ist es jedoch tatsächlich ein positiver Farbverlauf.

Dasselbe gilt für den unten angegebenen Mindestpunkt. Aufgrund der invertierten y-Achse sieht der minimale Punkt im kartesischen Koordinatenraum (bei (0,0)) im Flash-Koordinatenraum wie ein Maximum aus. Durch die Bezugnahme auf den Ursprungsort im Flash-Koordinatenraum relativ zur quadratischen Kurve ist dies jedoch ein Minimum.


Schritt 5: Änderungsrate für den Farbverlauf

Nehmen wir an, ich interessiere mich dafür, den tiefsten Punkt einer Kurve zu finden. Wie gehe ich vor? Schauen Sie sich das Bild unten an (beide Figuren befinden sich im selben Koordinatenraum).

Um den minimalen Punkt zu erhalten, setzen wir einfach \ (\ frac df (x) dx = 0 \), da wir per Definition nach dem Punkt suchen, an dem der Gradient Null ist. Wie oben gezeigt, stellt sich jedoch heraus, dass auch der Maximalpunkt einer Kurve diese Bedingung erfüllt. Wie unterscheiden wir zwischen diesen beiden Fällen??

Versuchen wir die Differenzierung des zweiten Grades. Es gibt uns die Änderungsrate des Gradienten.

\ [
\ frac df (x) dx = 2ax + b \\
\ frac df ^ 2 (x) dx ^ 2 = 2a
\]

Ich erkläre dies anhand des Bildes (gezeichnet im kartesischen Koordinatenraum). Wenn wir entlang der x-Achse inkrementieren, ändert sich der Gradient von negativ nach positiv. Die Änderungsrate sollte also a sein positiv Wert.

Wir können auch sehen, dass wenn \ (\ frac df ^ 2 (x) dx ^ 2 \) positiv ist, ein minimaler Punkt auf der Kurve vorhanden ist. Ist dagegen die Rate negativ, liegt ein maximaler Punkt vor.


Schritt 6: Zurück zum Problem

Nun können wir das in Schritt 1 dargestellte Problem lösen. Erinnern wir uns an die Quarzgleichung (mit dem höchsten Grad 4), die wir erreicht haben:

\ [
z ^ 2 = (x_c-x_p) ^ 2 + [(ax_c ^ 2 + bx_c + c) -y_p] ^ 2
\]

Dieselbe Quarzgleichung, aufgetragen

Denken Sie daran, dass wir daran interessiert sind, den minimalen Punkt auf dieser Kurve zu finden, da der entsprechende Punkt auf der ursprünglichen quadratischen Kurve der Punkt ist, der sich im minimalen Abstand vom roten Punkt befindet.

Unterscheiden wir also die Quartic-Funktion, um den Gradienten dieser Kurve zu erhalten, und setzen Sie den Gradienten dieser Quartic-Funktion mit Null gleich. Sie werden sehen, dass der Farbverlauf tatsächlich eine kubische Funktion ist. Ich werde interessierte Leser auf die Seite von Wolfram verweisen. Für dieses Tutorial werde ich nur das Ergebnis ihrer Algebra-Arbeit abholen:

\ [
\ frac d (z ^ 2) dx =
2 (x_c-x_p) + 2 (ax_c ^ 2 + bx_c + c - y_p) (2ax_c + b) \\
\ frac d (z ^ 2) dx = 2a ^ 2 (x_c) ^ 3 + 3ab (x_c) ^ 2 + (b ^ 2 + 2ac-2ay_p + 1) (x_c) + (bc-by_p-) x_p) \\
Gleiche \ Steigung \ zu \ 0 \\
\ frac d (z ^ 2) dx = 0 \\
2a ^ 2 (x_c) ^ 3 + 3ab (x_c) ^ 2 + (b ^ 2 + 2ac-2ay_p + 1) (x_c) + (bc-by_p-x_p) = 0 \\
Vergleiche \ mit \ cubic \ equation \\
Axe ^ 3 + Bx ^ 2 + Cx + D = 0 \\
A = 2a ^ 2 \\
B = 3ab \\
C = b ^ 2 + 2ac-2ay_p + 1 \\
D = bc-by_p-x_p
\]

Suchen Sie nach den Wurzeln dieser (eher unordentlichen) kubischen Funktion, und wir werden zu den Koordinaten der drei blauen Punkte kommen, wie oben angegeben.

Wie filtern wir unsere Ergebnisse nach dem Mindestpunkt? Man erinnere sich an den vorherigen Schritt, dass ein Mindestpunkt eine positive Änderungsrate hat. Um diese Änderungsrate zu erhalten, unterscheiden Sie die kubische Funktion, die den Gradienten darstellt. Wenn die Änderungsrate für den angegebenen blauen Punkt positiv ist, ist es einer von die minimalen Punkte. Bekommen das Wählen Sie den Punkt, an dem wir interessiert sind, den Punkt mit der höchsten Änderungsrate.


Schritt 7: Ausgabe der Ausgabe

Hier ist ein Beispiel für die Implementierung der oben erläuterten Idee. Sie können die roten Punkte ziehen, um Ihre quadratische Kurve anzupassen. Der blaue Punkt kann auch gezogen werden. Wenn Sie den blauen Punkt verschieben, wird der gelbe Punkt neu positioniert, so dass der Abstand zwischen den blauen und gelben Punkten zwischen allen Punkten der Kurve minimal ist.

Bei der Interaktion mit der Flash-Präsentation können drei gelbe Punkte gleichzeitig angezeigt werden. Zwei davon sind ausgeblendet und beziehen sich auf die aus der Berechnung erhaltenen Wurzeln, werden jedoch zurückgewiesen, da sie nicht die Punkte sind, die dem blauen Punkt auf der Kurve am nächsten liegen.


Schritt 8: ActionScript-Implementierung

Hier ist also die ActionScript-Implementierung des obigen. Das vollständige Skript finden Sie in Demo2.as.

Zuerst müssen wir die quadratische Kurve zeichnen. Beachten Sie, dass die Matrix m2 wird zur weiteren Berechnung herangezogen.

 private Funktion redraw_quadratic_curve (): 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));  graphics.clear(); graphics.lineStyle(1); graphics.drawPath(cmd, coord); 

Und hier ist derjenige, der das erläuterte mathematische Konzept implementiert. c1 bezieht sich auf einen Punkt, der zufällig auf der Bühne positioniert ist.

 private Funktion recalculate_distance (): void var a: Number = m2.n11; var b: Anzahl = m2.n21; var c: Anzahl = m2.n31; / * f (x) = Ax ^ 3 + Bx ^ 2 + Cx + D * / var A: Anzahl = 2 * a * a var B: Anzahl = 3 * b * a var C: Anzahl = b * b + 2 * c * a - 2 * a * c1.y +1 var D: Anzahl = c * b - b * c1.y - c1.x quartic_gradient = new EqCubic (); quartic_gradient.define (A, B, C, D); quartic_gradient.calcRoots (); roots = quartic_gradient.roots_R; var gewählt: Anzahl = Wurzeln [0]; if (! isNaN (Wurzeln [1]) &&! isNaN (Wurzeln [2])) // Steigung und Steigungsrate aller realen Wurzeln berechnen var quartic_rate: Vector. = neuer Vektor.; für (var i: int = 0; i < roots.length; i++)  if (!isNaN(roots[i])) quartic_rate.push(quartic_gradient.diff1(roots[i])); else roots.splice(i, 1);  //select the root that will produce the shortest distance for (var j:int = 1; j < roots.length; j++)  //the rate that corresponds with the root must be the highest positive value //because that will correspond with the minimum point if (quartic_rate[j] > quartic_rate [j - 1]) gewählt = Wurzeln [j];  // positioniere die zusätzlichen Wurzeln in der Demo position_extras ();  else // Entferne die zusätzlichen Wurzeln in der Demo kill_extras ();  intersec_points [0] .x = gewählt intersec_points [0] .y = quadratische_equation.fx_of (gewählt); 

Schritt 9: Beispiel: Kollisionserkennung

Verwenden wir dieses Konzept, um die Überlappung zwischen einem Kreis und einer Kurve zu erkennen.

Die Idee ist einfach: Wenn der Abstand zwischen dem blauen Punkt und dem gelben Punkt kleiner als der Radius des blauen Punkts ist, kommt es zu einer Kollision. Schauen Sie sich die Demo unten an. Die interaktiven Elemente sind die roten Punkte (zur Kontrolle der Kurve) und der blaue Punkt. Wenn der blaue Punkt mit der Kurve kollidiert, wird er etwas ausgeblendet.


Schritt 10: ActionScript-Implementierung

Nun, der Code ist ziemlich einfach. Überprüfen Sie die vollständige Quelle CollisionDetection.as.

 graphics.moveTo (intersec_points [0] .x, intersec_points [0] .y); graphics.lineTo (c1.x, c1.y); var distance: Number = Math2.Pythagoras (intersec_points [0] .x, intersec_points [0] .y, c1.x, c1.y) if (Abstand < c1.radius) c1.alpha = 0.5; else c1.alpha = 1.0; t.text = distance.toPrecision(3);

Schritt 11: Abprallen von der Kurve

Nun, da wir wissen, wann eine Kollision auftritt, versuchen wir, eine Kollisionsreaktion zu programmieren. Wie wäre es, wenn Sie von der Oberfläche abprallen? Schauen Sie sich die Flash-Präsentation unten an.

Sie können das Schiff (Dreiecksform) sehen, ist von einem Kreis (durchscheinend blau) umgeben. Sobald der Kreis mit der Kurve kollidiert, springt das Schiff von der Oberfläche ab.


Schritt 12: Steuern des Schiffes

Hier ist der ActionScript-Code zur Steuerung des Schiffes.

 öffentliche Funktion CollisionDetection2 () / ** * Instanziierung des Schiffes und seiner blauen Kreisfläche * / ship = new Triangle (); addChild (Schiff); ship.x = Math.random () * stage.stageWidth; ship.y = stage.stageHeight * 0,8; c1 = neuer Kreis (0x0000ff, 15); addChild (c1); c1 α = 0,2; / ** * Schiffsgeschwindigkeit * / velo = new Vector2D (0, -1); updateShip (); stage.addEventListener (KeyboardEvent.KEY_DOWN, handleKey); stage.addEventListener (KeyboardEvent.KEY_UP, handleKey); stage.addEventListener (Event.EXIT_FRAME, handleEnterFrame); / ** * Die Kurve und die Berechnungen * / quadratic_equation = new EqQuadratic (); curve_points = neuer Vektor.; füllen (curve_points, 0xff0000, 3); intersec_points = neuer Vektor.; populate (intersec_points, 0xffff00, 3, false); redraw_quadratic_curve ();  private Funktion handleKey (e: KeyboardEvent): void if (e.type == "keyDown") if (e.keyCode == Keyboard.UP) isUp = true; else if (e.keyCode == Keyboard.DOWN) isDown = true; if (e.keyCode == Keyboard.LEFT) isLeft = true; else if (e.keyCode == Keyboard.RIGHT) isRight = true;  if (e.type == "keyUp") wenn (e.keyCode == Keyboard.UP) isUp = false; else if (e.keyCode == Keyboard.DOWN) isDown = false; if (e.keyCode == Keyboard.LEFT) isLeft = false; else if (e.keyCode == Keyboard.RIGHT) isRight = false;  private Funktion handleEnterFrame (e: Event): void / ** * Bestimmt die Größe * / if (isUp) velo.setMagnitude (Math.min (velo.getMagnitude () + 0.2, 3)); else if (isDown) velo.setMagnitude (Math.max (velo.getMagnitude () - 0,2, 1)); / ** * Steuere die Richtung * / if (isRight) velo.setAngle (velo.getAngle () + 0.03); else if (isLeft) velo.setAngle (velo.getAngle () - 0,03); recalculate_distance (); wenn (Entfernung) < c1.radius) bounce(); updateShip();  /** * Update ship's position, orientation and it's area (the blue-ish circle) */ private function updateShip():void  ship.x += velo.x; ship.y += velo.y; ship.rotation = Math2.degreeOf(velo.getAngle()); c1.x = ship.x; c1.y = ship.y; if (ship.x > stage.stageWidth || ship.x < 0) velo.x *= -1; if (ship.y > stage.stageHöhe || ship.y < 0) velo.y *= -1; 

Sie können sehen, dass die Tastatursteuerelemente Flags aktualisieren, um anzuzeigen, ob die linke, obere, rechte oder untere Taste gedrückt wird. Diese Flags werden vom Enterframe-Ereignishandler erfasst und aktualisieren die Größe und Richtung des Schiffes.


Schritt 13: Berechnung des Reflexionsvektors

Die Vektorberechnung des Reflexionsvektors habe ich in diesem Beitrag bereits behandelt. Ich werde hier nur behandeln, wie man den Normalenvektor aus dem Gradienten erhält.

\ [
\ frac df (x) dx = Farbverlauf \\
line \ gradient = \ frac y x \\
Angenommen, \ gradient = 0,5 \\
y = 0,5 \\
x = 1 \\
Vektor \ von \ left \ normal =
\ begin bmatrix -1 \\ 0.5 \ end bmatrix \\
Vektor \ of \ right \ normal =
\ begin bmatrix 1 \\ - 0.5 \ end bmatrix
\]


Schritt 14: ActionScript-Implementierung

Im folgenden ActionScript wird das im vorherigen Schritt erläuterte mathematische Konzept implementiert. Überprüfen Sie die hervorgehobenen Zeilen:

 private Funktion bounce (): void var gradient: Number = quadratic_equation.diff1 (intersec_points [0] .x); var grad_vec: Vector2D = neuer Vector2D (1, Farbverlauf); var left_norm: Vector2D = grad_vec.getNormal (false); var right_norm: Vector2D = grad_vec.getNormal (); var chosen_vec: Vector2D; if (velo.dotProduct (left_norm)> 0) chosen_vec = left_norm ansonsten chosen_vec = right_norm var chosen_unit: Vector2D = chosen_vec.normalise (); var proj: Number = velo.dotProduct (chosen_unit); chosen_unit.scale (-2 * proj); velo = velo.add (gewählte_einheit); 

Fazit

Vielen Dank für deine Zeit! Wenn Sie dies als nützlich empfunden haben oder Fragen haben, hinterlassen Sie einige Kommentare.