학원/경일게임아카데미

33. 스무번째 수업과제 [원과 원의 충돌]

셩잇님 2023. 1. 11. 16:08
반응형

경일게임아카데미 프로그래밍반 28기 20일차 수업과제 (2021. 05. 06)

 

 

 


 

 

 

오늘은 WIN32 API을 활용하여 원과 원의 충돌을 제작해봅시다!

과제1 - 원과 원의 충돌을 제작해보세요.
조건.
1. 원 하나는 화면 중앙에 위치한다.
2. 또 다른 사용자의 원은 마우스로 움직인다.
3. 두 원이 충돌하면 화면 중앙 원을 색칠시킨다.

 

 

 


 

 

 

playGround.h 소스코드

#pragma once
#include "gameNode.h"

class playGround : public gameNode
{
private:
	
	// 초기화
	RECT _user_Ellipse;				// 사용자 원
	float _user_radius;				// 사용자 원 반지름

	RECT _center_Ellipse;				// 가운데 원
	float _center_Ellipse_x;			// 가운데 원 X축 중점 좌표
	float _center_Ellipse_y;			// 가운데 원 Y축 중점 좌표
	float _center_Ellipse_radius;			// 가운데 원 반지름

	float _distance_x;				// 가운데 원 중점 X 좌표 - 사용자 원 중점 X 좌표 값
	float _distance_y;				// 가운데 원 중점 Y 좌표 - 사용자 원 중점 Y 좌표 값
	float _hypo;					// 빗변

	bool _iscrash;					// 충돌여부

public:
	playGround();
	~playGround();

	virtual HRESULT init();
	virtual void release();
	virtual void update();
	virtual void render(HDC hdc);
};

 

 

 


 

 

 

playGround.cpp 소스코드

#include "stdafx.h"
#include "playGround.h"

playGround::playGround()
{
}

playGround::~playGround()
{
}

//초기화는 여기다 하세요 제발
HRESULT playGround::init()
{
	gameNode::init();

	// 초기화
	_iscrash = false;															// 충돌여부
	_center_Ellipse = RectMakeCenter(WINSIZEX / 2, WINSIZEY / 2, 300, 300); 	// 중앙 원
	return S_OK;
}

//메모리 해제는 여기다 하세요 제발
void playGround::release()
{
	gameNode::release();
}

//여기에다 연산하세요 제에발
void playGround::update()
{
	gameNode::update();

	_user_Ellipse = RectMakeCenter(_ptMouse.x, _ptMouse.y, 100, 100);		// 사용자 원 초기화
	_user_radius = (_user_Ellipse.right - _user_Ellipse.left) / 2;			// 사용장 원 반지름

	_center_Ellipse_x = (_center_Ellipse.left + _center_Ellipse.right) / 2;		// 가운데 원 중점 X 좌표
	_center_Ellipse_y = (_center_Ellipse.top + _center_Ellipse.bottom) / 2;		// 가운데 원 중점 Y 좌표
	_center_Ellipse_radius = (_center_Ellipse.right - _center_Ellipse.left) / 2;	// 가운데 원 반지름

	_distance_x = _center_Ellipse_x - _ptMouse.x;					// 가운데 원 중점 X 좌표 - 사용자 원 중점 X 좌표 값
	_distance_y = _center_Ellipse_y - _ptMouse.y;					// 가운데 원 중점 Y 좌표 - 사용자 원 중점 Y 좌표 값
	_hypo = sqrt(_distance_x * _distance_x + _distance_y * _distance_y);		// 빗변
	
	/*
	-sqrt 함수가 하는일 : 매개변수 x로 들어온 숫자에 루트를 씌워서 계산한 값을 반환해주는 일을 합니다.
	즉, 루트 x를 구해주는 함수입니다.를 구해주는 함수입니다. (제곱근을 구해주는 함수)
	출처: https://blockdmask.tistory.com/307 [개발자 지망생]
	*/

	// 충돌여부 판정
	if ((_user_radius + _center_Ellipse_radius) >= _hypo)
	{
		_iscrash = true;
	}
	else
	{
		_iscrash = false;
	}
}

//여기에다 그려라 좀! 쫌!
void playGround::render(HDC hdc)
{
	HDC backDC = this->getBackBuffer()->getMemDC();
	PatBlt(backDC, 0, 0, WINSIZEX, WINSIZEY, WHITENESS);
	// 위에 건들지마라
	//================제발 이 사이에 좀 그립시다==========================
	
	// 사용자 원과 중앙 원 그려주기
	Ellipse(backDC, _user_Ellipse);
	Ellipse(backDC, _center_Ellipse);

	// 충돌일 때 가운데 원 색칠
	if (_iscrash)
	{
		HBRUSH brush1 = CreateSolidBrush(RGB(0, 0, 0));
		HBRUSH oldBrush1 = (HBRUSH)SelectObject(backDC, brush1);
		Ellipse(backDC, _center_Ellipse);
		SelectObject(backDC, oldBrush1);
		DeleteObject(brush1);
	}

	//==================================================
	//여기도 건들지마라
	this->getBackBuffer()->render(hdc, 0, 0);
}

 

 

 


 

 

 

