DirectX8일차_RectCollision
수업내용
0) 게임상에서는 타원의 개념은 없다. 균등스케일을통해 정확한 원만 취급한다.
1) Rect collider
회전행렬을 이용한다.
원 충돌체와 마찬가지로 균등한 스케일로 취급해야한다.
사각형을 그리기위해선 5개의 점을 찍어주어야한다.

한사각형이 회전하지않을 상태에서는 구하기 쉽지만 회전해있는상태에서는 어떻게 될까?
그건 바로 회전하지 않은상태에서 먼저 구한뒤 -> 돌려버리면 되는것이다.
이를위해서 월드 행렬을 이용하는것이다.
Vector2.h 파일의 추가
Vector2 operator*(const Matrix& m) const
{
XMVECTOR coord = XMLoadFloat2(this);
//XMVector2TransformNormal
coord = XMVector2TransformCoord(coord, m);
Vector2 result;
XMStoreFloat2(&result, coord);
return result;
}
행렬을 곱할때 랜더링 값의행렬의 4번째 w값을 1로 두는데 0으로 두고 싶을때,
(0으로 두면 위치값을 받지않음 즉! 벡터의 개념으로만 생각을 한다는뜻이다.!)
사각형끼리의 겹치는부분
rectcollider.h
#pragma once
class RectCollider : public Collider
{
public:
RectCollider(Vector2 size);
~RectCollider() = default;
virtual bool IsPointCollision(Vector2 point) override;
virtual bool IsRectCollision(RectCollider* rect, Vector2* overlap) override;
virtual bool IsCircleCollision(CircleCollider* circle) override;
Vector2 LeftTop();
Vector2 LeftBottom();
Vector2 RightTop();
Vector2 RightBottom();
float Left();
float Right();
float Top();
float Bottom();
Vector2 Size() { return size * GetGlobalScale(); }
Vector2 Half() { return Size() * 0.5f; }
private:
bool IsBetweenPoint(const Vector2& start, const Vector2& end1, const Vector2& end2, const Vector2& point);
bool IsAABB(RectCollider* rect, Vector2* overlap);
bool IsOBB(RectCollider* rect);
private:
Vector2 size;
};
Rectcollider.cpp
#include "Framework.h"
RectCollider::RectCollider(Vector2 size) : size(size)
{
tag = "Rect";
type = Type::RECT;
Vector2 halfSize = size * 0.5f;
vector<Vertex>& vertices = mesh->GetVertices();
vertices.emplace_back(-halfSize.x, +halfSize.y);
vertices.emplace_back(+halfSize.x, +halfSize.y);
vertices.emplace_back(+halfSize.x, -halfSize.y);
vertices.emplace_back(-halfSize.x, -halfSize.y);
vertices.emplace_back(-halfSize.x, +halfSize.y);
mesh->CreateMesh();
}
//회전X
//bool RectCollider::IsPointCollision(Vector2 point)
//{
// if (point.x > Left() && point.x < Right())
// {
// if (point.y > Bottom() && point.y < Top())
// return true;
// }
//
// return false;
//}
//역행렬
//bool RectCollider::IsPointCollision(Vector2 point)
//{
// Vector2 coord = point * XMMatrixInverse(nullptr, world);
//
// if (abs(coord.x) < size.x * 0.5f && abs(coord.y) < size.y * 0.5f)
// return true;
//
// return false;
//}
//Cross
bool RectCollider::IsPointCollision(Vector2 point)
{
bool isLeftTop = IsBetweenPoint(LeftTop(), RightTop(), LeftBottom(), point);
bool isRightBottom = IsBetweenPoint(RightBottom(), RightTop(), LeftBottom(), point);
bool isRightTop = IsBetweenPoint(RightTop(), LeftTop(), RightBottom(), point);
return isLeftTop && isRightBottom && isRightTop;
}
bool RectCollider::IsRectCollision(RectCollider* rect, Vector2* overlap)
{
if (overlap)
return IsAABB(rect, overlap);
return IsOBB(rect);
}
bool RectCollider::IsCircleCollision(CircleCollider* circle)
{
return false;
}
Vector2 RectCollider::LeftTop()
{
Vector2 edge = Vector2(-size.x, +size.y) * 0.5f;
return edge * world;
}
Vector2 RectCollider::LeftBottom()
{
Vector2 edge = Vector2(-size.x, -size.y) * 0.5f;
return edge * world;
}
Vector2 RectCollider::RightTop()
{
Vector2 edge = Vector2(+size.x, +size.y) * 0.5f;
return edge * world;
}
Vector2 RectCollider::RightBottom()
{
Vector2 edge = Vector2(+size.x, -size.y) * 0.5f;
return edge * world;
}
float RectCollider::Left()
{
float minLeft = min(LeftTop().x, LeftBottom().x);
float minRight = min(RightTop().x, RightBottom().x);
return min(minLeft, minRight);
}
float RectCollider::Right()
{
float maxLeft = max(LeftTop().x, LeftBottom().x);
float maxRight = max(RightTop().x, RightBottom().x);
return max(maxLeft, maxRight);
}
float RectCollider::Top()
{
float maxLeft = max(LeftTop().y, LeftBottom().y);
float maxRight = max(RightTop().y, RightBottom().y);
return max(maxLeft, maxRight);
}
float RectCollider::Bottom()
{
float minLeft = min(LeftTop().y, LeftBottom().y);
float minRight = min(RightTop().y, RightBottom().y);
return min(minLeft, minRight);
}
bool RectCollider::IsBetweenPoint(const Vector2& start, const Vector2& end1, const Vector2& end2, const Vector2& point)
{
Vector2 A = end1 - start;
Vector2 B = end2 - start;
Vector2 startToPoint = point - start;
float crossA = Vector2::Cross(A, startToPoint);
float crossB = Vector2::Cross(B, startToPoint);
return crossA * crossB < 0.0f;
}
bool RectCollider::IsAABB(RectCollider* rect, Vector2* overlap)
{
return false;
}
bool RectCollider::IsOBB(RectCollider* rect)
{
return false;
}
2) 외적을 통해 충돌여부 구하기

