공부 일지/C++

[C++] 3.1 공 튕기기 시뮬레이션

Roble 2023. 10. 3. 15:20

기본 틀 코드

#include "Game2D.h"
#include "Examples/PrimitivesGallery.h"
#include "RandomNumberGenerator.h"
#include <vector>
#include <memory>

namespace jm
{
	class RigidCircle
	{
	public:
		vec2 pos;  
		vec2 vel;  
		const float radius = 0.1f;

		void draw() // 원 그리기
		{
			beginTransformation();
			{
				translate(pos);
				drawFilledCircle(Colors::hotpink, radius - 1e-3f); 
				setLineWidth(2.0f);
				drawWiredCircle(Colors::black, radius); 
			}
			endTransformation();
		}

		void update(const float & dt)
		{
			// numerical integration 수치 적분
			// wall collision, friction 벽에 부딪치면 튕겨내기, 마찰력
		}
	};

	class Example : public Game2D
	{
	public:
		RigidCircle rigid_body;  // RigidCircle 객체

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

		void reset()
		{
			// Initial position and velocity
			rigid_body.pos = vec2(-0.8f, 0.3f);
			rigid_body.vel = vec2(10.0f, 0.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
		{
			// physics update
			rigid_body.update(getTimeStep() * 0.1f);   // 0.1f 은 애니메이션 속도가 될 것. 더 올려보고 내려보고 해보자

			// draw
			drawWall();
			rigid_body.draw();

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

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

	return 0;
}

 

RigidCircle 클래스

    • 멤버
      • pos 👉 현재 위치
      • vel 👉 현재 속도
      • radius 👉 공의 반지름
    • void update(const float & dt)
      • 공의 위치와 속도를 갱신하는 함수
      • 아래에서 구현할 것

 

 

Example 클래스

  • RigidCircle rigid_body
    • 공 객체 생성
  • void update() override
    • 매 프레임 실행됨 (Game2D의 update 오버라이딩)
      • 공의 위치 & 속도 업데이트
        • rigid_body.update(getTimeStep() * 0.1f);
        • RigidCircle 공 객체의 update함수 호출
      • 벽 그리기
        • drawWall()
      • 공 그리기
        • rigid_body.draw();
        • RigidCircle 공 객체의 draw 호출
      • R키 누르면 리셋
        • if (isKeyPressedAndReleased(GLFW_KEY_R)) reset();
        • void reset 공의 위치와 속도를 초기화

 

 

RigidCircle의 update() 함수 구현하기 : 공 튕기기

1. 위치 갱신 : 공 움직이기

움직임은 물리 이론을 적용시킨다   :   거리(s) = 속력(v) x 시간(t)

void update(const float & dt)
{
	pos = pos + vel * dt;
}

 

공이 수평방향으로 계속 날아간다.

 

2. 속도 갱신 : 중력

void update(const float & dt)
{
	static const vec2 gravity = vec2(0.0f, -9.8f);

	vel = vel + gravity * dt;
	pos = pos + vel * dt;
}

중력(-9.8f)을 받아 공이 점점 아래로 떨어지게 된다.

 

3. 탄성 : 벽에 부딪치면 튕겨지기

void update(const float & dt)
{
	static const vec2 gravity = vec2(0.0f, -9.8f);

	// numerical integration
	vel = vel + gravity * dt;
	pos = pos + vel * dt;

	// wall collision, friction
	if (1.0f - pos.x <= radius) // right wall
	{
		pos.x = 1.0f - radius;
	}
}
  1. 충돌 여부 검사
    • if (1.0f - pos.x <= radius )  '오른쪽 벽과 충돌했다면'
      • 현재 만들어진 벽의 x좌표값이 1.0f 이다 
      • 벽과 공의 중심의 거리 (1.0f -pos.x)
    • pos.x = 1.0f - radius;
      • if 조건이 충족되면(벽에 충돌할 때) pos.x가 그 좌표 고정
      • 중력이 작용중이기 때문에 벽에 쓸려 떨어지게된다.
  2. 튕겨지는 정도 정하기
void update(const float & dt)
{
	static const vec2 gravity = vec2(0.0f, -9.8f);
	static const float coef_res = 0.7f;

	// numerical integration
	vel = vel + gravity * dt;
	pos = pos + vel * dt;

	// wall collision, friction
	if (1.0f - pos.x <= radius) // right wall
	{
		pos.x = 1.0f - radius;
				
		if(vel.x >= 0.0f)   // 안전 장치 
			vel.x *= -1.0f * coef_res;
	}
}
  • static const float coef_res = 0.7f;    '탄성력의 정도를 0.7이라고 하겠다'
  • if(vel.x >= 0.0f)
    • 오른쪽에 부딪치는것이므로 오른쪽 방향 속도가 있는 상태인 경우일때
  • 공의 방향 뒤집히게하기
    • vel.x *= -1.0f;   속도에 -1.0f 를 곱해서 방향 뒤집기
  • 공의 질량, 공의 딱딱한 정도, 벽의 딱딱한 정도 등에 따라 탄성력과 튕겨질때의 속도가 달라진다
    • vel.x *= -1.0f * coef_res;
    • coef_res 값이 1.0f 기준으로 크면 더 빠르게 작으면 더 느린 속도로 튕겨지게 된다

4. 나머지 벽 완성하기

void update(const float & dt)
{
	static const vec2 gravity = vec2(0.0f, -9.8f);
	static const float coef_res = 1.0f;

	// numerical integration
	vel = vel + gravity * dt;
	pos = pos + vel * dt;

	// wall collision, friction
	if (1.0f - pos.x <= radius) // right wall
	{
		pos.x = 1.0f - radius;

		if(vel.x >= 0.0f)
			vel.x *= -1.0f * coef_res;
	}

	if (pos.y <= -1.0f + radius) // ground
	{
		pos.y = -1.0f + radius;
				
		if (vel.y <= 0.0f)
			vel.y *= -1.0f * coef_res;
	}

	if (pos.x <= -1.0f + radius) // left wall
	{
		pos.x = -1.0f + radius;

		if (vel.x <= 0.0f)
			vel.x *= -1.0f * coef_res;
	}
}

 

5. 마찰력 : 공이 바닥에 닿으면 생기는 반대 힘

if (pos.y <= -1.0f + radius) // ground
{
	pos.y = -1.0f + radius;
				
	if (vel.y <= 0.0f)
		vel.y *= -1.0f * coef_res;

	vel.x *= coef_friction; // ground 부분에 마찰력을 추가한 한줄
}
  • static const float coef_friction = 0.9f;        (물리 이론x) 위에서 마찰력을 0.9f로 정해두었었다.
  • vel.x *= coef_friction;              공의 방향상관없이 x속도가 마찰력에 곱해져 느려진다.

 

update 부분 완성 코드

void update(const float & dt)
{
	static const vec2 gravity = vec2(0.0f, -9.8f);
	static const float coef_res = 0.7f; // coefficient of restitution
	static const float coef_friction = 0.9f; // friction  (not physical)

	// 공의 위치와 속도 업데이트
	vel = vel + gravity * dt;
	pos = pos + vel * dt;

	// 각각의 벽에 부딪쳤을 때 처리
	if (1.0f - pos.x <= radius) // right wall
	{
		pos.x = 1.0f - radius; // 벽을 넘어가지 않게

		if(vel.x >= 0.0f)
			vel.x *= -1.0f * coef_res; // 탄성력에 따른 속도 조정
	}

	if (pos.y <= -1.0f + radius) // ground
	{
		pos.y = -1.0f + radius;
				
		if (vel.y <= 0.0f)
			vel.y *= -1.0f * coef_res;

		vel.x *= coef_friction;  // 바닥에 부딪쳤을땐 특별히 마찰력에 따른 x축 방향의 속도 조정
	}

	if (pos.x <= -1.0f + radius) // left wall
	{
		pos.x = -1.0f + radius;

		if (vel.x <= 0.0f)
			vel.x *= -1.0f * coef_res;
	}
}