Javascript에서 2D 물리 구현

참고: 이 메시지는 martinheinz.dev
실제 애니메이션의 물리와 실현은 매우 복잡하고 어려워 보일 수 있지만 사실은 그렇지 않다.이러한 알고리즘은 속도, 가속도, 중력을 포함한 각종 물리 개념의 실제 시뮬레이션을 만들어 낼 수 있다.

그러면 자바스크립트에서 2D 물리 시뮬레이션을 실현할 때 이 알고리즘들이 어떻게 작동하는지 봅시다!
여기에서 애니메이션과 예제를 볼 수 있습니다: https://martinheinz.github.io/physics-visual/
TL;DR: 내 저장소에서 소스 코드를 찾을 수 있습니다.https://github.com/MartinHeinz/physics-visual

등속 가속 운동


우리는 가장 기본적인 일부터 물건을 움직인다.
만약 우리가 고른 속도로 운동하기만 한다면, 우리는 다음과 같은 코드를 사용할 수 있다.
function move(dt) {
    x += vx * dt;
    y += vy * dt;
}
위의 코드에서 xy 은 타원, 다음 vxvy 은 각각 수평축과 수직축의 속도, dt (시간 증가분) 은 두 타이머 박자 사이의 시간, 자바스크립트의 경우 requestAnimationFrame 에 대한 두 개의 호출입니다.
예를 들어, (150, 50) 에 있는 객체를 서남쪽으로 이동하려면 다음 작업을 수행합니다(단일 선택 후 이동).
x = 150 += -1 * 0.1 -> 149.9
y =  50 +=  1 * 0.1 -> 50.1
그러나 고르게 움직이는 것은 지루하기 때문에 물체의 이동을 가속화시킨다.
function move(dt) {
    vx += ax * dt;
    vy += ay * dt;
    x += vx * dt;
    y += vy * dt;
}
이 코드에서 우리는 x축과 y축의 가속도를 나타내는 axay를 추가했다.우리는 가속도로 속도나 속도의 변화(vx/vy를 계산한 후에 그것으로 예전처럼 물체를 이동한다.이제 앞의 예제를 복사하여 x축에만 가속도 (서쪽으로) 를 추가하면 다음을 얻을 수 있습니다.
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;  Moved further (-0.01) than in previous example!
 y =  50 +=  1   * 0.1 ->  50.1  //  y += vy * dt;

중력


기왕 우리가 물체를 이동할 수 있다면, 물체를 다른 물체로 옮기는 것은 어떻습니까?이게 중력이야.이 점을 실현하기 위해서 우리는 무엇을 보충해야 합니까?
그냥 우리가 뭘 해야 하는지 알려주고 싶었어요.

먼저 고등학교 때의 몇 가지 등식을 되돌아봅시다.
힘 방정식:
F = m * a    ... Force is Mass times Acceleration
a = F / m    ... From that we can derive that force acting on some object (mass) accelerates
만약 우리가 지금 그것을 두 물체가 상호작용하는 힘으로 확장하고 싶다면, 우리는 다음과 같은 것을 얻을 수 있다.

그것은 좀 복잡해졌기 때문에 (적어도 나에게는) 그것을 분해해 봅시다.이 등식 중|F|은 힘의 크기로 두 물체는 모두 같지만 방향이 상반된다.이 물체들은 그 질량m_1m_2에 의해 표시된다.k 여기는 인력 상수이고 r 이 물체의 중심 거리이다.만약 이것이 여전히 큰 의미가 없다면, 다음은 그림이다.

만약 우리가 가시화를 만들고 싶다면, 우리는 최종적으로 두 개 이상의 대상을 얻을 수 있을 것이다. 그렇지?그렇다면 우리가 더 많은 물체가 상호작용할 때 무슨 일이 일어날까?

그림을 보면 두 개의 주황색 물체가 검은색 물체를 끌고 있는 것을 볼 수 있다. 하나는 힘F_1F_2, 우리가 흥미를 느끼는 것은 최종력F이다. 우리는 이렇게 계산할 수 있다.
  • 우리는 먼저 상술한 등식
  • 계산력F_1F_2을 사용한다
  • 그리고 우리는 그것을 벡터로 분해한다:
  • 마지막으로 우리가 얻은 것F:

  • 알겠습니다. 우리는 이미 필요한 수학 지식을 모두 습득했습니다. 지금 코드는 어떻게 됩니까?모든 절차를 생략하고 주석이 있는 최종 코드만 보여 드리겠습니다. 더 많은 정보가 필요하시면 언제든지 연락 주십시오.🙂
    function moveWithGravity(dt, o) {  // "o" refers to Array of objects we are moving
        for (let o1 of o) {  // Zero-out accumulator of forces for each object
            o1.fx = 0;
            o1.fy = 0;
        }
        for (let [i, o1] of o.entries()) {  // For each pair of objects...
            for (let [j, o2] of o.entries()) {
                if (i < j) {  // To not do same pair twice
                    let dx = o2.x - o1.x;  // Compute distance between centers of objects
                    let dy = o2.y - o1.y;
                    let r = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
                    if (r < 1) {  // To avoid division by 0
                        r = 1;
                    }
                    // Compute force for this pair; k = 1000
                    let f = (1000 * o1.m * o2.m) / Math.pow(r, 2);  
                    let fx = f * dx / r;  // Break it down into components
                    let fy = f * dy / r;
                    o1.fx += fx;  // Accumulate for first object
                    o1.fy += fy;
                    o2.fx -= fx;  // And for second object in opposite direction
                    o2.fy -= fy;
                }
            }
        }
        for (let o1 of o) {  // for each object update...
            let ax = o1.fx / o1.m;  // ...acceleration 
            let ay = o1.fy / o1.m;
    
            o1.vx += ax * dt;  // ...speed
            o1.vy += ay * dt;
    
            o1.x += o1.vx * dt;  // ...position
            o1.y += o1.vy * dt;
        }
    }
    

    부딪치다


    물체가 사방으로 이동할 때, 그것들도 어느 점에서 충돌할 것이다.충돌을 해결할 수 있는 두 가지 옵션이 있습니다. 즉, 객체를 충돌 또는 반사로 전환하여 추진 솔루션을 살펴보겠습니다.

    충돌을 해결하기 전에 먼저 두 객체가 충돌하고 있는지 확인해야 합니다.
    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
        }
    }
    
    우리는 먼저 Collision류를 성명하는데, 이 종류는 두 개의 충돌 대상을 나타낸다.checkCollision 함수에서 우리는 먼저 물체 거리의 xy 분량을 계산한 다음에 그들의 실제 거리d를 계산한다.만약 그들의 반경이 그들의 거리d보다 작다면, 그것은 반드시 충돌 중이기 때문에 우리는 새로운 Collision 대상으로 돌아간다.

    이제 충돌을 해결하기 위해서는 위치 이동의 방향과 크기를 알아야 합니다.
    n_x = d_x / d        ... this is eigenvector
    n_y = d_y / d
    
    s = r_1 + r_2 - d  ... s is size of collision (see picture)  
    

    따라서 JavaScript 코드에서는 다음과 같이 됩니다.
    function resolveCollision(info) {  // "info" is a Collision object from above
        let nx = info.dx /info.d;  // Compute eigen vectors
        let ny = info.dy /info.d;
        let s = info.o1.r + info.o2.r - info.d; // Compute penetration depth
        info.o1.x -= nx * s/2;  // Move first object by half of collision size
        info.o1.y -= ny * s/2;
        info.o2.x += nx * s/2;  // Move other object by half of collision size in opposite direction
        info.o2.y += ny * s/2;
    }
    
    이 충돌 솔루션의 상호 작용 예제를 https://martinheinz.github.io/physics-visual/ 에서 볼 수 있습니다(객체 추진 클릭).

    힘껏 충돌을 풀다


    aaaa와 마지막 퍼즐 - 반탄물체를 통해 충돌을 해결한다.이런 상황에서 모든 수학 연산을 생략하는 것이 가장 좋다. 왜냐하면 이것은 문장의 길이를 배로 증가시킬 수 있기 때문이다. 그래서 내가 너희들에게 말하고자 하는 것은 우리가 동량보존법칙과 에너지보존법칙을 고려해야 한다는 것이다. 이것은 우리가 다음과 같은 신기한 방정식을 구축하고 구하는 데 도움이 된다.
    k = -2 * ((o2.vx - o1.vx) * nx + (o2.vy - o1.vy) * ny) / (1/o1.m + 1/o2.m) ... *Magic*
    
    그럼 이 신기한k은 우리에게 어떤 도움이 될까요?우리는 물체가 이동하는 방향(우리는 이전처럼 특징방향량n_xn_y을 사용하여 계산할 수 있지만, 이동하는 방향이 얼마나 되는지 모른다. 이것이 바로 k이다.이것이 바로 우리가 벡터(z를 계산하는 방법이다. 이것은 우리가 이 물체를 어디에서 이동하는지 알려준다.


    이제 마지막 코드:
    function resolveCollisionWithBounce(info) {
        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;
    
        // Magic...
        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;  // Same as before, just added "k" and switched to "m" instead of "s/2"
        info.o1.vy -= k * ny / info.o1.m;
        info.o2.vx += k * nx / info.o2.m;
        info.o2.vy += k * ny / info.o2.m;
    }
    

    결론


    이 글은 많은 수학 지식을 포함하고 있지만 대부분이 간단하기 때문에 나는 이것이 네가 이러한 물리 개념을 이해하고 익히는 데 도움을 줄 수 있기를 바란다.자세한 내용을 보려면 내 저장소 here 와 대화식 프레젠테이션 here 의 코드를 보십시오.

    좋은 웹페이지 즐겨찾기