[C++] 3.3 질량-용수철 시스템

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 게임 만들기 연습 문제 패키지 강의를 듣고 정리한 필기입니다.😀
🌜 공부에 사용된 홍정모 교수님의 코드들 보러가기
🌜 [홍정모의 게임 만들기 연습 문제 패키지] 강의 들으러 가기!


Chapter 3. 게임 물리 맛보기 : 질량 - 용수철 시스템

image

스프링은 늘어나고 줄어들고 하며 길이가 변한다.

  • \(m\)
    • 질량이 몰려있는 점. 질점
  • \(k\)
    • 스프링의 딱딱한 정도
    • k가 클수록 뻑뻑하여 스프링이 덜 늘어난다고 볼 수 있다.
  • 한 시스템 안에는 \(m\), \(k\) 가 여러개가 들어갈 수 있다.

image

  • 가속도 = 힘/질량
    • \(\vec{a} = {\vec{f}\over{m}}\)
  • 위에서 구한 가속도로 속도와 위치를 구할 수 있다.
    • 가속도를 시간으로 두번 적분하면 위치
    • 가속도를 시간으로 한번 적분하면 속도


🔔 Spring Force

Hooke’s Law : 질점이 1개일 때 🔵

  • 용수철에 외부적인 힘을 가하는 대상(질점)이 1개 (🔵파란공 1개)
  • 어떤 힘을 받아서 용수철의 원래 길이가 늘어나면
    • 수축. 다시 원래의 길이로 줄어들기 위해 당기는 힘이 작용
  • 어떤 힘을 받아서 용수철의 원래 길이가 줄어들면
    • 팽창. 다시 원래의 길이로 늘어나기 위해 늘어나는 힘이 작용

image

  • \(l_0\)
    • 용수철 원래의 길이
  • \(l\)
    • 용수철의 변형된 현재의 길이
  • \(k\)
    • 용수철의 딱딱한 정도
  • ✨ \(f_{spring}\) ✨
    • 스칼라
    • 원래 길이로 돌아가려는 용수철의 힘
      • 외부의 힘이 아니라 스스로 원래대로 돌아가려는 힘
    • \[f_{spring} = -k(l-l_0)\]
      • \((l-l_0)\)
        • 스칼라
        • 길이 변화분 = 원래 길이 - 변형된 길이
        • 늘어난 길이가 클수록 원래 길이로 돌아가려는 \(f_{spring}\)힘도 강해진다.
        • \(l>l_0\) 👉🏻 \(f_{spring}<0\)ㄱ=
          • 용수철이 늘어났으니 원래대로 줄어들기 위해 수축 줄어들려는 힘 작용
        • \(l<l_0\) 👉🏻 \(f_{spring}>0\)
          • 용수철이 줄어들었으니 원래대로 늘어나기 위해 팽창 늘어나려는 힘 작용
      • \(k\)
        • 용수철이 딱딱한 정도가 높을 수록 용수철이 원래대로 돌아가려는 \(f_{spring}\)힘도 커진다.
      • -
        • 스칼라(단순히 부호로 방향을 나타냄)
        • 힘의 방향을 나타내기 위해 마이너스를 붙였다.
        • \((l-l_0)\)가 양수이면 수축힘이 작용하는데 이때 \(f_{spring}\) 힘의 방향이 음수로 나오게끔 마이너스가 붙음.

Hooke’s Law : 질점이 2개일 때 🔵🔵

image

  • 용수철에 외부적인 힘을 가하는 대상(질점)이 2개 (🔵파란공 2개)
    • 질점이 1개일 때와 다르게 방향이 필요하다. 즉, 벡터가 필요
  • 이때의 용수철은 두 질점 사이를 잇는 거리 벡터가 됨

