사실적인 2D 물리 시뮬레이션 및 시각화를 구현하면서 JavaScript로 재미있게 놀자!
https://towardsdatascience.com/implementing-2d-physics-in-javascript-860a7b152785
실제 애니메이션의 물리 및 구현은 매우 복잡하고 어려워 보일 수 있지만 실제로는 그렇지 않습니다.
이 알고리즘은 매우 간단 할 수 있으며 속도, 가속 또는 중력을 포함한 다양한 물리 개념의 사실적인 시뮬레이션을 생성 할 수 있습니다.
JavaScript로 2D 물리 시뮬레이션을 구현하는 동안 알고리즘이 어떻게 작동하는지 봅시다!
여기에서 애니메이션과 예제를 확인할 수 있습니다.
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;
중량
이제 물건을 움직일 수 있게 되었습니다. 물체를 다른 물체로 옮기는 것은 어떻습니까?
그게 바로 중력입니다.
이를 구현하기 위해 무엇을 추가해야 합니까?
우리가 무엇을 하려고 하는지 알고 있습니다.
먼저, 고등학교의 몇 가지 방정식을 생각해 봅시다.
힘의 방정식 :
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 개의 물체를 강제로 확장하려면 다음을 얻습니다.
Force EQ
조금 복잡해 지므로 (적어도) 분해 해 보겠습니다. 이 방정식에서 | F | 힘의 크기는 반대 방향으로 두 물체에 동일합니다.
이 물체는 질량-m_1 및 m_2로 표시됩니다.
k는 중력 상수이고 r은이 물체의 무게 중심 거리입니다. 여전히 이해가 되지 않으면 다음 그림이 있습니다.
Objects Pulling
시각화를 만들고 싶다면 2 개 이상의 객체가 생깁니다. 서로 더 많은 물체가 작용하면 어떻게 됩니까?
Forces with Multiple Objects
위의 그림을 보면, F_1과 F_2의 힘으로 검은 색을 당기는 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 } }
먼저 2 개의 충돌 객체를 나타내는 Collision 클래스를 선언합니다.
checkCollision 함수에서 먼저 객체 거리의 x 및 y 구성 요소를 계산 한 다음 실제 거리 d를 계산합니다.
반지름의 합이 거리 d보다 낮으면 충돌해야 하므로 새 Collision 객체를 반환합니다.
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)
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)를 계산하는 방법입니다.
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; }
결론
이 게시물에는 많은 수학이 포함되어 있지만 대부분 매우 간단하므로 이 물리적 개념을 이해하고 익히는 데 도움이 되었기를 바랍니다.
자세한 내용을 보려면 여기 내 저장소 및 대화 형 데모에서 코드를 확인하십시오.
등록된 댓글이 없습니다.