Maths et ActionScript des courbes racines

Dans le premier tutoriel de cette série, nous avons examiné les courbes utilisant des équations et AS3. Nous allons maintenant nous attaquer à la résolution de ces équations pour trouver les racines d’une courbe, c’est-à-dire les endroits où la courbe croise une droite donnée. Nous pouvons utiliser ceci pour prédire les collisions avec des surfaces courbes et éviter le "tunneling" dans les jeux Flash.


Étape 1: Racines quadratiques

Tout d’abord, le temps d’une révision mathématique rapide. Dans ce tutoriel, nous allons simplement accepter et appliquer les méthodes que nous allons utiliser, mais les lecteurs intéressés peuvent consulter la page de Wikipedia sur les équations du second degré pour plus d'informations sur les dérivations mathématiques..

Donc \ (f (x) \) est une fonction quadratique. Si \ (f (x) \) équivaut à 0, \ (x \) peut être obtenu par cette formule:

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

\ (b ^ 2 - 4ac \) est appelé le discriminant de la formule. Si le discriminant est négatif, la racine carrée du discriminant produira racines imaginaires, que nous ne pouvons pas tracer. Inversement, si le discriminat est positif, vous aurez de vraies racines numériques et vous pourrez les tracer à l'écran..


Étape 2: Visualiser les racines quadratiques

Alors, quelles sont les racines? Dans notre contexte, il n’ya rien de plus que des points d’intersection entre la courbe quadratique et une ligne. Par exemple, supposons que nous voulions trouver le ou les points d'intersection de l'ensemble d'équations suivant:

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

C’est un scénario typique de recherche du ou des points d’intersection entre une courbe quadratique et l’axe des x (l’axe des x étant la ligne où y == 0). Puisque par définition les points d'intersection sont partagés par \ (f (x) \) et \ (g (x) \), nous pouvons en conclure que \ (f (x) = g (x) \) pour les valeurs de X que nous recherchons.

Il s’agit alors d’une opération triviale dans laquelle vous remplacez simplement les fonctions, puis appliquez la formule de l’étape 1 pour obtenir les racines. Maintenant, il y a plusieurs possibilités que nous pouvons anticiper comme indiqué ci-dessous.

