Las Matemáticas y ActionScript de Curvas Raíces

En el primer tutorial de esta serie, echamos un vistazo a las curvas de dibujo utilizando ecuaciones y AS3. Ahora, vamos a abordar la resolución de esas ecuaciones para encontrar las raíces de una curva, es decir, los lugares donde la curva cruza una línea recta dada. Podemos usar esto para predecir colisiones con superficies curvas, y para evitar "túneles" en juegos Flash.


Paso 1: Raíces cuadráticas

Primero, tiempo para una rápida revisión matemática. En este tutorial, solo aceptaremos y aplicaremos los métodos que usaremos, pero los lectores interesados ​​pueden consultar la página de Wikipedia sobre ecuaciones cuadráticas para obtener información sobre las derivaciones matemáticas..

Entonces \ (f (x) \) es una función cuadrática. Si \ (f (x) \) es equivalente a 0, \ (x \) se puede obtener con esta fórmula:

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

\ (b ^ 2 - 4ac \) se llama discriminante de la formula. Si el discriminante es negativo, la raíz cuadrada del discriminante producirá raíces imaginarias, que no podemos trazar. Por el contrario, si la discriminación es positiva, tendrá raíces de números reales y podrá trazarlas en la pantalla..


Paso 2: Visualización de raíces cuadráticas

Entonces, ¿qué son las raíces? Bueno, en nuestro contexto, no son más que puntos de intersección entre la curva cuadrática y una línea. Por ejemplo, supongamos que estamos interesados ​​en encontrar el (los) punto (s) de intersección del siguiente conjunto de ecuaciones:

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

Este es un escenario típico de buscar el (los) punto (s) de intersección entre una curva cuadrática y el eje x (porque el eje x es la línea donde y == 0). Como por definición los puntos de intersección son compartidos por \ (f (x) \) y \ (g (x) \), podemos concluir que \ (f (x) = g (x) \) para los valores de X que estamos buscando.

Entonces es una operación trivial en la que simplemente sustituyes las funciones y luego aplicas la fórmula del Paso 1 para obtener las raíces. Ahora hay varias posibilidades que podemos anticipar como se muestra a continuación..

(Como puede ver, "raíces imaginarias" significa, para nuestros propósitos, que la curva nunca cruza el eje x).

Ahora consideremos el caso donde \ (g (x) \) es más que una línea horizontal mundana. Digamos que es una línea inclinada, \ (g (x) \ = \ mx \ + \ d \). Ahora, cuando equiparemos ambas funciones, tendremos que hacer un poco de cálculo previo antes de que la fórmula pueda aplicarse efectivamente.

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

He incluido una presentación Flash interactiva a continuación, así que siéntete libre de arrastrar los puntos rojos y azules. Los puntos amarillos indican los puntos de intersección. Es posible que deba colocar la curva y la línea para que se crucen entre sí para que aparezcan los puntos amarillos..


Paso 3: Graficando esto con ActionScript

El script completo se puede encontrar en Demo1.as; Aquí solo explicaré un extracto crucial del código. Veamos el AS3 para dibujar la curva y la línea:

 Función privada redraw (): void var cmd: Vector. = nuevo vector.; var coord: vector. = nuevo vector.; // redibujar curva; m1 = new 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, curve_points [2] .x * curve_points [2] .x, curve_points [2] .x, 1, 0, 0,0,0,1); m2 = new Matrix3d ​​(curve_points [0] .y, 0, 0, 0, curve_points [1] .y, 0, 0, 0, curve_points [2] .y, 0, 0, 0, 0,0,0, 1) m1.invert (); m2.append (m1); quadratic_equation.define (m2.n11, m2.n21, m2.n31); para (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); 

El grueso de ActionScript para dibujar la curva de la línea 80 ~ 104 se toma en gran parte del tutorial anterior, por lo que solo explicaré un poco sobre el código para dibujar una línea..

