본문으로 바로가기

 

2020_0601_Framework.zip
0.03MB

 

[도형의 충돌처리]

  a. 각 객체에 enum문으로 모양을 의미하는 변수를 할당, 비교할 객체가 어떤 모양인지 체크

  b. Collision() -> if-else 구문으로 4가지 경우의 수 체크

더보기
bool CollisionScene::Collision(Enemy* enemy)
{
	if (enemy->Mode() == Enemy::SHAPE_MODE::ELLIPSE && m_pPlayer->Mode() == Player::SHAPE_MODE::ELLIPSE)
	{ // 원 - 원 충돌
		if (CollisionCircleCircle(m_pPlayer, enemy))
			return true;
	}
	else if (enemy->Mode() == Enemy::SHAPE_MODE::ELLIPSE && m_pPlayer->Mode() == Player::SHAPE_MODE::RECT)
	{ // 원(Enemy) - 사각형(Player) 충돌
		if ( CollisionCircleRect(enemy, m_pPlayer))
			return true;
	}
	else if (enemy->Mode() == Enemy::SHAPE_MODE::RECT && m_pPlayer->Mode() == Player::SHAPE_MODE::ELLIPSE)
	{ // 사각형(Enemy) - 원(Player) 충돌
		if (CollisionCircleRect(m_pPlayer, enemy))
			return true;
	}
	else if (enemy->Mode() == Enemy::SHAPE_MODE::RECT && m_pPlayer->Mode() == Player::SHAPE_MODE::RECT)
	{ // 사각형 - 사각형 충돌
		if (CollisionRectRect(m_pPlayer, enemy))
			return true;
	}
	return false;
}

 

 

1. Circle <-> Circle 충돌처리( CollisionScene::CollisionCircleCircle() )

  - expr1 : Circle1과 Circle2의 중점 사이의 거리

  - expr2 : Circle1의 반지름 + Circle2의 반지름

  => expr1 < expr2 일 때 충돌

더보기
bool CollisionScene::CollisionCircleCircle(Object* pCircle1, Object* pCircle2)
{
	double dist = Distance({ pCircle1->GetPos() }, { pCircle2->GetPos() });

	if (dist <= (double)(pCircle1->Size().x + pCircle2->Size().x))
		return true;

	return false;
}

 

2. Circle <-> Rect 충돌처리

  - 영역을 Rect의 꼭지점 부분과(4), 모서리 부분으로(4) 구분하여 검사

  - 모서리 부분

    a. Rect.left < Circle.x && Circle.x << Rect.right

    b. Rect.top < Circle.y && Circle.y << Rect.bottom

    c.  ( a || b ) 조건하 Rect <-> Rect(Circle을 Rect로 변환) 충돌검사

  - 꼭지점 부분

    a. case : Right Top 부분 (Circle.x >= Rect.right && Circle.y <= Rect.top) // 4개 귀퉁이 영역

    b. Point p(Rect.right, Rect.top) // p1(left top), p2(right top), p3(left bottom), p4(right bottom)

    c.  각각의 a 조건과 p<->Circle의 충돌검사를 &&연산하고 그 결과를 다시 || 연산하여 결과반환

더보기
bool CollisionScene::CollisionCircleRect(Object* pCircle, Object* pRect)
{
	int x = pCircle->GetPos().x;
	int y = pCircle->GetPos().y;
	int radius = pCircle->Size().x;
	int left = pRect->Left();
	int right = pRect->Right();
	int top = pRect->Top();
	int bottom = pRect->Bottom();
	if ((left <= x && x < right) || (top <= y && y < bottom))
	{
		if (!(
			(y + radius <= top)
			|| (y - radius >= bottom)
			|| (x + radius <= left)
			|| (x - radius >= right)
			))
			return true;
	}
	else
	{
		if (
			((Distance({ x, y }, { right, top }) <= radius) && (x >= right && y <= top))
			|| ((Distance({ x, y }, { right, bottom }) <= radius) && (x >= right && y >= bottom))
			|| ((Distance({ x, y }, { left, top }) <= radius) && (x <= left && y <= top))
			|| ((Distance({ x, y }, { left, bottom }) <= radius) && (x <= left && y >= bottom))
			)
			return true;
	}
	return false;
}

 

3. Rect <-> Circle 충돌처리

  - '2.' 에서 인자 순서만 바꿔서 검사

 

