정보실

웹학교

정보실

javascript JavaScript에서 2D 물리 구현

본문

사실적인 2D 물리 시뮬레이션 및 시각화를 구현하면서 JavaScript로 재미있게 놀자!


https://towardsdatascience.com/implementing-2d-physics-in-javascript-860a7b152785 


실제 애니메이션의 물리 및 구현은 매우 복잡하고 어려워 보일 수 있지만 실제로는 그렇지 않습니다. 

이 알고리즘은 매우 간단 할 수 있으며 속도, 가속 또는 중력을 포함한 다양한 물리 개념의 사실적인 시뮬레이션을 생성 할 수 있습니다.


JavaScript로 2D 물리 시뮬레이션을 구현하는 동안 알고리즘이 어떻게 작동하는지 봅시다!


1*PmC9jC95yEEUinzsFBzTqw.gif 


여기에서 애니메이션과 예제를 확인할 수 있습니다.


https://martinheinz.github.io/physics-visual/


TL; DR : 소스 코드는 내 저장소에서 사용할 수 있습니다.


https://github.com/MartinHeinz/physics-visual


균일하고 가속화 된 움직임 


물건을 옮기는 가장 기본적인 것부터 시작하겠습니다.


균일한 움직임 만 원한다면 다음과 같은 코드를 사용할 수 있습니다.


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

위의 코드에서 x와 y는 객체의 좌표입니다. 

예를 들어 타원, 다음 vx 및 vy는 각각 가로 및 세로 축의 속도이며 dt (시간 델타)는 타이머의 2 틱 사이의 시간이며 JavaScript의 경우 requestAnimationFrame에 대한 2 번의 호출입니다.


예를 들어-(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 축의 가속도를 나타내는 ax와 ay를 추가했습니다. 

가속도를 사용하여 속도 또는 속도의 변화 (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;

중량 


이제 물건을 움직일 수 있게 되었습니다. 물체를 다른 물체로 옮기는 것은 어떻습니까? 

그게 바로 중력입니다. 

이를 구현하기 위해 무엇을 추가해야 합니까?


우리가 무엇을 하려고 하는지 알고 있습니다.


1*VgTfclV5r9d6i5N5ENMoZg.gif 


먼저, 고등학교의 몇 가지 방정식을 생각해 봅시다.


힘의 방정식 :

F = m * a    ... Force is Mass times Acceleration
a = F / m    ... From that we can derive that force acting on some object (mass) accelerates

이제 서로 작용하는 2 개의 물체를 강제로 확장하려면 다음을 얻습니다.


1*TF8j5ivJyhrBKsXnxSp01A.png 

Force EQ


조금 복잡해 지므로 (적어도) 분해 해 보겠습니다. 이 방정식에서 | F | 힘의 크기는 반대 방향으로 두 물체에 동일합니다. 

이 물체는 질량-m_1 및 m_2로 표시됩니다. 

k는 중력 상수이고 r은이 물체의 무게 중심 거리입니다. 여전히 이해가 되지 않으면 다음 그림이 있습니다.


1*Tu7rIhMSKlzD0WH9ZxSsrA.png 

Objects Pulling


시각화를 만들고 싶다면 2 개 이상의 객체가 생깁니다. 서로 더 많은 물체가 작용하면 어떻게 됩니까?


1*FLE6mHaPTF0vmPLVJlKCuQ.png 

Forces with Multiple Objects


위의 그림을 보면, F_1과 F_2의 힘으로 검은 색을 당기는 2 개의 주황색 물체를 볼 수 있습니다. 우리가 관심을 갖는 것은 최종 힘 F입니다.

  • 먼저 위의 방정식을 사용하여 힘 F_1 및 F_2를 계산합니다.

1*56C1pQcmTY0aARNNc3Qzyw.png 


  • 그런 다음 벡터로 나눕니다.

1*bM2-bT3rFbR-JL0LYfmVbA.png 


자, 우리는 필요한 모든 수학을 가지고 있습니다. 이제 코드는 어떻게 보일까요? 

모든 단계를 아끼지 않고 의견과 함께 최종 코드를 보여 드리겠습니다. 

자세한 정보가 필요하면 언제든지 문의하십시오. 🙂

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

충돌 


물건이 움직이면 어느 시점에서 충돌합니다. 

충돌을 해결하기 위한 두 가지 옵션이 있습니다. 

충돌에서 개체를 밀어 내거나 튀어 나옵니다. 먼저 푸시 솔루션을 살펴 보겠습니다.


1*2l-io5j3ATIo-nsM4PrMlw.gif 


충돌을 해결하기 전에 먼저 두 객체가 실제로 충돌하는지 확인해야 합니다.


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

먼저 2 개의 충돌 객체를 나타내는 Collision 클래스를 선언합니다. 

checkCollision 함수에서 먼저 객체 거리의 x 및 y 구성 요소를 계산 한 다음 실제 거리 d를 계산합니다. 

반지름의 합이 거리 d보다 낮으면 충돌해야 하므로 새 Collision 객체를 반환합니다.


1*uQGNfgun2mqqMbfByRKfhg.png 

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)  

1*sQsDGqGJc8yqk72KRQ1hkw.png 

Movement After Collision


따라서 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/에서 이 충돌 해결의 대화식 예제를 볼 수 있습니다 (Pushing Through Objects 클릭)


힘으로 충돌 해결 


Aaaaand 퍼즐의 마지막 조각 — 물체를 튕겨서 충돌을 해결합니다. 

이 경우 기사를 두 배로 만드는 것처럼 모든 수학을 생략하는 것이 좋습니다. 

그래서 우리가 말할 것은 모멘텀 보존 법칙과 에너지 보존 법칙을 설명하여 우리를 도울 것입니다 

다음과 같은 마법 방정식을 작성하고 해결하십시오


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


이 마법의 k가 어떻게 우리를 도와 주나요? 우리는 객체가 이동하는 방향을 알고 있지만 (n_x 및 n_y와 같이 이전과 같은 고유 벡터를 사용하여 계산할 수 있음), 우리는 그 정도를 알지 못하며 k입니다. 

그래서 이것은 우리가 벡터 (z)를 계산하는 방법입니다.


1*6K-RoQCc3AFCnGCf6dFwmA.png 

1*Mn3v9zHTnRSOkIfGHReP2Q.png 

Momentum


그리고 이제 최종 코드 :


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

결론 


이 게시물에는 많은 수학이 포함되어 있지만 대부분 매우 간단하므로 이 물리적 개념을 이해하고 익히는 데 도움이 되었기를 바랍니다. 

자세한 내용을 보려면 여기 내 저장소 및 대화 형 데모에서 코드를 확인하십시오.



  • 트위터로 보내기
  • 페이스북으로 보내기
  • 구글플러스로 보내기
  • 카카오톡으로 보내기

페이지 정보

조회 12회 ]  작성일20-02-13 17:44

웹학교