(Comme vous pouvez le constater, "racines imaginaires" signifie, pour nos besoins, que la courbe ne croise jamais l'axe des x.)

Considérons maintenant le cas où \ (g (x) \) est plus qu'une simple ligne horizontale banale. Disons que c'est une ligne oblique, \ (g (x) \ = \ mx \ + \ d \). Maintenant, lorsque nous assimilons les deux fonctions, nous devons faire un peu de précalcul avant de pouvoir appliquer efficacement la formule..

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

J'ai inclus une présentation Flash interactive ci-dessous, alors n'hésitez pas à faire glisser les points rouges et bleus. Les points jaunes indiquent les points d'intersection. Vous devrez peut-être positionner la courbe et la ligne de manière à ce que les points jaunes apparaissent.


Étape 3: Tracer ceci avec ActionScript

Le script complet se trouve dans Demo1.as; Ici, je vais vous expliquer un extrait crucial du code. Regardons l'AS3 pour dessiner la courbe et la ligne:

 fonction privée redessiner (): void var cmd: Vector. = nouveau vecteur.; var coord: vecteur. = nouveau vecteur.; // redessiner la courbe; m1 = nouveau Matrix3d ​​(curve_points [0] .x * curve_points [0] .x, curve_points [0] .x, 1, 0, curve_points [1] .x * curve_points [1] .x, curve_points [1] .x , 1, 0, points_courbe [2] .x * points_courbe [2] .x, points_courbe [2] .x, 1, 0, 0,0,0,1); m2 = new Matrix3d ​​(points_courbe [0] .y, 0, 0, 0, points_courbe [1] .y, 0, 0, 0, points_courbe [2] .y, 0, 0, 0, 0,0,0, 1) m1.invert (); m2.append (m1); quadratic_equation.define (m2.n11, m2.n21, m2.n31); pour (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); 

La majeure partie du code ActionScript utilisé pour tracer la courbe entre les lignes 80 et 104 est largement empruntée au didacticiel précédent. Je vais donc expliquer un peu le code permettant de tracer une ligne.

Dans la présentation Flash ci-dessus, il y a deux points bleus interactifs. Chacun de ceux-ci a des coordonnées, et avec les deux points, une ligne est formée. Comme les deux points se trouvent sur la même ligne, ils partagent une pente commune et une ordonnée à l'origine pour former une équation de ligne générale:

\ [
y \ = \ mx \ + \ d, \\
m \ = \ pente, \ d \ = \ y-intercept
\]

Nous pouvons utiliser un peu d’algèbre pour résoudre les deux inconnues, \ (m \) et \ (d \). Étant donné les coordonnées des deux points bleus comme \ ((x_1, \ y_1) \) et \ ((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 =
je
\ begin bmatrix m \\ d \ end bmatrix
[/latex]

(Notez qu'une matrice avec -1 en exposant correspond à l'inverse de cette matrice.)

Donc, en utilisant ceci, \ (m \) et \ (d \) sont calculés. Nous pouvons maintenant dessiner la ligne en joignant les coordonnées \ ((0, y_3) \) et \ ((stage.stageWidth, y_4) \). Comment trouvez-vous \ (y_3 \) et \ (y_4 \)? Maintenant que \ (m \), \ (x \) et \ (d \) sont connus, nous pouvons simplement mettre toutes ces valeurs dans l’équation générale de la ligne,

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

… Pour obtenir ces \ (y \) s.


Étape 4: Calculer les racines quadratiques

Pour calculer la position des points d'intersection, nous allons utiliser la formule de l'étape 1. Ceci est fait en EqQuadratic.as comme les fonctions indiquées ci-dessous:

 / ** Lecture seule * Discriminant de l'équation * / fonction publique get discriminant (): Nombre // B * B-4 * A retourne _B * _B - 4 * _A * _C;  / ** * Effectue un calcul pour obtenir les racines * / fonction publique calcRoots (): void var disque: Nombre = this.discriminant // gère les racines imaginaires 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);  

Plus de détails sur EqQuadratic.as:

Une fonction Type Paramètre d'entrée La fonctionnalité
EqQuadratic Méthode Néant Constructeur de classe
définir Méthode Coefficients a, b et c de l'équation quadratique Instancier les valeurs de coefficient
fx_of Méthode Valeur de x Retourne \ (f (x) \) de l'entrée donnée \ (x \).
racines calciques Méthode Néant Effectue un calcul pour obtenir une racine quadratique
diff1 Méthode \ (x \) coordonne la différenciation du premier degré Différencié \ (f (x) \) de donné \ (x \) au premier degré.
diff2 Méthode \ (x \) coordonne la différenciation au second degré Différencié \ (f (x) \) de donné \ (x \) au deuxième degré.
discriminer Propriété en lecture seule Néant Renvoie la valeur du discriminant, \ (b ^ 2 - 4ac \)
racines_R Propriété en lecture seule Néant Renvoie un vecteur numérique pour les racines du nombre réel. Les éléments sont NaN si aucune racine réelle n'existe.
racines_i Propriété en lecture seule Néant Renvoie un vecteur de chaîne pour les racines du nombre imaginaire. Les éléments sont nuls s'il n'y a pas de racines imaginaires.

Étape 5: traçage de celui-ci avec ActionScript

Un exemple d'utilisation de cette EqQuadratic.as est dans Demo1.as. Après l'initiation de EqQuadratic, nous allons l'utiliser pour calculer les racines. Ensuite, après avoir validé la présence de racines réelles, nous les utiliserons pour tracer les points jaunes..

Maintenant, les racines se réfèrent uniquement au composant \ (x \) des coordonnées. Pour obtenir les \ (y \) s, devinez quoi? De nouveau, nous plaçons les valeurs de \ (m \), \ (d \) (calculées précédemment à l’étape 3) et \ (x \) (à partir des racines) dans l’équation générale de la ligne, \ (y \ = \ mx \ + \ d \). Vérifiez le code correspondant aux lignes 135 et 136.

 fonction privée recalculate_reposition (): void quadratic_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); racines de var: vecteur. = quadratic_equation.roots_R; if (! isNaN (racines [0]) &&! isNaN (racines [1])) intersec_points [0] .x = roots [0]; intersec_points [0] .y = n2.a * racines [0] + n2.b intersec_points [1] .x = roots [1]; intersec_points [1] .y = n2.a * racines [1] + n2.b sinon intersec_points [0] .x = -100; intersec_points [0] .y = -100; intersec_points [1] .x = -100; intersec_points [1] .y = -100; 

Étape 6: Racines cubiques

Les racines cubiques, sans surprise, sont les points d’intersection entre un cubique courbe et une ligne. Mais une courbe cubique est un peu différente d'une courbe quadratique, et à cet égard, les possibilités de localiser les intersections sont différentes..

L'image ci-dessous montre une courbe cubique coupant l'axe des x:

Encore une fois, voici une petite présentation Flash que vous pourrez expérimenter. Les points rouges et bleus peuvent être déplacés tandis que les jaunes indiquent simplement les points d'intersection.


Étape 7: Formule générale pour les racines cubiques

La formule générale pour trouver une courbe cubique a été découverte par Cardano. Bien que je sois tenté de développer les détails, je vais simplement indiquer aux lecteurs intéressés les liens suivants:

  • Wikipédia
  • Wolfram et
  • Un autre fichier PDF utilisateur.

Quoi qu'il en soit, le EqCubic.as class implémente cette formule pour résoudre les racines des fonctions cubiques avec d'autres fonctions utilitaires mathématiques. En règle générale, tous les attributs et méthodes de EqCubic.as suivez la description présentée à l'étape 4, car les deux classes EqQuadratic.as et EqCubic.as implémenter une interface commune, IEquation.as, sauf pour les détails énumérés ci-dessous.

Une fonction Différence
définir Un total de quatre coefficients (a, b, c, d) à entrer pour l’équation cubique; seulement trois pour l'équation quadratique.
racines_R, root_i Le total des racines réelles et imaginaires est trois pour une équation cubique, mais deux pour une équation quadratique.

Étape 8: Représentation graphique avec ActionScript

Voici l'implémentation d'Actionscript pour la présentation Flash à l'étape 5. Le code complet est en Demo3.as.

 fonction privée redessiner (): void var cmd: Vector. = nouveau vecteur.; var coord: vecteur. = nouveau vecteur.; // redessiner la courbe; m1 = nouveau Matrix3d ​​(curve_points [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 * points_courbe [1] .x * points_courbe [1] .x, points_courbe [1] .x * points_courbe [1] .x, points_courbe [1] .x, 1, points_courbe [2] .x * points_courbe [2] .x * points_courbe [2] .x, points_courbe [2] .x * points_courbe [2] .x, points_courbe [2] .x, 1, points_courbe [3] .x * points_courbe [3] .x * curve_points [3] .x, curve_points [3] .x * curve_points [3] .x, curve_points [3] .x, 1); m2 = new Matrix3d ​​(points_courbe [0] .y, 0, 0, 0, points_courbe [1] .y, 0, 0, 0, points_courbe [2] .y, 0, 0, 0, points_courbe [3] .y , 0, 0, 0) m1.invert (); m2.append (m1); cubic_equation.define (m2.n11, m2.n21, m2.n31, m2.n41); pour (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); 

Encore une fois, les commandes ActionScript pour dessiner une courbe cubique sont exactement les mêmes que celles expliquées dans mon article précédent, alors que les commandes Actionscript pour dessiner la ligne sont déjà expliquées à l'étape 3 de celui-ci..

Passons maintenant au calcul et au positionnement des racines cubiques:

 fonction privée recalculate_reposition (): void cubic_equation.define (m2.n11, m2.n21, m2.n31 - n2.a, m2.n41 - n2.b); cubic_equation.calcRoots (); racines de var: vecteur. = cubic_equation.roots_R; pour (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;   

Après instanciation cubic_equation dans le constructeur, nous définissons ses coefficients, calculons les racines et stockons les racines dans une variable.

Une petite remarque sur les racines: il existe au maximum trois racines réelles pour une équation cubique, mais toutes les racines réelles ne sont pas présentes dans toutes les situations, certaines racines pouvant être imaginaires. Alors que se passe-t-il lorsqu'il n'y a qu'une seule racine réelle, par exemple? Eh bien, une des variétés de racines appelées à partir de cubic_equation.roots_R sera un nombre réel, alors que tous les autres ne seront pas un nombre (NaN). Consultez le code ActionScript en surbrillance pour cela..


Étape 9: Prévision de l'endroit où un objet va entrer en collision avec une surface courbe

Une bonne application de calcul des racines consiste à projeter un point de collision sur une surface courbe, comme indiqué ci-dessous. Utilisez les touches fléchées gauche et droite pour diriger le navire en mouvement et appuyez vers le haut pour accélérer. Vous remarquerez que les points de collision qui se seraient produits dans le passé sont légèrement estompés..


Étape 10: mise en œuvre

L'idée est similaire à celle de mon tutoriel sur la prévision des points de collision. Cependant, au lieu d'entrer en collision avec une ligne droite, nous utilisons maintenant un incurvé ligne. Voyons le code.

L'extrait ci-dessous est appelé chaque image:

 mise à jour des fonctions privées (e: Event): void // Pilotage à gauche et à droite si (control == 1) velo = velo.rotate (Math2.radianOf (-5)); sinon si (contrôle == 2) velo = velo.rotate (Math2.radianOf (5)); // manipulant la vitesse var currVelo: Number = velo.getMagnitude (); si (augmentation == 0) currVelo - = 0,5; currVelo = Math.max (currVelo, 1); // limite inférieure de vélocité else if (augmentation == 1) currVelo + = 0.5; currVelo = Math.min (currVelo, 5); // limite supérieure de la vitesse velo.setMagnitude (currVelo); // met à jour la vitesse ship.x + = velo.x; ship.y + = velo.y; ship.rotation = Math2.degreeOf (velo.getAngle ()); // refléter lorsque le navire est hors du stade if (ship.x <0 || ship.x > stage.stageWidth) velo.x * = -1; si (ship.y <0 || ship.y > stage.stageHeight) velo.y * = -1; redessiner (); recalculer (); 

Le code de base réside dans redessiner et recalculer. Voyons d'abord ce qu'il y a dedans redessiner. C'est le même que nous utilisions dans les démos précédentes. Une petite note sur le tracé de la ligne. Nous avons vu dans les démos précédentes qu'il fallait deux points pour tracer l'équation. Eh bien, ici nous n’avons qu’un seul navire. Donc, pour obtenir le deuxième point, ajoutez simplement la vitesse du navire à sa position actuelle. J'ai souligné le code pour plus de commodité.

 fonction privée redessiner (): void var cmd: Vector. = nouveau vecteur.; var coord: vecteur. = nouveau vecteur.; // redessiner la courbe; m1 = nouveau Matrix3d ​​(w1.x * w1.x, w1.x, 1, 0, w2.x * w2.x, w2.x, 1, 0, w3.x * w3.x, w3.x, 1 , 0,0,0,0,1); m2 = new Matrix3d ​​(w1.y, 0, 0, 0, w2.y, 0, 0, 0, w3.y, 0, 0, 0, 0,0,0,1) m1.invert (); m2.append (m1); quadratic_equation.define (m2.n11, m2.n21, m2.n31); minX = Math.min (w1.x, w2.x, w3.x); maxX = Math.max (w1.x, w2.x, w3.x); pour (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); 

Maintenant pour recalculer, J'ai fait un petit calcul vectoriel pour vérifier si le point est derrière ou devant le vaisseau. Découvrez le code en surbrillance:

 fonction privée recalculer (): void quadratic_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); racines de var: vecteur. = quadratic_equation.roots_R; pour (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;   

Étape 11: Détection de collision basée sur le temps

Une autre grande application n’est pas aussi évidente que la première. Pour effectuer une détection de collision plus précise, au lieu de fonder notre conclusion sur la distance entre deux objets, nous allons en évaluer le résultat. temps pour avoir un impact. Pourquoi? Parce que le "tunneling" peut arriver si nous utilisons la détection de collision basée sur la distance

Considérons un algorithme de détection de collision basé sur la distance pour deux cercles. Sur les quatre images montrées, seule l'image 2.15 a détecté avec succès une collision entre deux cercles. Pourquoi? Parce que la distance actuelle entre le centre des cercles gris et rouge est inférieure à la somme des rayons des deux cercles.

(Les lecteurs intéressés par plus de détails sur ce sujet peuvent se référer à cet article.)

\ [distance \ entre \ cercles \

Le problème provient de la façon dont Flash procède image par image, une image à la fois, ce qui signifie que seules les images 1, 2 et 3 seront capturées avec succès, et non les moments entre ces instantanés. Maintenant, les cercles gris et rouges ne se sont pas entrés en collision dans ces cadres, selon un calcul basé sur la distance.!

Pour résoudre ce problème, nous avons besoin d'un moyen de voir la collision qui s'est produite entre images 2 et 3. Nous devons calculer le temps d’impact entre deux cercles. Par exemple, une fois que nous vérifions que le temps d’impact est inférieur à 1 image à l’image 2, cela signifie qu’une fois que Flash a procédé, une collision avant image avant ou même que le tunnel a définitivement eu lieu..

\ [si \ time \ to \ impact, \ t, \ remplit \ 0 \

La question est, comment calculons-nous cette fois?


Étape 12: Précalculs

Je vais essayer de montrer ma méthode aussi simplement que possible.

Étant donné le scénario ci-dessus, les deux cercles gris et rouge se trouvent actuellement à \ ((x_ gris, \ y_ gris) \) et à \ ((x_ rouge, \ y_ rouge) \). Ils se déplacent respectivement à \ (v_ grey \) et \ (v_ rouge \), et sont définis sur un chemin de collision. Nous sommes intéressés pour calculer le temps pris, \ (t \), pour qu'ils atteignent les positions \ ((x '_ grey, \ y' _ grey) \) et \ ((x '_ rouge , \ y '_ red) \), indiquée par les cercles gris et rouges translucides, où la collision s'est produite.

\ [
déplacement_ avenir = déplacement_ présent + vélocité * temps \\
x '_ grey = x_ grey + v_ grey_x * t \… (éq. \ 1) \\
y '_ grey = y_ grey + v_ grey_y * t \… (eq. 2) \\
x '_ rouge = x_ rouge + v_ rouge_x * t \… (éq. 3) \\
y '_ red = y_ red + v_ red_y * t \… (eq. 4)
\]

Notez que j'ai dérivé les composantes horizontales et verticales de \ (v_ gray \) dans \ (v_ gray_x \) et \ (v_ gray_y \). Même chose avec la vitesse du cercle rouge; consultez ce petit conseil pour savoir comment ces composants sont dérivés.

Il nous manque encore une relation pour relier toutes ces équations. Voyons l'image ci-dessous.

L'autre relation remonte à Pythagore. Lorsque les deux cercles se rencontrent, la distance entre les deux centres est exactement \ (rad_ grey \) plus \ (rad_ rouge \).

\ [
\ Théorème de Pythagore, \ z ^ 2 = x ^ 2 + y ^ 2 \\
(rad_ gris + rad_ rouge) ^ 2 = (x '_ gris -x' _ rouge) ^ 2+ (y '_ gris -y' _ rouge) ^ 2 \ … (Éq. \ 5) \\
\]

C’est là que vous substituez les équations 1 à 4 à l’équation 5. Je comprends que c’est mathématiquement décourageant; je l’ai donc séparée en étape 13. N'hésitez pas à la sauter pour obtenir le résultat à l’étape 14..


Étape 13 (facultatif): rigueur mathématique

Tout d'abord, nous établissons les identités suivantes.

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

N'oubliez jamais que tous les symboles mathématiques représentent une constante, à l'exception du temps, \ (t \), qui fait l'objet de l'intérêt.

\ (x_ grey, \ v_ grey_x, \ y_ rouge, \) et ainsi de suite sont tous définis dans le scénario.

Ensuite, nous allons essayer de décomposer notre problème terme par terme:

\ [
(rad_ gris + rad_ rouge) ^ 2 = (x '_ gris -x' _ rouge) ^ 2+ (y '_ gris -y' _ rouge) ^ 2 \ \
Considérons \ term \ (x '_ grey -x' _ rouge) ^ 2 \ et \ en utilisant \ id. \ 2 \\
(x '_ gris -x' _ rouge) ^ 2 = (x '_ gris) ^ 2-2 (x' _ gris) (x '_ rouge) + (x' _ rouge) ^ 2 \\
\]
\ [
Considérons \ term \ (x '_ grey) ^ 2 \\
(x '_ grey) ^ 2 \\
= (x_ grey + v_ grey_x * t) ^ 2, \ utilise \ id. \ 1 \\
= (x_ grey) ^ 2 + 2 (x_ grey) (v_ grey_x * t) + (v_ grey_x * t) ^ 2
\]
\ [
Considérons \ term \ -2 (x '_ grey) (x' _ rouge) \\
-2 (x '_ gris) (x' _ rouge) \\
= -2 (x_ grey + v_ grey_x * t) (x_ rouge + v_ red_x * t) \\
= -2 [(x_ grey) (x_ rouge) + (x_ grey) (v_ red_x * t) + (v_ grey_x * t) (x_ rouge) + (v_ gray_x * t) (v_ red_x * t)] \\
= -2 (x_ grey) (x_ rouge) - 2 (x_ grey) (v_ red_x * t) -2 (v_ grey_x * t) (x_ rouge) - 2 ( v_ gray_x * t) (v_ red_x * t)
\]
\ [
Considérons \ term \ (x '_ red) ^ 2 \\
(x '_ rouge) ^ 2 \\
= (x_ rouge + v_ rouge_x * t) ^ 2, \ utilise \ id. \ 1 \\
= (x_ red) ^ 2 + 2 (x_ red) (v_ red_x * t) + (v_ red_x * t) ^ 2
\]

Maintenant, en un coup d'œil, nous pouvons facilement voir que la puissance la plus élevée de \ (t \) est 2. Nous avons donc nous-mêmes une équation quadratique. Rassemblons tous les coefficients apportés par ces trois termes en fonction de leurs pouvoirs.

\ (t ^ 2 \) \ (t \) \ (t ^ 0 = 1 \)
\ ((v_ gray_x) ^ 2 \) \ (2 (x_ gray) (v_ gray_x) \) \ ((x_ grey) ^ 2 \)
\ (- 2 (v_ gray_x) (v_ red_x) \) \ (- 2 (x_ grey) (v_ red_x) - 2 (v_ grey_x) (x_ rouge) \) \ (- 2 (x_ grey) (x_ rouge) \)
\ ((v_ red_x) ^ 2 \) \ (2 (x_ red) (v_ red_x) \) \ ((x_ rouge) ^ 2 \)

Analysons les coefficients avec \ (t ^ 2 \) et \ (t ^ 0 \).

\ [
(v_ gray_x) ^ 2-2 (v_ gray_x) (v_ red_x) + (v_ red_x) ^ 2, \ remémorer \ 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_ grey) ^ 2-2 (x_ grey) (x_ rouge) + (x_ rouge) ^ 2, \ rappel \ id. \ 2 \\
(x_ grey) ^ 2-2 (x_ grey) (x_ rouge) + (x_ rouge) ^ 2 = (x_ gris - x rouge) ^ 2
\]

Et celui de \ (t \).

\ [
Simplifier\\
a = (x_ grey), \ b = (v_ grey_x) \\
c = (v_ red_x), \ d = (x_ rouge) \\
2ab-2ac-2bd + 2dc \\
= 2 [ab-ac-bd + dc] \\
= 2 [a (b-c) -d (b-c)] \\
= 2 [(b-c) (a-d)] \\
Resubstituer \\
2 [(b-c) (a-d)] = 2 (v_ gray_x -v_ red_x) (x_ grey -x_ rouge)
\]

Résumons en terme de \ ((x '_ grey -x' _ rouge) ^ 2 \)

\ [
(x '_ grey -x' _ rouge) ^ 2 \\
= (v_ gray_x -v_ red_x) ^ 2 * t ^ 2 + 2 (v_ gray_x -v_ red_x) (x_ gris -x_ rouge) * t + (x_ gris -x_ red) ^ 2
\]

Notez que cela ne concerne qu'un terme dans \ (eq. \ 5 \). Nous aurons besoin de suivre le même processus pour un autre terme \ ((y '_ grey -y' _ rouge) ^ 2 \). Comme ils ont la même forme algébrique, le résultat devrait également être le même..
\ [
(y '_ grey -y' _ rouge) ^ 2 \\
= (v_ grey_y -v_ rouge_y) ^ 2 * t ^ 2 + 2 (v_ grey_y -v_ rouge_y) (y_ gris -y_ rouge) * t + (y_ gris -y_ red) ^ 2
\]

Ainsi, après réarrangement en termes de \ (t \), \ (eq. \ 5 \) devrait être comme suit.

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


Étape 14: le résultat

Donc, de l'étape précédente, grâce à une algèbre rigoureuse, nous sommes arrivés à la formule suivante

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

Maintenant, c'est une formule quadratique énorme. Nous allons essayer de regrouper les coefficients dans celui accepté par EqQuadratic. Comparez les deux formes:

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


Étape 15: Exemple d'implémentation

Alors, voici une présentation Flash pour illustrer l’idée. Vous verrez deux particules sur la scène, l'une grise et l'autre rouge. Les deux sont connectés à une flèche, indiquant la magnitude et la direction de la vitesse.

  • Cliquez sur le bouton "Suivant" pour avancer d'une image à la fois..
  • Cliquez sur le bouton "Précédent" pour rétablir une image dans le temps..

Pour modifier la vitesse des particules, appuyez sur:

  • "Up" et "Down" pour augmenter et diminuer la magnitude de la vitesse, respectivement.
  • "Gauche" et "droite" pour faire pivoter la vélocité.
  • "N" pour changer le cercle que vous contrôlez.

Enfin, pour activer la visibilité des flèches, appuyez sur "V"


Étape 16: Une note sur les racines quadratiques

L'équation quadratique a deux racines. Dans ce contexte, nous nous intéressons aux vraies racines. Cependant, si les deux particules ne sont pas définies sur le chemin de collision (les deux chemins sont parallèles), des racines imaginaires seront alors produites au lieu de véritables racines. Dans ce cas, les deux racines réelles resteront NaN.

Si les deux particules sont placées sur un chemin de collision, nous aurons deux racines réelles. Mais que représentent ces deux racines??

Rappelez-vous à l'étape 12 que nous avons utilisé le théorème de Pythagore pour lier \ ((x '_ grey, \ y' _ grey) \) et) (\ ((x '_ rouge, \ y' _ rouge ) \) ensemble dans une équation. Eh bien, il existe deux situations dans lesquelles la distance entre le centre de deux cercles correspond exactement à la somme des deux rayons: un avant collision et un après collision. Regardez cette image:

Alors, lequel choisissons-nous? Évidemment le premier parce que l'instance après la collision ne nous intéresse pas. Nous devrions donc toujours choisir la moindre valeur des deux racines et l’évaluer. Si la valeur est positive et inférieure à 1, une collision se produira au cours de la prochaine image. Si la valeur est négative, la collision s'est produite dans le passé.


Étape 17: Le code ActionScript expliqué

Regardons le Actionscript implémenté pour cet exemple. Tout d'abord, les variables.

 // c1 est le cercle gris // c2 est le cercle rouge private var c1: Cercle, c2: Cercle; // v1 est la vitesse du cercle gris // v2 est la vitesse privée du cercle rouge var v1: Vector2D, v2: Vector2D, bascule: Boolean = true, usingV1: Boolean = true; // tri1 formera la pointe de la flèche de la v1 // tri2 formera la tête de la flèche de la v2 private var tri1: Triangle, tri2: Triangle; conteneur var privé: Sprite; private eq var: EqQuadratic;

Puis le calcul des racines. Vous pouvez vérifier le code ActionScript suivant avec les variables ci-dessus..