어떻게 구현해야 할까?

  1. 화면 중심에 원을 만든다
  2. 충돌 로직을 구현한다
  3. 충돌 시 원의 색을 바꿔준다.

 

로직은?

  1. 화면 중앙에 원을 만드는건 간단하다
  2. 유저의 원을 마우스의 x축과 y축을 기점으로 ptMouser를 이용하여 생성해준다.
  3. 충돌의 자세한 내용은 밑에서 서술하겠다.
  4. 충돌 시 원의 색을 바꾸는 것은 bool 값을 이용해 충돌일 때만 색을 칠하게 해준다.

 

 

 


 

 

 

충돌

먼저 충돌을 구하기 위해서는 여러 정보가 필요하다.
과제에서 사용자 원은 마우스를 중점으로 움직이기 때문에 사용자 원을 센터 원 바로 옆에 위치시켜서 그림을 그렸다.

user의 원의 반지름 Radius 이하 R은
user.right에서 user.left를 뺀 값에 /2를 해주면 된다.
왜냐하면 user.right에서 user.left를 뺀 값이 원의 지름이므로
/2를 수행하면 반지름을 구할 수 있게 된다.

user의 원의 중점 좌표 X/Y는
중점 좌표 X는 예전부터 구하는 방법을 알고 있다.
user.left에서 user.right를 더한 값에 / 2를 해주면 X축의 중점 좌표이다.
중점 좌표 Y도 동일하다
user.top에서 user.bottom을 더한 값에 / 2를 해주면 Y축의 중점 좌표이다.

나는 원의 반지름 구하는 것을 중점좌표와 헷갈려서 고생을 많이했다. 바보 🤯
중앙에 있는 원 또한 user의 반지름과 중점 좌표를 구한 것처럼 구해주면 된다.

 

 

 


 

 

 

이제 DisX를 구해보자 Dis는 거리를 뜻한다
그렇다면 DisX는 X의 거리를 구한다는 의미인데 이는 위 사진을 보면 이해할 수 있다.
center_x에서 user_x를 빼면 center와 user의 x축의 거리를 구할 수 있다.

마찬가지로 DisY를 구하려면
center_y에서 user_y를 빼면 center와 user의 y축의 거리를 구할 수 있다.

이제 구한 값을 이용해 피타고라스의 정리를 이용해보자
피타고라스의 정리 값은 a^2 + b^2 = c^2 이다.

여기서 우리는 disX^2 + disY^2를 하게 되면 hypo^2를 알수 있다.
이제 여기서 루트를 사용하자. 루트를 사용하면
root(disX * disX + DisY + DisY)는 Hypo가 된다.

 

 

 


 

 

 

이제 마지막으로 충돌이 되었는지 여부를 확인하자
user의 R + center의 R의 값이 hypo보다 크면 충돌이고,
그렇지 않으면 충돌이 아닌 상태이다.

위 소스코드에서 보면

// 충돌여부 판정
if ((_user_radius + _center_Ellipse_radius) >= _hypo)
{
	_iscrash = true;
}
else
{
	_iscrash = false;
}

이 부분이다.

그렇지만 아래 부분이 이해가 되질 않았다.

if ((_user_radius + _center_Ellipse_radius) >= _hypo)

왜 _user_radius + _center_Ellipse_radius 의 값이 _hypo보다 크면 충돌인가?
혼자서 생각해보다가 더는 모르겠어서 학원에 남아있는 분에게 여쭤보아 알 수 있었다. 😵

사진은 각각 위 왼쪽부터 미충돌, 정확히 충돌, 완전 충돌이다.
실수형에서 정확히 충돌이란 개념이 없겠지만 이는 단순히 이해하기 위해 적은것이다!

 

 

 


 

 

 

결과화면

 

 

 

 


 

 

 

개인적으로 해보고 싶은 심화학습
원과 원의 충돌이기 때문에 따로 무엇을 심화학습을 할 수 있을까? 생각해보았을 때 없는 것 같다.

 

아쉬운점

원과 원의 충돌이 머리로는 이해가 되는데 구현하려고 하니 큰 어려움이 뒤 따랐다.
그렇지만 무엇을 하든 기본이 중요하니 어떻게 구현해야 하는지 다시금 생각해보는 시간이 필요하다고 생각했다.
이로인해 글을 작성하면서 다시금 원과 원의 충돌을 보다 자세하게 작성하였다. ☺

 

 

 

 

반응형