4. Rect <-> Rect 충돌처리

  - 충돌하지 않는 경우를 " | | (or) "로 합한 결과를 ' ! ' 연산

  - ex) Rect1.bottom < Rect2.top // 절대 충돌하지 않는다

더보기
bool CollisionScene::CollisionRectRect(Object* pRect1, Object* pRect2)
{
	if (!(pRect1->Bottom() <= pRect2->Top()
		|| pRect1->Right() <= pRect2->Left()
		|| pRect1->Left() >= pRect2->Right()
		|| pRect1->Top() >= pRect2->Bottom()))
		return true;

	return false;
}

 

전체코드

CollisionScene
더보기
#pragma once

class Player;
class Enemy;

class CollisionScene :public Scene
{
public:
	CollisionScene();
	~CollisionScene();

public:
	virtual bool Init() override;
	virtual void Run() override;
	virtual void Update() override;
	virtual void Render() override;

private:
	Player* m_pPlayer;
	vector<Enemy*> enemys;
	int enemyCount;

public:
	bool Collision(Enemy* enemy);
	bool CollisionCircleRect(Object* pCircle, Object* pRect);
	bool CollisionRectRect(Object* pRect1, Object* pRect2);
	bool CollisionCircleCircle(Object* pCircle1, Object* pCircle2);
	double Distance(POINT p1, POINT p2);
	void RenderUI();
};
///////////////////////////////////////////////////////////////////////////////////////////////
#include "Framework.h"

CollisionScene::CollisionScene()
	:m_pPlayer(nullptr), enemyCount(0)
{
}

CollisionScene::~CollisionScene()
{
}

bool CollisionScene::Init()
{
	m_pPlayer = new Player;

	enemyCount = 6;
	for (int i = 0; i < enemyCount; i++)
	{
		Enemy* enemy = new Enemy;
		enemy->SetRandomSize(); // Pos 설정전 Size 먼저 재설정(순서 준수)
		enemy->SetPos(400 * (i + 1), bufferRT.top + 200);
		if (i >= 3)
		{ // i : 0, 1, 2 => 원형(기본)
			enemy->ChangeMode(1); // i : 3, 4, 5 => 사각형
			enemy->SetPos(400 * (i % 3 + 1), bufferRT.bottom - 200);
		}
		enemys.emplace_back(enemy);
	}
	return true;
}

void CollisionScene::Run()
{
}

void CollisionScene::Update()
{
	m_pPlayer->Update(); // 플레이어를 가장 먼저 Update

	for (int i = 0; i < enemyCount; i++)
	{
		enemys[i]->Update();
		if (Collision(enemys[i]))
		{ // 업데이트 마다 충돌체크해서 색상 변화
			int color = (enemys[i]->Color() + 1) % 7; // 현재색상 받아와서 다음 색상값 할당(Black(0) ~ Yellow(6) 순환)
			enemys[i]->Color(color); // 색상값 변경
			enemys[i]->SetBrush(); // 변경된 색상값을 이용하여 brush 재설정
		}
	}
}

void CollisionScene::Render()
{
	for (int i = 0; i < enemyCount; i++)
		enemys[i]->Render();

	m_pPlayer->Render(); // 플레이어를 가장 나중에 Render
	RenderUI();
}

bool CollisionScene::Collision(Enemy* enemy)
{
	if (enemy->Mode() == Enemy::SHAPE_MODE::ELLIPSE && m_pPlayer->Mode() == Player::SHAPE_MODE::ELLIPSE)
	{ // 원 - 원 충돌
		if (CollisionCircleCircle(m_pPlayer, enemy))
			return true;
	}
	else if (enemy->Mode() == Enemy::SHAPE_MODE::ELLIPSE && m_pPlayer->Mode() == Player::SHAPE_MODE::RECT)
	{ // 원(Enemy) - 사각형(Player) 충돌
		if ( CollisionCircleRect(enemy, m_pPlayer))
			return true;
	}
	else if (enemy->Mode() == Enemy::SHAPE_MODE::RECT && m_pPlayer->Mode() == Player::SHAPE_MODE::ELLIPSE)
	{ // 사각형(Enemy) - 원(Player) 충돌
		if (CollisionCircleRect(m_pPlayer, enemy))
			return true;
	}
	else if (enemy->Mode() == Enemy::SHAPE_MODE::RECT && m_pPlayer->Mode() == Player::SHAPE_MODE::RECT)
	{ // 사각형 - 사각형 충돌
		if (CollisionRectRect(m_pPlayer, enemy))
			return true;
	}
	return false;
}

