[도형의 충돌처리]
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;
}
'WinAPI Programming' 카테고리의 다른 글
WinAPI_2020_0605_금_알카노이드 (0) | 2020.06.08 |
---|---|
WinAPI_2020_0604_목_포트리스 게임 (0) | 2020.06.05 |
WinAPI_2020_0603_수_Vector_Line충돌_화살피하기 게임 (0) | 2020.06.04 |
WinAPI_2020_0602_화_충돌처리_똥피하기 게임 (0) | 2020.06.03 |
WinAPI_2020_0528_목_Point <-> Circle 충돌처리 (1) | 2020.05.31 |