Trabajamos con gráficos bidimensionales en JavaScript.

¡Buen dia amigos!

Crear animaciones realistas de procesos físicos puede parecer una tarea desalentadora, pero no lo es. Los algoritmos utilizados para esto pueden ser muy simples y al mismo tiempo reproducir con precisión fenómenos físicos como el movimiento, la aceleración y la gravedad (atracción).

¿Quiere saber cómo se implementan estos algoritmos en JS?



Los ejemplos se pueden encontrar aquí .

El código fuente está aquí .

Movimiento uniforme y acelerado.


Comencemos con el movimiento.

Para un movimiento uniforme, podemos usar el siguiente código:

function move(dt) {
    x += vx * dt
    y += vy * dt
}

Aquí x e y son las coordenadas del objeto, vx y vy son la velocidad del objeto a lo largo de los ejes horizontal y vertical, respectivamente, dt (time delta - time delta) es el tiempo entre dos marcas de temporizador, que en JS es igual a dos llamadas a requestAnimationFrame.

Por ejemplo, si queremos mover un objeto ubicado en un punto con coordenadas 150, 50 hacia el suroeste, podemos hacer lo siguiente (una marca de temporizador o un paso):

x = 150 += -1 * 0.1 - > 149.9
y = 50 += 1 * 0.1 - > 50.1

Incluso el movimiento es aburrido, así que aceleremos nuestro objeto:

function move(dt) {
    vx += ax * dt
    vy += ay * dt
    x += vx * dt
    y += vy * dt
}

Aquí ax y ay son la aceleración a lo largo de los ejes x e y, respectivamente. Usamos la aceleración para cambiar la velocidad (vx / vy). Ahora, si tomamos el ejemplo anterior y agregamos aceleración a lo largo del eje x (hacia el oeste), obtenemos lo siguiente:

vx = -1 += -1 * 0.1 - > -1.1 // vx += ax * dt
vy = 1 += 0 * 0.1 - > 1 // vy += ay * dt
x = 150 += -1.1 * 0.1 - > 149.89 // x += vx * dt;     -0.01
y = 50 += 1 * 0.1 - > 50.1 // y += vy * dt

Gravedad


Aprendimos a mover objetos individuales. ¿Qué tal aprender a moverlos uno con respecto al otro? Esto se llama gravedad o gravedad. ¿Qué necesitamos hacer para esto?

Esto es lo que queremos obtener:



Primero, recordemos algunas ecuaciones de la escuela secundaria.

La fuerza aplicada al cuerpo se calcula mediante la siguiente fórmula:
F = m * a ... la fuerza es igual a la masa multiplicada por la aceleración
a = F / m ... a partir de esto podemos concluir que la fuerza actúa sobre el objeto con aceleración

Si aplicamos esto a dos interactuando objetos, obtenemos lo siguiente:



Parece complicado (al menos para mí), así que vamos a hacerlo bien. En esta ecuación, | F | - esta es la magnitud de la fuerza, que es la misma para ambos objetos, pero dirigida en direcciones opuestas. Los objetos están representados por las masas m_1 y m_2. k es la constante gravitacional y r es la distancia entre los centros de masa de los objetos. ¿Todavía no está claro? Aquí hay una ilustración:



si queremos hacer algo interesante, necesitamos más de dos objetos.



En esta imagen vemos dos objetos naranjas que atraen al negro con las fuerzas F_1 y F_2, sin embargo, estamos interesados ​​en la fuerza resultante F, que podemos calcular de la siguiente manera:

  • Primero calculamos las fuerzas F_1 y F_2 usando la fórmula anterior:
  • luego traduce todo a vectores:

Genial, tenemos todos los cálculos necesarios. ¿Cómo traducimos esto en código? No te aburriré con los pasos intermedios e inmediatamente daré el código terminado con comentarios. Si necesita más información, puede escribirme, responderé todas sus preguntas.