bool CollisionScene::CollisionCircleRect(Object* pCircle, Object* pRect)
{
	int x = pCircle->GetPos().x;
	int y = pCircle->GetPos().y;
	int radius = pCircle->Size().x;
	int left = pRect->Left();
	int right = pRect->Right();
	int top = pRect->Top();
	int bottom = pRect->Bottom();
	if ((left <= x && x < right) || (top <= y && y < bottom))
	{
		if (!(
			(y + radius <= top)
			|| (y - radius >= bottom)
			|| (x + radius <= left)
			|| (x - radius >= right)
			))
			return true;
	}
	else
	{
		if (
			((Distance({ x, y }, { right, top }) <= radius) && (x >= right && y <= top))
			|| ((Distance({ x, y }, { right, bottom }) <= radius) && (x >= right && y >= bottom))
			|| ((Distance({ x, y }, { left, top }) <= radius) && (x <= left && y <= top))
			|| ((Distance({ x, y }, { left, bottom }) <= radius) && (x <= left && y >= bottom))
			)
			return true;
	}
	return false;
}

bool CollisionScene::CollisionRectRect(Object* pRect1, Object* pRect2)
{
	if (!(pRect1->Bottom() <= pRect2->Top()
		|| pRect1->Right() <= pRect2->Left()
		|| pRect1->Left() >= pRect2->Right()
		|| pRect1->Top() >= pRect2->Bottom()))
		return true;

	return false;
}

bool CollisionScene::CollisionCircleCircle(Object* pCircle1, Object* pCircle2)
{
	double dist = Distance({ pCircle1->GetPos() }, { pCircle2->GetPos() });

	if (dist <= (double)(pCircle1->Size().x + pCircle2->Size().x))
		return true;

	return false;
}

double CollisionScene::Distance(POINT p1, POINT p2)
{
	LONGLONG x = (LONGLONG)p1.x - (LONGLONG)p2.x;
	LONGLONG y = (LONGLONG)p1.y - (LONGLONG)p2.y;
	return sqrt(x * x + y * y);
}

void CollisionScene::RenderUI()
{
	wstring str = L"F1 : 원";
	TextOut(hdc, bufferRT.right - 300, bufferRT.top + 200, str.c_str(), str.length());
	str = L"F2 : 사각형";
	TextOut(hdc, bufferRT.right - 300, bufferRT.top + 240, str.c_str(), str.length());
	str = L"F3 : Size Down(1 pixel)";
	TextOut(hdc, bufferRT.right - 300, bufferRT.top + 280, str.c_str(), str.length());
	str = L"F4 : Size Up(1 pixel)";
	TextOut(hdc, bufferRT.right - 300, bufferRT.top + 320, str.c_str(), str.length());
	str = L"이동 : → ← ↑ ↓";
	TextOut(hdc, bufferRT.right - 300, bufferRT.top + 360, str.c_str(), str.length());
}
Object
더보기
#pragma once

class Object
{
public:
	virtual ~Object() = default;

public:
	virtual void Update() = 0;
	virtual void Render() = 0;

protected:
	POINT m_tPos{};
	POINT size{};
	int m_nID{};

public:
	POINT GetPos() const { return m_tPos; }
	void SetPos(const POINT& pos) { m_tPos = pos; };
	void SetPos(LONG x, LONG y) { m_tPos.x = x; m_tPos.y = y; }

	int	GetID() const { return m_nID; }
	void SetID(int nID) { m_nID = nID; }

	POINT Size() const { return size; }
	void Size(int x, int y) { size.x = x, size.y = y; }


public:
	int Left() { return m_tPos.x - size.x; }
	int Right() { return m_tPos.x + size.x; }
	int Top() { return m_tPos.y - size.y; }
	int Bottom() { return m_tPos.y + size.y; }
};
///////////////////////////////////////////////////////////////////////////////////////////////
Player & Enemy
더보기
#pragma once

class Player : public Object
{
public:
	Player();
	~Player();

public:
	enum class SHAPE_MODE
	{
		NONE,
		RECT,
		ELLIPSE
	};

public:

	SHAPE_MODE Mode() const { return mode; }
	void Mode(SHAPE_MODE _mode) { mode = _mode; }
	void ChangeMode(int nMode);

	double Speed() const { return speed; }
	void Speed(double d) { speed = d; }