\[\vec{f_{ij,spring}}=-k(\vert\vec{x_i}-\vec{x_j}\vert-l_0){\vec{x_i}-\vec{x_j}\over\vert\vec{x_i}-\vec{x_j}\vert}\]

  • \(\vec{f_{ij,spring}}\)
    • 벡터
    • \(\vec{x_j}\)(위치벡터)🔵로부터 \(\vec{x_i}\)(위치벡터)🔵로 향하는 방향을 가진 원래 길이대로 돌아가려는 힘 벡터
  • \({\vec{x_i}-\vec{x_j}\over\vert\vec{x_i}-\vec{x_j}\vert}\)
    • 방향
    • \(\vec{x_j}\)🔵로부터 \(\vec{x_i}\)🔵로 향하는 방향 벡터
    • \(\vec{f_{ij,spring}}\) 힘의 방향은 \({\vec{x_i}-\vec{x_j}\over\vert\vec{x_i}-\vec{x_j}\vert}\)와 일치한다.
  • \(\vert\vec{x_i}-\vec{x_j}\vert-l_0\)
    • 스칼라
    • \(\vert\vec{x_i}-\vec{x_j}\vert\)
      • \(\vec{x_j}\)🔵와 \(\vec{x_i}\)🔵사이의 길이
      • 늘어난 후의 길이를 뜻한다.
    • \(l_0\)
      • \(\vec{x_j}\)🔵와 \(\vec{x_i}\)🔵사이의 원래 길이
    • \(\vert\vec{x_i}-\vec{x_j}\vert-l_0\)은 길이 변화분(스칼라)을 의미한다. 스프링이 돌아가야하는 길이분이 됨.
      • 음수면 줄어든 것
      • 양수면 늘어난 것
  • \(k\)
    • 스칼라
    • 스프링의 딱딱한 정도에 비례
  • -
    • 방향
    • \(\vert\vec{x_i}-\vec{x_j}\vert-l_0\), 즉 길이변화분의 부호와 원래 힘으로 돌아가려는 방향의 부호는 반대이기 때문에 마이너스를 붙임
      • 줄어들어서 길이변화분이 음수면 다시 팽창시켜주어야 하고
      • 늘어나서 길이변화분이 양수면 다시 수축시켜주어야함

\(\vec{f_{ij,spring}}\) 의 표현

  1. \((\vert\vec{x_i}-\vec{x_j}\vert-l_0){\vec{x_i}-\vec{x_j}\over\vert\vec{x_i}-\vec{x_j}\vert}\) 를 길이가 변한만큼의 크기로, j🔵공에서 i🔵공을 향하는 방향을 가진 벡터라고 할 수 있다. 이를 \(\vec{V}\)라고 하면 \(\vec{f_{ij,spring}}=-k\vec{V}\)라고 표현할 수 있다.
  2. \(\vec{f_{ij,spring}}\)벡터의 방향은 \({\vec{x_i}-\vec{x_j}\over\vert\vec{x_i}-\vec{x_j}\vert}\)이다.
  3. ‘뉴턴 제 3의 법칙 : 작용-반작용’에 의해 \(\vec{f_{ij,spring}}=-\vec{f_{ji,spring}}\) 가 성립된다.
    • \(\vec{f_{ij,spring}}\) : j 로부터 i 로 향하는 방향을 가진 원래 길이대로 돌아가려는 힘 벡터
    • \(\vec{f_{ji,spring}}\) : i 로부터 j 로 향하는 방향을 가진 원래 길이대로 돌아가려는 힘 벡터
    • 방향이 서로 정반대


🔔 Damping Force

  • Damper
    • 두 물체의 ✨상대속도✨를 줄여준다.
      • 즉, 두 물체의 속도가 비슷해지도록 만든다.
      • 스프링이 출렁거리는 정도를 서서히 줄여 줌
    • 속도를 줄여주는 점성항력 같은 역할
      • 문이 너무 빨리 닫히지 않도록 제어해주는 무거운 문에 붙어있는 장치

Spring Force : 길이의 차이에 의해서 작용 됨

Damping Force : 속도의 차이에 의해서 작용 됨

  • 덤핑 계수 \(d\)
    • 단위 속도 당 물체의 운동을 방해하려는 힘
    • d값을 조정하여 Dumper의 영향을 조절할 수 있다.
  • 스프링이 원래로 돌아가는 과정에서 속도도 점점 줄여줄 필요가 있다.
    • 그래서 Dumper의 영향도 고려!

질점이 1개일 때 🔵