En la presentación Flash anterior, hay dos puntos azules interactivos. Cada uno de estos tiene coordenadas, y con ambos puntos, se forma una línea. Dado que ambos puntos se encuentran en la misma línea, comparten una pendiente común y una intersección en y para formar una ecuación de línea general:

\ [
y \ = \ mx \ + \ d, \\
m \ = \ pendiente, \ d \ = \ y-intercepto
\]

Podemos usar un poco de álgebra para resolver las dos incógnitas, \ (m \) y \ (d \). Dadas las coordenadas de los dos puntos azules como \ ((x_1, \ y_1) \) y \ ((x_2, \ y_2) \):

[látex]
y_1 = mx_1 + d \\
y_2 = mx_2 + d \\
[/látex]

[látex]
\ begin bmatrix y_1 \\ y_2 \ end bmatrix =
\ begin bmatrix x_1 & 1 \\ x_2 & 1 \ end bmatrix
\ begin bmatrix m \\ d \ end bmatrix \\
[/látex]

[látex]
\ 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 \\
[/látex]

[látex]
\ begin bmatrix x_1 & 1 \\ x_2 & 1 \ end bmatrix ^ - 1
\ begin bmatrix y_1 \\ y_2 \ end bmatrix =
yo
\ begin bmatrix m \\ d \ end bmatrix
[/látex]

(Tenga en cuenta que una matriz con superíndice -1 se refiere a la inversa de esa matriz).

Entonces, usando esto, se calculan \ (m \) y \ (d \). Ahora podemos dibujar la línea uniendo las coordenadas \ ((0, y_3) \) y \ ((stage.stageWidth, y_4) \). ¿Cómo encuentras \ (y_3 \) y \ (y_4 \)? Bueno, ahora que se conocen \ (m \), \ (x \) y \ (d \), simplemente podemos poner todos estos valores en la ecuación general de la línea,

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

... para obtener esos \ (y \) s.


Paso 4: Calcula las raíces cuadráticas

Para calcular la posición de los puntos de intersección, usaremos la fórmula del Paso 1. Esto se hace en EqQuadratic.as como las funciones que se muestran a continuación:

 / ** Solo lectura * Discriminante de la ecuación * / función pública obtener discriminante (): Número // B * B-4 * A * C retorno _B * _B - 4 * _A * _C;  / ** * Realiza el cálculo para obtener las raíces * / public function calcRoots (): void var disc: Number = this.discriminant // maneja las raíces imaginarias si (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);  

Más detalles de EqQuadratic.as:

Función Tipo Parámetro de entrada Funcionalidad
Eqquadratic Método Nulo Constructor de clase
definir Método Coeficientes a, byc de ecuación cuadrática. Instalar los valores del coeficiente.
fx_of Método Valor de x Devuelve \ (f (x) \) de una entrada \ (x \) dada.
CalcRoots Método Nulo Realiza el cálculo para obtener la raíz cuadrática.
diff1 Método \ (x \) coordenada para la diferenciación de primer grado. Diferenciado \ (f (x) \) de dado \ (x \) en primer grado.
diff2 Método \ (x \) coordenada para la diferenciación de segundo grado. Diferenciado \ (f (x) \) de dado \ (x \) en segundo grado.
discriminar Propiedad, solo lectura Nulo Devuelve el valor de discriminante, \ (b ^ 2 - 4ac \)
raices_R Propiedad, solo lectura Nulo Devuelve un vector de Número para las raíces de un número real. Los elementos son NaN si no existen raíces reales.
raíces_i Propiedad, solo lectura Nulo Devuelve un vector de cadena para las raíces de un número imaginario. Los elementos son nulos si no existen raíces imaginarias..

Paso 5: Graficando esto con ActionScript

Un ejemplo de la utilización de este EqQuadratic.as es en Demo1.as. Después de la iniciación de Eqquadratic, La usaremos para calcular las raíces. Luego, después de validar la presencia de raíces reales, las usaremos para trazar los puntos amarillos..