	int Color() const { return color; }
	void Color(int n) { color = n; }

public:
	SHAPE_MODE mode;
	double speed;
	int color;
	HBRUSH brush;

public:
	virtual void Update() override;
	virtual void Render() override;

public:
	void InitPlayer();
	void SetBrush();    
// 여기까지 Player, Enemy 동일

// Player에만 있는 함수
public:
	void Move();
	void SizeUp();
	void SizeDown();
    
// Enemy에만 있는 함수
public:
	void SetRandomPos();
	void SetRandomSize();
};
/////////////////////////////////////////////////////////////////////////////////////////////
#include "Framework.h"

Player::Player()
	:mode(SHAPE_MODE::NONE), speed(0.0)
{
	InitPlayer();
}

Player::~Player()
{
}

void Player::ChangeMode(int nMode)
{
	switch (nMode)
	{
	case 0:
		Mode(SHAPE_MODE::ELLIPSE);
		break;
	case 1:
		Mode(SHAPE_MODE::RECT);
		break;
	default:
		break;
	}
}

void Player::Update() // Player만 해당
{
	Move();
	if (GetAsyncKeyState(VK_F1))
		ChangeMode(0);
	else if (GetAsyncKeyState(VK_F2))
		ChangeMode(1);
	else if (GetAsyncKeyState(VK_F3))
		SizeDown();
	else if (GetAsyncKeyState(VK_F4))
		SizeUp();
}

void Player::Render() // Player, Enemy 공통
{
	SelectObject(hdc, brush);
	switch (mode)
	{
	case Player::SHAPE_MODE::RECT:
		Rectangle(hdc, Left(), Top(), Right(), Bottom());
		break;
	case Player::SHAPE_MODE::ELLIPSE:
		Ellipse(hdc, Left(), Top(), Right(), Bottom());
		break;
	}

	wstring str = to_wstring(m_tPos.x) +  L", " + to_wstring(m_tPos.y);
	TextOut(hdc, m_tPos.x - 50, m_tPos.y-20, str.c_str(), str.length());
	wstring str2 = to_wstring(size.x) +  L", " + to_wstring(size.y);
	TextOut(hdc, m_tPos.x - 50, m_tPos.y+20, str2.c_str(), str2.length());
}

void Player::InitPlayer()
{
	// 화면 중앙에 Player 배치
	SetPos(POINT{ (LONG)(bufferRT.right * 0.5), (LONG)(bufferRT.bottom * 0.5) });
	SetID(0);
	Mode(SHAPE_MODE::ELLIPSE);
	Size(100, 100);
	Speed(10.0);
	Color(0); // Enemy는 컬러 기본값 1
	SetBrush();
}

void Player::SetBrush() // Player, Enemy 공통
{
	switch (color)
	{
	case 0: brush = CreateSolidBrush(BLACK); break;
	case 1: brush = CreateSolidBrush(RED); break;
	case 2: brush = CreateSolidBrush(GREEN); break;
	case 3: brush = CreateSolidBrush(BLUE); break;
	case 4: brush = CreateSolidBrush(CYAN); break;
	case 5: brush = CreateSolidBrush(MAGENTA); break;
	case 6: brush = CreateSolidBrush(YELLOW); break;
	case 7: brush = CreateSolidBrush(WHITE); break;
	default: break;
	}
}

// Move, SizeUp&Down : Player만 해당
void Player::Move()
{
	if (GetAsyncKeyState(VK_UP))
		m_tPos.y -= speed * 1.0;
	if (GetAsyncKeyState(VK_DOWN))
		m_tPos.y += speed * 1.0;
	if (GetAsyncKeyState(VK_LEFT))
		m_tPos.x -= speed * 1.0;
	if (GetAsyncKeyState(VK_RIGHT))
		m_tPos.x += speed * 1.0;
}

void Player::SizeUp()
{
	size.x += 1;
	size.y += 1;
}

void Player::SizeDown()
{
	size.x -= 1;
	size.y -= 1;
}

// SetRandomPos, SetRandomSize : Enemy만 해당
void Enemy::SetRandomPos()
{
	LONG x, y;
	x = (rand() % (bufferRT.right - size.x * 2)) + size.x;
	y = (rand() % (bufferRT.bottom - size.y * 2)) + size.y;
	m_tPos.x = x;
	m_tPos.y = y;
}

void Enemy::SetRandomSize()
{
	LONG x;
	x = (rand() % 80) + 50;
	size.x = x;
	size.y = x;
}