프로그래밍/수학

프로그래밍을 위한 수학 - 뉴턴의 운동 법칙

Lou Park 2022. 8. 30. 23:52

 

움직이는 물체는 위치, 속도, 가속도 벡터를 가진다.

가속도는 속도에 영향을 미치고, 속도는 위치에 영향을 미치는 관계다. 프레임마다 물체가 움직인다는 것은 accX가 가속도, velocityX가 속도라고 하면 위치 (x,y)는 대략적으로 다음과 같이 쓸 수 있다.

function move() {
	this.velocityX += this.accX
	this.velocityY += this.accY
	this.x += this.velocityX
	this.y += this.velocityY
}

힘(Force)은 뭘까? 여기서 말하는 힘은 물리학에서 말하는 힘이다.

"힘은 질량을 지닌 물체를 가속하는 벡터"이다. 단순히 한 프레임이 지나면 특정 거리만큼 이동하는 물체가 아니라 "자연"스럽게 이동하는 물체를 구현하려면 뉴턴의 세가지 운동 법칙을 알아 둘 필요가 있다.

 

뉴턴의 제 1법칙: 관성의 법칙

움직이지 않는 물체는 계속 움직이지 않으려하고,

움직이는 물체는 불균형력이 가해지지 않는 한 일정한 속도와 방향으로 움직이려한다.

물체에 가해진 알짜힘이 0일때 물체의 질량 중심의 가속도는 0이라는 것이다. 그러면 질량과 가속도는 무엇일까?

 

뉴턴의 제 2법칙: 가속도의 법칙

너무나도 많이 들어봤을 F=ma. 힘 = 질량 * 가속도다.

이를 가속도에 대한 식으로 정리하면 a = F / m 이다. 이것은 가속도는 힘과 비례하고, 질량에는 반비례한다는 것을 보여준다. 

 

질량(mass)은 물체에 포함되어있는 물질의 양인데, 일반적으로 프로그래밍 할때는 질량의 기준이 명확하지 않다. 프로그래머가 임의의 수로 설정한다. 질량과 무게(weight)는 어떻게 다를까? 무게는 물체에 작용하는 중력의 을 말한다. 뉴턴의 제 2법칙에 따라 무게(F) = 질량(m) * 중력가속도(a)가 된다. 질량이 1kg인 물체는 지구에서도, 달에서도 질량이 1kg이지만, 무게는 달에가면 1/6로 줄어들게 된다. 

 

뉴턴의 제 3법칙: 작용 반작용

힘은 항상 으로 발생한다. 두 힘은 세기가 같지만 방향은 반대다. 물체B에대한 물체 A의 힘인 벡터 V가 있다고 하면 물체 B가 물체 A에 가하는 힘인 V * -1 도 적용해야한다는 것이다. 예시 프로그램에서는 보여주지 않을것이라 따로 얘기하자면, 공이 벽에 부딫혔을때 반대로 튕기는 것이 여기에 해당된다.

Mover.prototype.checkEdges = function() {
    // 가로벽에 부딫혔으므로 vx * -1
    if (this.position.x > width) {
        this.position.x = width;
        this.velocity.x *= -1;
    } else if (this.position.x < 0) {
        this.velocity.x *= -1;
        this.position.x = 0;
    }
    if (this.position.y > height) {
    	// 바닥에 부딫혔으므로 vy * -1
        this.velocity.y *= -1;
        this.position.y = height;
    }
};

속도 벡터에 * -1을 곱하여 반대방향으로 똑같은 힘이 진행되도록 한다.

 

 

적용하기

class Ball {
	constructor() {
    	this.x = 100
        this.y = 100
    }
    
    move() {
    	// ...
    }
}

var ball = Ball();

setInterval(() => {
	ball.move();
}, 1000 / 30)

30FPS의 애니메이션을 만들어보려고 한다. 매 프레임마다 공(Ball)의 move 함수를 호출해서 공을 움직여 줄 것이다. 당장 프로그램을 시작하면 공은 (100, 100)의 위치에 정지한다. 아무런 힘도 공에게 주지 않았기 때문이다.

 