d라는 점이 내부에 있는지 확인하기 위해 외적을 이용할수도 있다.
만약 d1이라면 외적했을때의 cross값이 양수 X음수 = 음수가 되고,
d2라면 음수 X 음수 = 양수가 나오고 ,
d3라면 양수 X양수 =양수 가나오기 때문에
cross 값이 양수인지 음수인지에 따라 충돌여부를 구할수가 있다.
두벡터를 외적하면 끝없이 펼쳐지는 영역이 모두 속하기 떄문에
총 세번의 외적이 필요하다
왜 두번이 아니라 세번일까?
두번을 외적한다면 아래와 같은 그림이 그려지고 빨간색과 파란색이 겹치는 부분이 충돌인식 범위인것이다.

따라서 세번을 외적해주면 아래와 같은 범위를 가진 그림이 그려지기에 충돌범위를 구하기 위해선 총 세번의 외적이 필요하다.

따라서 내부 코드를 살펴보면
bool RectCollider::IsBetweenPoint(const Vector2& start, const Vector2& end1, const Vector2& end2, const Vector2& point)
{
Vector2 A = end1 - start;
Vector2 B = end2 - start;
Vector2 startToPoint = point - start;
float crossA = Vector2::Cross(A, startToPoint);
float crossB = Vector2::Cross(B, startToPoint);
return crossA * crossB < 0.0f;
}
위의 코드는 외적을 한후 이 영역안에 속해있는지를 true, false로 판별해주는 코드이다.
bool RectCollider::IsPointCollision(Vector2 point)
{
bool isLeftTop = IsBetweenPoint(LeftTop(), RightTop(), LeftBottom(), point);
bool isRightBottom = IsBetweenPoint(RightBottom(), RightTop(), LeftBottom(), point);
bool isRightTop = IsBetweenPoint(RightTop(), LeftTop(), RightBottom(), point);
return isLeftTop && isRightBottom && isRightTop;
}
위의 코드는 점과 사각형이 충돌하였을때를 판별하는 코드
3) AABB || OBB 란?
대표적인 사각형 충돌처리 방법의 갈래라고 할 수 있다.
우선 AABB: (Axis Aligned blinding box)로써 축을 기준으로해서 회전을 하지않은 사각형의 충돌범위를 구할수있다.
하지만 회전한 사각형은 아래의 그림과같이 빨간색또한 범위로 인식하기에 적합하지않다.

