Nous travaillons avec des graphiques en deux dimensions en JavaScript

Bonjour mes amis!

Créer des animations réalistes de processus physiques peut sembler une tâche intimidante, mais ce n'est pas le cas. Les algorithmes utilisés pour cela peuvent être très simples et en même temps reproduire avec précision des phénomènes physiques tels que le mouvement, l'accélération et la gravité (attraction).

Vous voulez savoir comment ces algorithmes sont implémentés dans JS?



Des exemples peuvent être trouvés ici .

Le code source est ici .

Mouvement uniforme et accéléré


Commençons par le mouvement.

Pour un mouvement uniforme, nous pouvons utiliser le code suivant:

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

Ici x et y sont les coordonnées de l'objet, vx et vy sont la vitesse de l'objet le long des axes horizontal et vertical, respectivement, dt (time delta - time delta) est le temps entre deux marques de minuterie, ce qui en JS équivaut à deux appels à requestAnimationFrame.

Par exemple, si nous voulons déplacer un objet situé à un point avec les coordonnées 150, 50 vers le sud-ouest, nous pouvons faire ce qui suit (une marque de minuterie ou une étape):

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

Même le mouvement est ennuyeux, alors donnons à notre objet une accélération:

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

Ici, ax et ay sont l'accélération le long des axes x et y, respectivement. Nous utilisons l'accélération pour changer la vitesse (vx / vy). Maintenant, si nous prenons l'exemple précédent et ajoutons une accélération le long de l'axe x (à l'ouest), nous obtenons ce qui suit:

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

La gravité


Nous avons appris à déplacer des objets individuels. Que diriez-vous d'apprendre à les déplacer les uns par rapport aux autres? C'est ce qu'on appelle la gravité ou la gravité. Que devons-nous faire pour cela?

Voici ce que nous voulons obtenir:



Tout d'abord, rappelons quelques équations du lycée.

La force appliquée au corps est calculée par la formule suivante:
F = m * a ... la force est égale à la masse multipliée par l'accélération
a = F / m ... à partir de cela, nous pouvons conclure que la force agit sur l'objet avec accélération

Si nous appliquons cela à deux objets en interaction, nous obtenons ce qui suit:



Cela semble compliqué (au moins pour moi), alors faisons les choses correctement. Dans cette équation, | F | - c'est une amplitude de force qui est la même pour les deux objets, mais dirigée dans des directions opposées. Les objets sont représentés par les masses m_1 et m_2. k est la constante gravitationnelle et r est la distance entre les centres de masse des objets. Toujours pas clair? Voici une illustration:



Si nous voulons faire quelque chose d'intéressant, nous avons besoin de plus de deux objets.



Dans cette image, nous voyons deux objets orange attirer le noir avec les forces F_1 et F_2, mais nous nous intéressons à la force résultante F, que nous pouvons calculer comme suit:

  • on calcule d'abord les forces F_1 et F_2 en utilisant la formule précédente:
  • puis traduisez tout en vecteurs:

Génial, nous avons tous les calculs nécessaires. Comment traduire cela en code? Je ne vous ennuierai pas avec les étapes intermédiaires et je donnerai immédiatement le code fini avec des commentaires. Si vous avez besoin de plus d'informations, vous pouvez m'écrire, je répondrai à toutes vos questions.

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
    }
}

Choc


Les corps en mouvement entrent parfois en collision. Lors d'une collision, soit des objets sont poussés par d'autres, soit certains objets rebondissent sur d'autres. Tout d'abord



, parlons de pousser: Tout d'abord, nous devons déterminer qu'il y a eu une collision:

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
    }
}

Nous déclarons une classe Collision qui représente deux objets en collision. Dans la fonction checkCollision, nous calculons d'abord la différence entre les coordonnées x et y des objets, puis nous calculons leur distance réelle d. Si la somme des rayons des objets est inférieure à la distance entre eux, alors il y a eu collision de ces objets - renvoyez l'objet Collision.



Ensuite, nous devons déterminer la direction du déplacement et sa magnitude (magnitude):
n_x = d_x / d ... c'est le vecteur
n_y = d_y / d

s = r_1 + r_2 - d ... c'est la "magnitude" de la collision (voir photo ci-dessous)



En JS, cela peut sembler Donc:

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
}

Rebondir


La dernière partie du puzzle est la mise en œuvre du rebond d'un objet d'un autre lors d'une collision. Je ne donnerai pas tous les calculs mathématiques, car cela rendra l'article très long et ennuyeux, je me limiterai au fait que je mentionne la loi de conservation de l'élan et la loi de conservation de l'énergie, qui aident à arriver à la formule magique suivante:

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

Comment utiliser magic k? Nous savons dans quelle direction les objets se déplaceront, mais nous ne savons pas jusqu'où. C'est k. Voici comment le vecteur (z) est calculé, montrant où les objets doivent se déplacer:





Le code ressemble à ceci:

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
}

Conclusion


L'article a de nombreuses équations, mais la plupart d'entre elles sont très simples. J'espère que cet article vous a un peu aidé à comprendre comment les phénomènes et processus physiques sont implémentés dans JS.

All Articles