DX11 2D Game Programming
DX11_2020_0727_월_Collision(OBB_Circle)
HI2
2020. 7. 27. 12:32
200727_Circle_Collision_Obb.zip
1.57MB
1. Rect Collider class
- rect<->circle : circle class에서 실행
더보기
bool RectCollider::IsCollision(CircleCollider* circle)
{ // AABB 미구현
if (isObb)
return circle->OBB(this);
//else
// return circle->AABB(this);
}
bool RectCollider::OBB(CircleCollider* circle)
{ // circle class에서 구현
return circle->OBB(this);
}
2. Circle Collider class
a. Collider::CreateData() ( b. Collision 코드 참조)
- Vector2 Rotate() ( radian 각을 이용한 Vector 회전)
더보기
Vector2 Vector2::GetRotatedVector(double angle)
{
Vector2 tmp{};
tmp.x = x * cos(angle) - y * sin(angle);
tmp.y = x * sin(angle) + y * cos(angle);
return tmp;
}
void Vector2::Rotate(double angle)
{
Vector2 tmp{};
tmp.x = x * cos(angle) - y * sin(angle);
tmp.y = x * sin(angle) + y * cos(angle);
//
x = tmp.x;
y = tmp.y;
}
b. Collision
b.1 Point <-> Circle (center <-> point distnce / radius 비교)
b.2 Circle <-> Circle (circle 1&2 center distance / radius 1+2 비교)
b.3 Rect <-> Circle (OBB 충돌 처리 방식)
b.3.a. Rect의 가로축, 세로축을 이용한 사영값 비교
b.3.b. Rect의 꼭지점과 Circle 중점 사이의 거리 / radius 비교(제곱값(Sqr)으로 비교)
더보기


