공부 일지/C++

[C++] 3.2 공 두 개를 충돌시켜보자

Roble 2023. 12. 12. 14:08

RigidCircle.h

"공" 객체를 찍어낼 클래스를 포함한 헤더

#pragma once

#include "Game2D.h"

namespace jm
{
	class RigidCircle
	{
	public:
		vec2 pos;
		vec2 vel;
		RGB color = Colors::hotpink;
		float radius = 0.1f;
		float mass = 1.0f;

	public:
		void draw()
		{
			beginTransformation();
			{
				translate(pos);
				drawFilledCircle(color, radius - 1e-3f);
				setLineWidth(2.0f);
				drawWiredCircle(Colors::black, radius);
			}
			endTransformation();
		}

		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.99f; // friction (not physical)

			// numerical integration
			vel += gravity * dt;
			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.x <= -1.0f + radius) // left 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;
			}
		}
	};
}

 

 

Vector2.h

여기서 함수 앞 "T"는 template으로 자료형을 값에 맞게 정해주는 기능이다.

이 강의에서는 float값을 사용하므로 float형으로 스칼라 값을 리턴될 것이다.

<Vector.h>

T getDotProduct(const Vector2<T>& v) const  // 매개변수로 받은 벡터와의 내적
{
		return x * v.x + y * v.y;
}

T getMagnitude()  // 벡터의 크기 
{
		return std::sqrt(x * x + y * y);
}

 

main

#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() 
		{
			// 두 공의 멤버 값들 초기화 
			rb0.pos = vec2(-0.8f, 0.3f);
			rb0.vel = vec2(5.0f, 0.0f); 
			rb0.color = Colors::hotpink;
			rb0.radius = 0.1f;
			rb0.mass = 1.0f; // 

			rb1.pos = vec2(0.8f, 0.3f);
			rb1.vel = vec2(-5.0f, 0.0f); 
			rb1.color = Colors::yellow;
			rb1.radius = 0.15f;
			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
			rb0.update(dt);  
			rb1.update(dt);

			// check collision between two rigid bodies
			const float distance = (rb0.pos - rb1.pos).getMagnitude(); 

			if (distance <= rb0.radius + rb1.radius)   // 충돌했을 때 
			{
				// compute impulse
				const auto vel_rel = rb0.vel - rb1.vel;    // 상대 속도 구하기
				const auto normal = (rb0.pos - rb1.pos) / (rb0.pos - rb1.pos).getMagnitude(); // 노말 벡터 구하기 n1

				if (vel_rel.getDotProduct(normal) < 0.0f) // approaching, 두 개의 공이 가까워지고 있을 때만 충격량 계산
				{      
					const auto impulse = normal * -(1.0f + epsilon) * vel_rel.getDotProduct(normal) /
						((1.0f / rb0.mass) + (1.0f / rb1.mass));

					// update velocities of two bodies
					rb0.vel += impulse / rb0.mass; // 🔴원의 새로운 속도 
					rb1.vel -= impulse / rb1.mass; // 🟡원의 새로운 속도
				}
			}

			// draw
			drawWall();
			rb0.draw();
			rb1.draw();

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

	};
}

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

	return 0;
}