function moveWithGravity(dt, o) { // o -  ,    
    for (let o1 of o) { //   ()   
        o1.fx = 0
        o1.fy = 0
    }

    for (let [i, o1] of o.entries()) { //    
        for (let [j, o2] of o.entries()) {
            if (i < j) { //            
                let dx = o2.x - o1.x //     
                let dy = o2.y - o1.y
                let r = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2))
                if (r < 1) { //     0
                    r = 1
                }

                //     ; k = 1000
                let f = (1000 * o1.m * o2.m) / Math.pow(r, 2)
                let fx = f * dx / r
                let fy = f * dy / r
                o1.fx += fx //   
                o1.fy += fy
                o2.fx -= fx //      
                o2.fy -= fy
            }
        }
    }

    for (let o1 of o) { //    ...
        let ax = o1.fx / o1.m // 
        let ay = o1.fy / o1.m

        o1.vx += ax * dt // 
        o1.vy += ay * dt

        o1.x += o1.vx * dt // 
        o1.y += o1.vy * dt
    }
}

Choque


Los cuerpos móviles a veces chocan. De una colisión, los objetos son expulsados ​​por otros, o algunos objetos rebotan de otros. Primero



, hablemos sobre empujar: en primer lugar, necesitamos determinar que hubo una colisión:

class Collision {
    constructor(o1, o2, dx, dy, d) {
        this.o1 = o1
        this.o2 = o2

        this.dx = dx
        this.dy = dy
        this.d = d
    }
}

function checkCollision(o1, o2) {
    let dx = o2.x - o1.x
    let dy = o2.y - o1.y
    let d = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2))
    if(d < o1.r + o2.r){
        return {
            collisionInfo: new Collision(o1, o2, dx, dy, d),
            collided: true
        }
    }
    return {
        collisionInfo: null,
        collided: false
    }
}

Declaramos una clase de colisión que representa dos objetos en colisión. En la función checkCollision, primero calculamos la diferencia entre las coordenadas x e y de los objetos, luego calculamos su distancia real d. Si la suma de los radios de los objetos es menor que la distancia entre ellos, entonces hubo una colisión de estos objetos: devuelva el objeto Colisión.



Luego, necesitamos determinar la dirección del desplazamiento y su magnitud (magnitud):
n_x = d_x / d ... este es el vector
n_y = d_y / d

s = r_1 + r_2 - d ... esta es la "magnitud" de la colisión (ver imagen a continuación)



En JS, puede parecer Entonces:

function resolveCollision(info){ // "info" -   Collision   
    let nx = info.dx / info.d //  
    let ny = info.dy / info.d
    let s = info.o1.r + info.o2.r - info.d //   
    info.o1.x -= nx * s/2 //       
    info.o1.y -= ny * s/2
    info.o2.x += nx * s/2 //      
    info.o2.y += ny * s/2
}

Rebotar


La parte final del rompecabezas es la implementación del rebote de un objeto de otro en una colisión. No daré todos los cálculos matemáticos, ya que esto hará que el artículo sea muy largo y aburrido, me limitaré al hecho de mencionar la ley de conservación del momento y la ley de conservación de la energía, que ayudan a llegar a la siguiente fórmula mágica:

k = -2 * ((o2.vx - o1.vx) * nx + (o2.vy - o1.vy) * ny) / (1 / o1.m + 1 / o2.m) ... * Magic *

¿Cómo podemos usar magic k? Sabemos en qué dirección se moverán los objetos, pero no sabemos qué tan lejos. Esto es k. Así es como se calcula el vector (z), que muestra dónde deben moverse los objetos:





El código se ve así:

function resolveCollisionWithBounce(info){
    let nx = info.dx / info.dy
    let ny = info.dy / info.d
    let s = info.o1.r + info.o2.r - info.d
    info.o1.x -= nx * s/2
    info.o1.y -= ny * s/2
    info.o2.x += nx * s/2
    info.o2.y += ny * s/2

    // ...
    let k = -2 ((info.o2.vx - info.o1.vx) * nx + (info.o2.vy - info.o1.vy) * ny) / (1/info.o1.m + 1/info.o2.m)
    info.o1.vx -= k * nx / info.o1.m //   ,   "k"   "s/2"  "m"
    info.o1.vy -= k * ny / info.o1.m
    info.o2.vx += k * nx / info.o2.m
    info.o2.vy += k * ny / info.o2.m
}

Conclusión


El artículo tiene muchas ecuaciones, pero la mayoría de ellas son muy simples. Espero que este artículo te haya ayudado un poco a comprender cómo se implementan los fenómenos y procesos físicos en JS.

All Articles