#pragma once
class CircleCollider : public Collider
{
public:
float radius;
bool isObb;
public:
CircleCollider(float radius);
CircleCollider(float radius, Transform* target);
virtual ~CircleCollider();
//
virtual void Update() override;
virtual void CreateData() override;
// Collider을(를) 통해 상속됨
// Circle <-> Point
virtual bool IsCollision(Vector2 position) override;
// overlap 사용x
virtual bool IsCollision(IN RectCollider* rect, OUT Vector2* overlap = nullptr) override;
// Circle <-> Circle
virtual bool IsCollision(CircleCollider* circle) override;
// Circle <-> Collider(Rect or Circle)
virtual bool IsCollision(IN Collider* collider, OUT Vector2* overlap = nullptr);
//
bool OBB(class RectCollider* rect);
//
float SeparateAxis(Vector2 separate, Vector2 e1, Vector2 e2);
ObbDesc GetObb();
//
float Left() { return pos.x - radius + offset.x; }
float Right() { return pos.x + radius + offset.x; }
float Top() { return pos.y + radius + offset.y; }
float Bottom() { return pos.y - radius + offset.y; }
};
//////////////////////////////////////////////////////////////////////////////////////////
#include "Framework.h"
CircleCollider::CircleCollider(float radius)
:radius(radius), isObb(true)
{
CreateData();
}
CircleCollider::CircleCollider(float radius, Transform* target)
: radius(radius), isObb(true), Collider(target)
{
CreateData();
}
CircleCollider::~CircleCollider()
{
}
void CircleCollider::Update()
{
Collider::Update();
}
void CircleCollider::CreateData()
{
type = Type::CIRCLE;
// 원의 중심 + 12시 방향 반지름 위치에서 시작
Vector2 startPos = { offset.x, radius + offset.y }; // 시작점
vertices.emplace_back(startPos.x, startPos.y);
//
Vector2 vertex = startPos;
double angle = PI / 18.0;
int count = 2 * PI / angle;
for (int i = 0; i < count; ++i)
{
vertex.Rotate(angle);
vertices.emplace_back(vertex.x, vertex.y);
} // 마지막점이 시작점과 동일한 위치에 생성
Collider::CreateData();
}
bool CircleCollider::IsCollision(Vector2 position)
{
Vector2 v = position - pos;
float dist = v.Length();
if (dist < radius)
return true;
return false;
}
bool CircleCollider::IsCollision(IN RectCollider* rect, OUT Vector2* overlap)
{ // Circle class에서 구현하고, Rect class에서 호출시 Circle class로 접근하여 실행
return OBB(rect);
}
bool CircleCollider::IsCollision(CircleCollider* circle)
{
float dist = (circle->pos - pos).Length();
if (dist > radius + circle->radius)
return false;
return true;
}
bool CircleCollider::IsCollision(IN Collider* collider, OUT Vector2* overlap)
{
return Collider::IsCollision(collider, overlap);
}
bool CircleCollider::OBB(RectCollider* rect)
{
ObbDesc obb = rect->GetObb();
//
Vector2 nea1 = obb.direction[0];
Vector2 ea1 = nea1 * obb.length[0];
Vector2 nea2 = obb.direction[1];
Vector2 ea2 = nea2 * obb.length[1];
//
Vector2 distance = pos - obb.position;
//
float lengthA{}, lengthB{}, length{};
// 1. sep Axis : nea1
lengthA = radius;
lengthB = SeparateAxis(nea1, ea1, ea2); // b
length = abs(distance.Dot(nea1)); // d
if (length > lengthA + lengthB)
return false;
// 2. sep Axis : nea2
lengthA = radius;
lengthB = SeparateAxis(nea2, ea1, ea2);
length = abs(distance.Dot(nea2));
if (length > lengthA + lengthB)
return false;
// 3. Rect's center to vertex dist(Circle <-> Point(rect's 4 vertex)
float corner_Width = abs(distance.Dot(nea1)) - obb.length[0];
float corner_Height = abs(distance.Dot(nea2)) - obb.length[1];
float corner_LengthSqr = (corner_Width * corner_Width) + (corner_Height * corner_Height);
lengthB = radius * radius;
if (corner_LengthSqr > lengthB)
return false;
//
return true;
}
float CircleCollider::SeparateAxis(Vector2 separate, Vector2 e1, Vector2 e2)
{
float r1 = abs(separate.Dot(e1));
float r2 = abs(separate.Dot(e2));
return (r1 + r2);
}
CircleCollider::ObbDesc CircleCollider::GetObb()
{
ObbDesc obbDesc{};
obbDesc.position = pos;
//
obbDesc.length[0] = radius;
obbDesc.length[1] = radius;
//
Float4x4 world{};
XMStoreFloat4x4(&world, matrix);
obbDesc.direction[0] = { 1.0f, 0 }; // Right vector
obbDesc.direction[0].Normalize();
obbDesc.direction[1] = { 0, 1.0f }; // Up vector
obbDesc.direction[1].Normalize();
//
return obbDesc;
}
3. TestScene
a. RectCollider*, CircleCollider*를 업캐스팅, vector<Collider*>로
일관적인 객체 처리 ( Update, Render, Collision)
더보기
#pragma once
class CollisionTest : public Scene
{
public:
vector<Collider*> colliders;
//
CircleCollider* circle;
CircleCollider* circle2;
RectCollider* rect;
RectCollider* rect2;
//
public:
CollisionTest();
~CollisionTest();
private:
// Scene을(를) 통해 상속됨
virtual void Update() override;
virtual void Render() override;
virtual void PostRender() override;
//
void Collision();
void CollisionMouse();
//
void InitTest();
void DeleteTest();
//
void CollisionTest();
//
void CircleMove();
void RectRotate();
};
//////////////////////////////////////////////////////////////////////////////////////////
#include "Framework.h"
#include "CollisionTest.h"
CollisionTest::CollisionTest()
:colliders{}
{
InitTest();
}
CollisionTest::~CollisionTest()
{
DeleteTest();
}
void CollisionTest::Update()
{
Collision();
//
CircleMove();
RectRotate();
//
circle->Update();
circle2->Update();
rect->Update();
rect2->Update();
}
void CollisionTest::Render()
{
circle->Render();
circle2->Render();
rect->Render();
rect2->Render();
}
void CollisionTest::PostRender()
{
}
void CollisionTest::Collision()
{
for (auto collider : colliders)
{
Vector2* overlap{};
if(circle->IsCollision(collider, overlap))
collider->SetColor({ 1.0f, 0, 0, 1.0f });
else
collider->SetColor({ 0, 1.0f, 0, 1.0f });
}
}
void CollisionTest::CollisionMouse()
{
for (auto collider : colliders)
{
if (collider->IsCollision(MOUSE->GetMousePos()))
collider->SetColor({ 1.0f, 0, 0, 1.0f });
else
collider->SetColor({ 0, 1.0f, 0, 1.0f });
}
}
void CollisionTest::InitTest()
{
// Player 역할을 하는 circle은 colliders에 추가하지 않는다
circle = new CircleCollider(80.0f);
circle->pos = { (float)WinWidth * 0.5f, (float)WinHeight * 0.2f };
//
circle2 = new CircleCollider(80.0f);
circle2->pos = { (float)WinWidth * 0.2f, (float)WinHeight * 0.8f };
circle2->Update();
colliders.emplace_back(circle2);
//
rect = new RectCollider({80.0f,40.0f});
rect->pos = { (float)WinWidth * 0.5f, (float)WinHeight * 0.8f };
rect->angle += PI / 3.0f;
rect->Update();
colliders.emplace_back(rect);
//
rect2 = new RectCollider({80.0f,40.0f});
rect2->pos = { (float)WinWidth * 0.8f, (float)WinHeight * 0.8f };
rect2->Update();
colliders.emplace_back(rect2);
}
void CollisionTest::DeleteTest()
{
delete circle;
//
for (auto collider : colliders)
delete collider;
colliders.clear();
}
void CollisionTest::CircleMove()
{
if (KeyPress(VK_RIGHT))
circle->pos.x += DELTA * 300.0f;
else if (KeyPress(VK_LEFT))
circle->pos.x -= DELTA * 300.0f;
if (KeyPress(VK_UP))
circle->pos.y += DELTA * 300.0f;
else if (KeyPress(VK_DOWN))
circle->pos.y -= DELTA * 300.0f;
}
void CollisionTest::RectRotate()
{
if (KeyPress('W'))
rect->angle += DELTA;
else if (KeyPress('S'))
rect->angle -= DELTA;
}