image

  • \[{f_{ij,damping}}=-d(v_i-v_j)\]
    • 스칼라
    • 질점이 1개일때 질점이 아닌 고정된 물체와 질점과의 속도 차이, 즉 상대속도를 0으로 만드려는 힘
      • 🔵질점의 속도가 0이 아니라면 Damper는 이 질점의 속도를 점점 0으로 만들고자 할 것.
    • -
      • 스칼라(단순히 부호로 방향을 나타냄)
      • 상대속도의 반대방향으로 작용. 멈추게 해야하니까.
    • \(d\)
      • 스칼라
      • 덤핑계수
      • 상대속도를 0 으로 만드는 힘
    • \((v_i-v_j)\)
      • 스칼라
      • j🔵질점에서 느끼는 i🔵질점의 상대적인 속도
      • 상대속도
      • 상대속도가 클수록 상대속도를 0으로 만드려는 힘이 크게 작용할 것

질점이 2개일 때 🔵🔵

image

  • \[\vec{f_{ij,damping}}=-d{(\vec{v_i}-\vec{v_j})\cdot{\vec{x_i}-\vec{x_j}\over\vert\vec{x_i}-\vec{x_j}\vert}}×{\vec{x_i}-\vec{x_j}\over\vert\vec{x_i}-\vec{x_j}\vert}\]
    • 외부의 힘을 받아 움직이는 두 질점 🔵🔵의 상대속도(속도 차이)를 0으로 만드는 힘
    • \(\vec{x_j}\)(위치벡터)🔵로부터 \(\vec{x_i}\)(위치벡터)🔵로 향하는 방향을 가진 두 벡터의 상대 속도를 0으로 만드려는 힘을 가진 벡터
    • -
      • 방향
    • \(d\)
      • 스칼라
      • 덤핑계수
    • \((\vec{v_i}-\vec{v_j})\cdot{\vec{x_i}-\vec{x_j}\over\vert\vec{x_i}-\vec{x_j}\vert}\)
      • 스칼라
      • 상대속도 벡터(\(\vec{v_i}-\vec{v_j}\))와 향하던 방향 벡터(\({\vec{x_i}-\vec{x_j}\over\vert\vec{x_i}-\vec{x_j}\vert}\))끼리의 벡터 내적값
        • 양수면 두 질점이 멀어지고 있던 중
          • 즉, 두 질점 사이의 스프링이 팽창하고 있던 중
        • 음수면 두 질점이 가까워지고 있던 중
          • 즉, 두 질점 사이의 스프링이 수축하고 있던 중
        • 사실 양수건 음수건 이 내적값의 절대값이 크다는건 스프링이 수축하는 정도, 혹은 팽창하는 정도가 크다는 것이므로 스프링이 팍팍 움직이고 있다는 것을 의미한다.
        • 이런 상태일 수록 상대 속도를 0 으로 만드려는 힘을 가진 \(f_{ij,damping}\)의 크기 또한 커질 수 밖에 없다.
        • 벡터 내적의 자세한 설명은 벡터내적 포스트 참고
    • \(\vec{x_i}-\vec{x_j}\over\vert\vec{x_i}-\vec{x_j}\vert\)
      • 방향
      • \(\vec{f_{ij,damping}}\)벡터의 방향이 된다.


🔔 빨간 공🔴이 노란 공🟡을 당겨오도록 스프링 구현해보기

image

  • 질점을 1개로 하였다. 🔴rb0은 고정. 🟡rb1은 외부 힘(중력)에 의하여 움직이는 질점.
  • 🔴rb0와 🟡rb1은 선(스프링)으로 연결되어 있다.
  • 🟡공에 스프링 힘을 주기전엔 🟡가 중력의 영향을 받아 끝없이 자유낙하 하는 모습이 된다.
    • \(\vec{f_{ij,spring}}\)와 \(\vec{f_{ij,damping}}\)를 더한 스프링 힘을 주어 🟡이 중력의 영향을 받더라도 스프링 힘에 의해 원래 위치로 돌아가게끔 만들자.
#include "Game2D.h"
#include "Examples/PrimitivesGallery.h"
#include "RandomNumberGenerator.h"
#include "RigidCircle.h"
#include <vector>
#include <memory>

namespace jm
{
	class Example : public Game2D
	{
	public:
		RigidCircle rb0, rb1;