Ahora las raíces se refieren solo al componente \ (x \) de las coordenadas. Para obtener los \ (y \) s, ¿adivina qué? Nuevamente, colocamos los valores de \ (m \), \ (d \) (calculados anteriormente en el Paso 3) y \ (x \) (de las raíces) en la línea de la ecuación general, \ (y \ = \ mx \ + \ d \). Consulta el código correspondiente en las líneas 135 y 136..

 función privada recalculate_reposition (): void quadratic_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); raíces de var: 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; 

Paso 6: Raíces cúbicas

Las raíces cúbicas, como es lógico, son los puntos de intersección entre una cúbico Curva y una recta. Pero una curva cúbica es un poco diferente a una curva cuadrática y, a este respecto, las posibilidades de dónde se podrían ubicar las intersecciones son diferentes.

La imagen de abajo muestra una curva cúbica que se interseca con el eje x:

Una vez más, aquí hay una pequeña presentación en Flash para que experimentes. Los puntos rojos y azules se pueden arrastrar mientras que los amarillos solo indican los puntos de intersección.


Paso 7: Fórmula general para raíces cúbicas

La fórmula general para encontrar una curva cúbica fue descubierta por Cardano. Aunque me tientan a elaborar los detalles, solo señalaré a los lectores interesados ​​los siguientes enlaces:

  • Wikipedia
  • Wolfram, y
  • Otro archivo PDF útil.

De todos modos, el Eqcubic.as La clase implementa esta fórmula para resolver raíces de funciones cúbicas junto con otras funciones de utilidad matemáticas. En general todos los atributos y métodos para Eqcubic.as siga la descripción como se presenta en el Paso 4, porque ambas clases EqQuadratic.as y Eqcubic.as implementar una interfaz común, IEquation.as, excepto por los detalles listados abajo.

Función Diferencia
definir Un total de cuatro coeficientes (a, b, c, d) a ingresar para la ecuación cúbica; solo tres por ecuación cuadrática.
raices_R, root_i El total de raíces reales e imaginarias es tres para una ecuación cúbica, pero dos para una ecuación cuadrática.

Paso 8: Graficando esto con ActionScript

Aquí está la implementación de Actionscript para la presentación Flash del Paso 5. El código completo está en Demo3.as.

 Función privada redraw (): void var cmd: Vector. = nuevo vector.; var coord: vector. = nuevo vector.; // redibujar curva; m1 = Matrix3d ​​nuevo (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 * curve_points [1] .x * curve_points [1] .x, curve_points [1] .x * curve_points [1] .x, curve_points [1] .x, 1, curve_points [2] .x * curve_points [2] .x * curve_points [2] .x, curve_points [2] .x * curve_points [2] .x, curve_points [2] .x, 1, curve_points [3] .x * curve_points [3] .x * curve_points [3] .x, curve_points [3] .x * curve_points [3] .x, curve_points [3] .x, 1); m2 = nuevo Matrix3d ​​(curve_points [0] .y, 0, 0, 0, curve_points [1] .y, 0, 0, 0, curve_points [2] .y, 0, 0, 0, curve_points [3] .y , 0, 0, 0) m1.invert (); m2.append (m1); cubic_equation.define (m2.n11, m2.n21, m2.n31, m2.n41); para (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); 

Nuevamente, los comandos de ActionScript para dibujar una curva cúbica son exactamente los mismos que se explicaron en mi artículo anterior, mientras que los comandos de ActionScript para dibujar la línea ya se explicaron en el Paso 3 de este..