따라서 회전을 하거나, 긴 직사각형의 형태일때 OBB (Object blinding box )를 사용한다.
OBB는 겹치는 부분이 삼각형 사각형 등 다양한 형태가 나올수 있기때문에 겹치는 부분은 생각하지 않는다.
bool RectCollider::IsRectCollision(RectCollider* rect, Vector2* overlap)
{
if (overlap)
return IsAABB(rect, overlap);
return IsOBB(rect);
}
bool RectCollider::IsAABB(RectCollider* rect, Vector2* overlap)
{
float left = max(Left(), rect->Left());
float right = min(Right(), rect->Right());
float bottom = max(Bottom(), rect->Bottom());
float top = min(Top(), rect->Top());
overlap->x = right - left;
overlap->y = top - bottom;
return overlap->x > 0 && overlap->y > 0;
}
겹치는 부분overlap 이 있을때는 AABB로 계산, 없을때는 OBB로 계산
4) OBB (중요)
분리축을 각각 2개씩 가진다. (Seperate Axis Theory)
내적공식
5) 원과 사각형 충돌
2가지 방법이있다
1.원을 사각형으로 취급해서 충돌 처리 하는것
2.원을 점으로 취급해서 충돌 처리 하는것
6)
과제:
plane.cpp
#include "Framework.h"
Plane::Plane() : Quad(L"Resources/Textures/Shooting/player.png")
{
collider = new CircleCollider(PLANE_SIZE);
collider->SetParent(this);
parent = new Transform();
parent->SetParent(this);
cursor = new Quad(L"Resources/Textures/Shooting/cursor.png");
cursor->SetParent(collider);
cursor->Translate(Vector2::Right() * 60.0f);
cursor->SetLocalScale({ 0.5f,0.5f });
satellite = new Satellite();
satellite->SetParent(parent);
satellite->Translate(Vector2::Right() * 100.0f);
satellite->SetPivot({50.0f,0.0f});
BulletManager::Get();
}
Plane::~Plane()
{
delete collider;
delete cursor;
delete satellite;
}
void Plane::Update()
{
Move();
AutoRotation();
parent->Rotation(5.0f);
Fire();
UpdateWorld();
collider->UpdateWorld();
parent->UpdateWorld();
cursor->UpdateWorld();
satellite->Update();
}
void Plane::Render()
{
Quad::Render();
collider->Render();
cursor->Render();
satellite->Render();
collider->RenderUI();
}
void Plane::Move()
{
if (KEY->Press('W'))
Translate(Vector2::Up() * speed * DELTA);
if (KEY->Press('S'))
Translate(Vector2::Down() * speed * DELTA);
if (KEY->Press('A'))
Translate(Vector2::Left() * speed * DELTA);
if (KEY->Press('D'))
Translate(Vector2::Right() * speed * DELTA);
}
void Plane::AutoRotation()
{
Quad* target = EnemyManager::Get()->GetClosestEnemy(localPosition);
if (target == nullptr)
return;
direction = target->GetLocalPosition() - localPosition;
float cross = Vector2::Cross(GetRight(), direction);
if (cross> 0)
{
localRotation.z += direction.Angle() * DELTA;
}
else if(cross<0)
{
localRotation.z -= direction.Angle() * DELTA;
}
}
void Plane::Fire()
{
Vector2 temp = mousePos - localPosition;
if (KEY->Down(VK_SPACE))
{
BulletManager::Get()->Fire(cursor->GetGlobalPosition(), GetRight());
}
}
satellite.h
#pragma once
class Satellite : public Quad
{
private:
const Vector2 RECTSIZE = { 200,100 };
public:
Satellite();
~Satellite();
void Update();
void Render();
void SetLevel();
void IsAttackCollision();
RectCollider* GetCollider() { return collider; }
private:
float z;
float speed = 100.0f;
RectCollider* collider;
};
satellite.cpp
#include "Framework.h"
Satellite::Satellite() : Quad(L"Resources/Textures/Shooting/gameover.png")
{
SetLocalScale({ 0.1f,0.1f });
collider = new RectCollider(RECTSIZE);
collider->SetParent(this);
collider->SetLocalScale({ 7.0f,3.0f });
}
Satellite::~Satellite()
{
delete collider;
}
void Satellite::Update()
{
IsAttackCollision();
UpdateWorld();
collider->UpdateWorld();
}
void Satellite::Render()
{
Quad::Render();
collider->Render();
collider->RenderUI();
}
void Satellite::IsAttackCollision()
{
if (EnemyManager::Get()->IsCollision(collider))
{
}
}
void Satellite::SetLevel()
{
}
enemy.cpp
#include "Framework.h"
Enemy::Enemy(): Quad(L"Resources/Textures/Shooting/triEnemy.png")
{
SetActive(false);
localScale = HALF_SCALE;
collider = new CircleCollider(40.0f);
collider->SetParent(this);
}
Enemy::~Enemy()
{
delete collider;
}
void Enemy::Update()
{
if (!IsActive()) return;
if (hp <= 0)
{
SetActive(false);
}
Vector2 direction = (target->GetLocalPosition() - localPosition).Normalized();
Invincible();
Collision();
Translate(direction * speed * DELTA);
localRotation.z = direction.Angle();
UpdateWorld();
collider->UpdateWorld();
}
void Enemy::Render()
{
Quad::Render();
collider->Render();
}
void Enemy::Spawn()
{
hp = MAX_HP;
//랜덤
colorBuffer->SetColor(Random(0.0f, 1.0f), Random(0.0f, 1.0f), Random(0.0f, 1.0f));
speed = Random(MIN_SPEED, MAX_SPEED);
//랜덤위치
switch (Random(0, 4))
{
case 0:
{
pos.x = Random(-INTERVAL, 0);
pos.y = Random(0, SCREEN_HEIGHT);
}
break;
case 1:
{
pos.x = Random(0, SCREEN_WIDTH);
pos.y = Random(SCREEN_HEIGHT, INTERVAL+SCREEN_HEIGHT);
}
break;
case 2:
{
pos.x = Random(SCREEN_WIDTH, INTERVAL+SCREEN_WIDTH);
pos.y = Random(0, SCREEN_HEIGHT);
}
break;
case 3:
{
pos.x = Random(0, SCREEN_WIDTH);
pos.y = Random(0, -INTERVAL);
}
break;
}
SetLocalPosition(pos);
SetActive(true);
}
void Enemy::Collision()
{
if (isInvincible) return;
if (BulletManager::Get()->IsBulletCollision(collider))
{
hp--;
isInvincible = true;
speed = -speed;
}
}
void Enemy::Invincible()
{
if (isInvincible)
{
invincibleTime += DELTA;
collider->GetColor()->SetColor(1, 0, 0);
if (invincibleTime > INVINCIBLE_DURATION)
{
isInvincible = false;
invincibleTime = 0.0f;
speed = -speed;
collider->GetColor()->SetColor(0, 1, 0);
}
}
}