		Example()
			: Game2D()
		{
			reset();
		}

		void reset()
		{
			// Initial position and velocity
			rb0.pos = vec2(0.0f, 0.5f);
			rb0.vel = vec2(0.0f, 0.0f);
			rb0.color = Colors::hotpink;
			rb0.radius = 0.03f;
			rb0.mass = 1.0f;

			rb1.pos = vec2(0.5f, 0.5f);
			rb1.vel = vec2(0.0f, 0.0f);
			rb1.color = Colors::yellow;
			rb1.radius = 0.03f;
			rb1.mass = rb0.mass * std::pow(rb1.radius / rb0.radius, 2.0f);
		}

		void drawWall()
		{
			setLineWidth(5.0f);
			drawLine(Colors::blue, { -1.0f, -1.0f }, Colors::blue, { 1.0f, -1.0f });
			drawLine(Colors::blue, { 1.0f, -1.0f }, Colors::blue, { 1.0f, 1.0f });
			drawLine(Colors::blue, { -1.0f, -1.0f }, Colors::blue, { -1.0f, 1.0f });
		}

		void update() override
		{
			const float dt = getTimeStep() * 0.4f;
			const float epsilon = 0.5f;

			// physics update (Temporarily disabled)
			//rb0.update(dt); ⭐이부분은 필요 없다.
			//rb1.update(dt); ⭐공이 벽에 부딪칠때, 바닥에 부딪칠때를 처리하는 함수였으므로. 이 예제에선 필요 X

			// coefficients
			const vec2 gravity(0.0f, -9.8f);
			const float l0 = 0.5f; // l_0 원래의 초기길이
			const float coeff_k = 100.0f; // 스프링의 딱딱한 정도
			const float coeff_d = 100.0f; // 스프링의 출렁거림을 줄여준다. 🟡속도를 낮추려는 힘.

			// update rb1 (Note: rb0 is fixed)
			{
				const auto distance = (rb1.pos - rb0.pos).getMagnitude();
				const auto direction = (rb1.pos - rb0.pos) / distance;

				// compute stiffness force & damping force
				const auto spring_force = direction * -(distance - l0) * coeff_k  
																+ direction * -(rb1.vel - rb0.vel).getDotProduct(direction) * coeff_d ;

				const auto accel = gravity + spring_force / rb1.mass;

				rb1.vel += accel * dt;
				rb1.pos += rb1.vel * dt;
			}

			// draw
			drawWall();

			// spring
			drawLine(Colors::red, rb0.pos, Colors::red, rb1.pos);

			// mass points
			rb0.draw();
			rb1.draw();

			// reset button
			if (isKeyPressedAndReleased(GLFW_KEY_R)) reset();
		}

	};
}

int main(void)
{
	jm::Example().run();

	return 0;
}
  • const auto distance = (rb1.pos - rb0.pos).getMagnitude();
    • \(\vert\vec{x_i}-\vec{x_j}\vert\)
    • 🟡 중심좌표에서 🔴 중심좌표 사이의 거리
    • 가속도를 받아 계속 늘어났다 줄어들었다 할 것.
  • const auto direction = (rb1.pos - rb0.pos) / distance;
    • \(\vec{x_i}-\vec{x_j}\over\vert\vec{x_i}-\vec{x_j}\vert\)
    • 🟡 중심좌표에서 🔴 중심좌표를 향하는 방향
  • const auto spring_force = direction * -(distance - l0) * coeff_k + direction * -(rb1.vel - rb0.vel).getDotProduct(direction) * coeff_d;
    • \(=\vec{f_{ij,spring}}+\vec{f_{ij,damping}}\)
    • \(k\), \(d\) 모두 고려하여 합한 총 스프링 힘
  • 가속도
    1. 중력 가속도
      • gravity
    2. 스프링힘에 의한 가속도
      • spring_force/rb1.mass
      • 가속도 = 힘 / 질량
  • 🟡의 속도 업데이트
    • rb1.vel += accel * dt;
      • 중력 가속도 + 스프링힘에 의한 가속도
  • 🟡의 위치 업데이트
    • rb1.pos += rb1.vel * dt;


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

C++ games 카테고리 내 다른 글 보러가기

댓글 남기기