현재까지 구현한 프로그램

 

이 공에게 속도를 줘보겠다.

class Ball {
	constructor() {
    	this.x = 0
        this.y = 0

        this.velocityX = 1;
        this.velocityY = 0;
    }
    
    move() {
    	this.x += this.velocityX
        this.y += this.velocityY
    }
}

velocityX,Y는 각각 X, Y축으로 진행하는 속도다.

이제 공은 매 프레임마다 X축으로 1, Y축으로 0px만큼 움직일 것이다.

 

공은 마치 중력이 없는 것처럼 (ㄹㅇ 없지만ㅋㅋㅋ)

계속 오른쪽으로만 y=100에 맞춰서 진행할것이다. 

 

"자연"스럽지 않다!

이 공에게 중력을 부여해서 얼마못가 바닥에 떨어지게 해보자.

var gravity = 0.7;

class Ball {
	constructor() {
    	this.x = 0
        this.y = 0

        this.velocityX = 1;
        this.velocityY = 1;

        this.accX = 0;
        this.accY = 0;
    }

    addForce(fx, fy) {
        this.accX += fx;
        this.accY += fy;
    }
    
    move() {
        // apply gravity
        this.addForce(0, gravity);

        this.velocityX += this.accX;
        this.velocityY += this.accY;

    	this.x += this.velocityX
        this.y += this.velocityY

        this.accX = 0;
        this.accY = 0;
    }
}

눈에 띄는 건 accX,Y가 생겼다는 것이다. 이것은 가속도 벡터다. addForce 함수를 이용해서 가속도 벡터들을 더해준다. addForce(0, gravity) 중력은 Y축, 즉 위에서 아래로만 작용하는 힘이다.

그래서 accY에만 중력값이 더해지게 된다.

 

 

가속도는 속도에 영향을 미치게된다. velocityX, Y에는 accX, Y만큼 더해지고, 속도는 위치(x, y)에 영향을 준다.

최종적으로는 y축의 값이 중력가속도인 0.7 만큼 더해져서 공을 1초에 21px씩 아래로 떨어지도록 만든다.

다음 프레임에는 이번 프레임에 계산된 가속도가 누적해서 적용되지 않게 하기 위해 this.accX,Y = 0으로 클리어해준다.

 

여전히 석연치 않음을 느낄 것이다. addForce라는 거추장스러운...함수에 대해서.

 

자, 그럼 바람도 추가해보자.

class Ball {
	...
    addForce(fx, fy) {
        this.accX += fx;
        this.accY += fy;
    }
    
    move() {
        // apply gravity
        this.addForce(0, gravity);
        // apply wind
        this.addForce(-1, -0.8);

        this.velocityX += this.accX;
        this.velocityY += this.accY;

    	this.x += this.velocityX
        this.y += this.velocityY

        this.accX = 0;
        this.accY = 0;
    }
}

서쪽으로(-1) 부는 바람인데, 밑에서 토네이도처럼 위로 올라가는 힘(0.8)도 있다. 

서쪽으로 이동하려는 힘이 -1, 속도는 1이다. 그렇다면 물체는? X축으로는 정지한다. 알짜힘이 0이되었기 때문이다. 하지만 이번엔 Y축으로는 물체는 0.1만큼 더 올라가게 된다.

 

 

여태까지 해왔던 코드는 F=ma에서 물체의 질량(mass)를 1이라고 가정하고 F = a로 단순화 시켜서 프로그래밍 했다. 자 그럼 이제 질량이다. 질량을 대략 10이라고 그냥 줘 보겠다.

 

a = F / m이니, addForce 함수는 이렇게 변한다. 질량이 더 커지면 커질수록, 중력이나 바람에 의한 가속도는 적어질 것이다. 

class Ball {
	constructor() {
        this.mass = 10;
    	this.x = 0
        this.y = 0

        this.velocityX = 1;
        this.velocityY = 1;

        this.accX = 0;
        this.accY = 0;
    }

    // 힘 벡터의 합
    addForce(fx, fy) {
        fx /= this.mass
        fy /= this.mass
        this.accX += fx;
        this.accY += fy;
    }
    ...
}