Ahora vamos a pasar a calcular y posicionar las raíces cúbicas:

 función privada recalculate_reposition (): void cubic_equation.define (m2.n11, m2.n21, m2.n31 - n2.a, m2.n41 - n2.b); cubic_equation.calcRoots (); raíces de var: vector. = cubic_equation.roots_R; para (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;   

Después de la instanciación cubic_equation En el constructor, procedemos a definir sus coeficientes, calcular las raíces y almacenar las raíces en una variable..

Una pequeña nota sobre las raíces: hay un máximo de tres raíces reales para una ecuación cúbica, pero no todas las raíces reales están presentes en todas las situaciones, ya que algunas raíces pueden ser imaginarias. Entonces, ¿qué sucede cuando solo hay una raíz real, por ejemplo? Bueno, una de las matrices de raíces llamadas cubic_equation.roots_R será un número real, mientras que todos los demás no serán un número (Yaya). Echa un vistazo al ActionScript resaltado para esto.


Paso 9: Predecir dónde un objeto chocará con la superficie curva

Una gran aplicación del cálculo de raíces es proyectar un punto de colisión en una superficie curva, como se muestra a continuación. Usa las teclas de flecha izquierda y derecha para dirigir el barco en movimiento, y presiona hacia arriba para acelerar. Notará que los puntos de colisión que habrían ocurrido en el pasado están ligeramente atenuados..


Paso 10: Implementación

La idea es similar a la de mi tutorial sobre la predicción de puntos de colisión. Sin embargo, en lugar de chocar con una línea recta, ahora estamos usando un curvo línea. Echemos un vistazo al código.

El siguiente fragmento de código se llama cada fotograma:

 actualización de funciones privadas (e: Evento): void // Girar a la izquierda y a la derecha si (control == 1) velo = velo.rotate (Math2.radianOf (-5)); else if (control == 2) velo = velo.rotate (Math2.radianOf (5)); // manipulando la velocidad var currVelo: Number = velo.getMagnitude (); if (aumentar == 0) currVelo - = 0.5; currVelo = Math.max (currVelo, 1); // límite inferior para la velocidad else if (aumento == 1) currVelo + = 0.5; currVelo = Math.min (currVelo, 5); // límite superior para la velocidad velo.setMagnitude (currVelo); // actualizar la velocidad ship.x + = velo.x; ship.y + = velo.y; ship.rotation = Math2.degreeOf (velo.getAngle ()); // refleja cuando el barco está fuera de escena si (ship.x <0 || ship.x > stage.stageWidth) velo.x * = -1; si (nave.y <0 || ship.y > stage.stageHeight) velo.y * = -1; redibujar (); recalcular(); 

El código central se encuentra en redibujar y recalcular. Veamos primero que hay en redibujar. Es el mismo que habíamos estado usando en demostraciones anteriores. Una pequeña nota en el dibujo de la línea. Vimos en demostraciones anteriores que se necesitan dos puntos para dibujar, obtener la ecuación. Bueno, aquí solo tenemos una nave. Entonces, para obtener el segundo punto, simplemente agregue la velocidad de la nave a su posición actual. He resaltado el código para mayor comodidad..

 Función privada redraw (): void var cmd: Vector. = nuevo vector.; var coord: vector. = nuevo vector.; // redibujar curva; m1 = nueva Matriz3d (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 = nueva Matriz 3d (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); para (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); 

Ahora para recalcular, He hecho un pequeño cálculo vectorial para verificar si el punto está detrás o delante del barco. Echa un vistazo al código resaltado:

 función privada recalcular (): void quadratic_equation.define (m2.n11, m2.n21 - n2.a, m2.n31 - n2.b); quadratic_equation.calcRoots (); raíces de var: vector. = quadratic_equation.roots_R; para (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 && raíces [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;   

Paso 11: Detección de colisión basada en el tiempo

Otra gran aplicación no es tan obvia como la primera. Para realizar una detección de colisión más precisa, en lugar de basar nuestra conclusión en la distancia entre dos objetos, veremos su tiempo para impactar. ¿Por qué? Debido a que "túnel" puede ocurrir si usamos la detección de colisión basada en la distancia:

Considere un algoritmo de detección de colisión que se basa en la distancia de dos círculos. De los cuatro cuadros mostrados, solo el cuadro 2.15 detectó con éxito la colisión entre dos círculos. ¿Por qué? Debido a que la distancia actual entre los centros de los círculos gris y rojo es menor que la suma de los radios de ambos círculos.

(Los lectores interesados ​​en más detalles sobre este tema pueden consultar este artículo).

\ [distancia \ entre \ círculos \

El problema se debe a la manera en que Flash avanza por un fotograma discreto a la vez, lo que significa que solo los fotogramas 1, 2 y 3 se capturarán con éxito, y no los momentos entre esas instantáneas de tiempo. Ahora, los círculos gris y rojo no colisionaron en estos cuadros de acuerdo con un cálculo basado en la distancia, por lo que el círculo rojo atraviesa el círculo gris.!

Para solucionar esto, necesitamos una manera de ver la colisión que ocurrió Entre cuadros 2 y 3. Necesitamos calcular el tiempo para impactar entre dos círculos. Por ejemplo, una vez que verificamos que el tiempo de impacto es inferior a 1 fotograma en el fotograma 2, esto significa que una vez que Flash proceda, se habrá producido definitivamente una colisión hacia adelante del fotograma o incluso un túnel..

\ [if \ time \ to \ impact, \ t, \ satisface \ 0 \

La pregunta es, ¿cómo calculamos este tiempo??


Paso 12: Precálculos

Trataré de mostrar mi método lo más simple posible..

Dado el escenario anterior, los dos círculos grises y rojos se encuentran actualmente en \ ((x_ gray, \ y_ gray) \) y \ ((x_ red, \ y_ red) \). Se mueven en \ (v_ gray \) y \ (v_ red \) respectivamente, y se establecen en una ruta de colisión. Nos interesa calcular el tiempo, \ (t \), para que alcancen las posiciones \ ((x '_ gray, \ y' _ gray) \) y \ ((x '_ red , \ y '_ rojo) \), indicados por los círculos translúcidos de gris y rojo, donde ocurrió la colisión.

\ [
displacement_ future = displacement_ present + speed * 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)
\]

Tenga en cuenta que he derivado los componentes horizontales y verticales de \ (v_ gray \) en \ (v_ gray_x \) y \ (v_ gray_y \). Lo mismo ocurre con la velocidad del círculo rojo; Echa un vistazo a este Consejo rápido para saber cómo se derivan estos componentes..

Todavía nos falta una relación para unir todas estas ecuaciones. Veamos la imagen de abajo..

La otra relación se remonta a Pitágoras. Cuando ambos círculos se encuentran, la distancia entre ambos centros es exactamente \ (rad_ gray \) más \ (rad_ red \).

\ [
El teorema de Pitágoras, \ z ^ 2 = x ^ 2 + y ^ 2 \\
(rad_ gray + rad_ red) ^ 2 = (x '_ gray -x' _ red) ^ 2+ (y '_ gray -y' _ red) ^ 2 \ ... (eq. \ 5) \\
\]

Aquí es donde se sustituyen las ecuaciones 1 ~ 4 en la ecuación 5. Entiendo que es bastante desalentador matemáticamente, así que lo separé en el Paso 13. Siéntase libre de omitirlo para llegar al resultado en el Paso 14.


Paso 13 (Opcional): Rigor Matemático

Primero, establecemos las siguientes identidades..

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

En todo momento, tenga en cuenta que todos los símbolos matemáticos representan una constante, excepto el tiempo, \ (t \), que es el tema de interés.

\ (x_ gray, \ v_ gray_x, \ y_ red, \) y así sucesivamente están definidos en el escenario.

A continuación, trataremos de dividir nuestro problema término por término:

\ [
(rad_ gray + rad_ red) ^ 2 = (x '_ gray -x' _ red) ^ 2+ (y '_ gray -y' _ red) ^ 2 \ \
Considere \ term \ (x '_ gray -x' _ red) ^ 2 \ y \ utilizando \ id. \ 2 \\
(x '_ gris -x' _ rojo) ^ 2 = (x '_ gris) ^ 2-2 (x' _ gris) (x '_ rojo) + (x' _ rojo) ^ 2 \\
\]
\ [
Considere \ term \ (x '_ gris) ^ 2 \\
(x '_ gris) ^ 2 \\
= (x_ gray + v_ gray_x * t) ^ 2, \ utilize \ id. \ 1 \\
= (x_ gris) ^ 2 + 2 (x_ gris) (v_ gray_x * t) + (v_ gray_x * t) ^ 2
\]
\ [
Considere \ term \ -2 (x '_ gray) (x' _ red) \\
-2 (x '_ gris) (x' _ rojo) \\
= -2 (x_ gray + v_ gray_x * t) (x_ red + v_ red_x * t) \\
= -2 [(x_ gray) (x_ red) + (x_ gray) (v_ red_x * t) + (v_ gray_x * t) (x_ red) + (v_ gray_x * t) (v_ red_x * t)] \\
= -2 (x_ gray) (x_ red) - 2 (x_ gray) (v_ red_x * t) -2 (v_ gray_x * t) (x_ red) - 2 ( v_ gray_x * t) (v_ red_x * t)
\]
\ [
Considere \ term \ (x '_ red) ^ 2 \\
(x '_ rojo) ^ 2 \\
= (x_ red + v_ red_x * t) ^ 2, \ utilize \ id. \ 1 \\
= (x_ red) ^ 2 + 2 (x_ red) (v_ red_x * t) + (v_ red_x * t) ^ 2
\]

Ahora, de un vistazo, podemos ver fácilmente que el mayor poder de \ (t \) es 2. Entonces, tenemos una ecuación cuadrática. Recolectemos todos los coeficientes aportados por estos tres términos de acuerdo a sus poderes.

\ (t ^ 2 \) \ (t \) \ (t ^ 0 = 1 \)
\ ((v_ gray_x) ^ 2 \) \ (2 (x_ gray) (v_ gray_x) \) \ ((x_ gris) ^ 2 \)
\ (- 2 (v_ gray_x) (v_ red_x) \) \ (- 2 (x_ gray) (v_ red_x) - 2 (v_ gray_x) (x_ red) \) \ (- 2 (x_ gris) (x_ rojo) \)
\ ((v_ red_x) ^ 2 \) \ (2 (x_ red) (v_ red_x) \) \ ((x_ red) ^ 2 \)

Analicemos los coeficientes con \ (t ^ 2 \) y \ (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_ gray) ^ 2-2 (x_ gray) (x_ red) + (x_ red) ^ 2, \ recall \ id. \ 2 \\
(x_ gris) ^ 2-2 (x_ gris) (x_ rojo) + (x_ rojo) ^ 2 = (x_ gris -x_ rojo) ^ 2
\]

Y eso de \ (t \).

\ [
Simplificar\\
a = (x_ gray), \ 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)] \\
Sustituir \\
2 [(b-c) (a-d)] = 2 (v_ gray_x -v_ red_x) (x_ gray -x_ red)
\]

Resumamos en términos de \ ((x '_ gray -x' _ red) ^ 2 \)

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

Tenga en cuenta que esto solo satisface un término en \ (eq. \ 5 \). Tendremos que realizar el mismo proceso para otro término \ ((y '_ gray -y' _ red) ^ 2 \). Como tienen la misma forma algebraica, el resultado también debe ser el mismo..
\ [
(y '_ gris -y' _ rojo) ^ 2 \\
= (v_ gray_y -v_ red_y) ^ 2 * t ^ 2 + 2 (v_ gray_y -v_ red_y) (y_ gray -y_ red) * t + (y_ gray -y_ rojo) ^ 2
\]

Por lo tanto, después de la reorganización en términos de \ (t \), \ (eq. \ 5 \) debe ser como sigue.

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


Paso 14: El Resultado

Así que desde el paso anterior, a través del álgebra rigurosa llegamos a la siguiente fórmula:

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

Ahora esta es una gran fórmula cuadrática. Intentaremos agrupar los coeficientes en los aceptados por Eqquadratic. Compara las dos formas:

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


Paso 15: Implementación de la muestra

Así que aquí hay una presentación Flash para demostrar la idea. Verás dos partículas en el escenario, una gris y la otra roja. Ambos están conectados a una flecha, que indica la magnitud y la dirección de la velocidad.

  • Haga clic en el botón "Siguiente" para avanzar un cuadro en el tiempo.
  • Haga clic en el botón "Atrás" para revertir un cuadro a tiempo.

Para alterar la velocidad de las partículas, presione:

  • "Arriba" y "abajo" para aumentar y disminuir la magnitud de la velocidad, respectivamente.
  • "Izquierda" y "derecha" para girar la velocidad.
  • "N" para cambiar el círculo que controlas.

Finalmente, para alternar la visibilidad de las flechas, presiona "V"


Paso 16: Una nota sobre las raíces cuadráticas

Hay dos raíces en la ecuación cuadrática. En este contexto, estamos interesados ​​en las raíces reales. Sin embargo, si las dos partículas no se establecen en el camino de colisión (ambos caminos son paralelos entre sí), entonces se producirán raíces imaginarias en lugar de raíces reales. En este caso, ambas raíces reales permanecerán. Yaya.

Si ambas partículas se colocan en una ruta de colisión, obtendremos dos raíces reales. ¿Pero qué representan estas dos raíces??

Recuerde en el Paso 12 que utilizamos el Teorema de Pitágoras para atar \ ((x '_ gray, \ y' _ gray) \) y \ ((x '_ red, \ y' _ red ) \) juntos en una ecuación. Bueno, hay dos situaciones donde la distancia entre los centros de dos círculos es exactamente la suma de ambos radios: uno antes de la colisión y otro después de la colisión. Mira esta imagen:

Entonces, ¿cuál elegimos? Obviamente el primero porque no estamos interesados ​​en la instancia después de la colisión. Así que siempre debemos elegir el valor menor de ambas raíces y evaluarlas. Si el valor es positivo y menor que 1, se producirá una colisión durante el siguiente fotograma. Si el valor es negativo, la colisión ocurrió en el pasado..


Paso 17: El ActionScript explicado

Veamos el Actionscript implementado para este ejemplo. Primero, las variables..

 // c1 es el círculo gris // c2 es el círculo rojo private var c1: Circle, c2: Circle; // v1 es la velocidad del círculo gris // v2 es la velocidad del círculo rojo private var v1: Vector2D, v2: Vector2D, toggle: Boolean = true, utilizando V1: Boolean = true; // tri1 formará la punta de flecha de la v1 // tri2 formará la punta de flecha de la v2 privada var tri1: Triángulo, tri2: Triángulo; contenedor var privado: Sprite; privada var eq: eqQuadratic;

Luego el cálculo de las raíces. Es posible que desee realizar una comprobación cruzada del siguiente código ActionScript con las variables anteriores..

 var p: Número = v1.x - v2.x; var q: Número = c1.x - c2.x; var r: Número = v1.y - v2.y; var s: Número = c1.y - c2.y; var a: Número = p * p + r * r; var b: Número = 2 * (p * q + r * s); var c: Número = q * q + s * s - (c1.radius + c2.radius) * (c1.radius + c2.radius); eq.define (a, b, c); eq.calcRoots (); raíces de var: vector. = eq.roots_R;

Así es como debes interpretar las raíces:

 // si no hay raíces reales disponibles, entonces están activadas, no en la ruta de colisión si (isNaN (raíces [0]) && isNaN (raíces [1])) t.text = "Partículas no en la ruta de colisión".  else var time: Number = Math.min (roots [0], roots [1]) var int_time: int = time * 1000; tiempo = int_time / 1000; t.text = "Marcos a impactar:" + time.toString () + "\ n"; if (time> 1