본문 바로가기
프로그래밍/공부

[Game] 충돌

by Sik.K 2023. 1. 13.

특정 오브젝트가 다른 오브젝트와 충돌을 했을 때, 이를 어떻게 확인하면 좋을까?

 

우선 단순하게는 해당 객체의 좌표가 다른 객체의 좌표와 겹쳐졌을 때 반응을 일으키면 된다.

 

충돌을 판정하는 알고리즘의 종류는 몇 가지가 있다.

 

1) 픽셀

 

픽셀은 좌표이다. 때문에 특정 픽셀이 어떤 객체와 충돌하였는지를 판별하기 위해선 간단한 코드만 있다면 충분하다.

bool IntersectRectCoord(Object rect, Vector2 pixel)
{
    if(rect.GetWorldPos().x + Rect.scale.x * 0.5f < pixel.x &&
    rect.GetWorldPos().x - Rect.scale.x * 0.5f < pixel.x &&
    rect.GetWorldPos().y + Rect.scale.y * 0.5f < pixel.y &&
    rect.GetWorldPos().y - Rect.scale.y * 0.5f < pixel.y)
    {
    	return true;
    }
    return false;
}

 

대략 이 정도가 될 것이다. 확인하려는 pixel의 x와 y 좌표값을 대조하려는 객체의 중점으로부터 스케일의 반만큼 멀어진 곳의 x, y 좌표들과 비교하면 충돌이 되었는지 확인이 가능하다.

 

2) 원

 

원은 비교적 간단하다. 원은 중점으로부터 360도 방향 전부 선이 존재한다. 그 선들과 중점의 간격은 반지름으로 정의할 수 있고 그 반지름은 원의 객체가 갖는 스케일 값의 절반이다.

 

만약 두 원의 충돌 여부를 확인하려면 어떻게 해야 할까?

 

사진으로 나타내면 이해하기 쉽다.

 

원과 원의 충돌 여부 확인

두 원에는 각각 중점에서부터 원의 둘레와 이어진 반지름 A와 B가 있다.

그리고 두 원의 중점을 잇는 선 C가 있을 때, 만약 C가 A + B 보다 길이가 작아진다면 그것은 무엇을 의미하는 것일까?

 

바로 충돌이다.

 

이를 코드로 구현하면 다음과 같다.

 

bool IntersectCircleCircle(Object circle1, Object circle2)
{
    Vector2 dis = circle1.GetWorldPos() - circle1.GetWorldPos();
    
    if(dis.Length() < ((circle1.scale.x * 0.5) + (circle2.scale.x * 0.5)))
    {
    	return true;
    }
	
    return false;
}

이를 통해 두 원 간의 충돌 여부를 확인할 수 있다.

 

3) AABB(Axis Aligned Bounding Box)

 

3 - 1) 사각형과 사각형

 

축이 나란한 두 객체, 예를 들면 사각형 두 개가 있다고 가정하면 이 둘의 충돌 여부는 어떻게 확인이 가능할까?

 

간단하다. 각 꼭짓점의 좌표값을 비교하면 된다.

 

AABB - 사각형과 사각형

어떤 사각형 두 개가 서로 충돌을 한다고 하면 서로의 꼭짓점이 다른 사각형 내부에 들어가 있다는 것이고, 이는 좌표의 충돌을 의미한다.

 

사각형 A와 B의 각각 왼쪽 아래 꼭짓점과 오른쪽 위의 꼭짓점은 객체가 가지고 있는 가장 작은 좌표값의 정점과 가장 큰 좌표값의 정점이다.

 

이 정점들의 x y를 비교하면 서로 충돌을 하였는지 확인이 가능하다.

 

bool IntersectRectRect(Object rect1, Object rect2)
{
    Vector2 rc1max, rc1min, rc2max, rc2min;
    rc1max = Vector2(rect1.GetWorldPos().x + rect1.scale.x * 0.5, rect1.GetWorldPos().y + rect1.scale.y * 0.5);
    rc1min = Vector2(rect1.GetWorldPos().x - rect1.scale.x * 0.5, rect1.GetWorldPos().y - rect1.scale.y * 0.5);
    rc2max = Vector2(rect2.GetWorldPos().x + rect2.scale.x * 0.5, rect2.GetWorldPos().y + rect2.scale.y * 0.5);
    rc2min = Vector2(rect2.GetWorldPos().x - rect2.scale.x * 0.5, rect2.GetWorldPos().y - rect2.scale.y * 0.5);

    if(rc1max.x > rc2min.x && rc1max.y < rc2min.y &&
    rc2max.x > rc1min.x && rc2max.y < rc1min.y)
    {
       return true;
    }

    return false;
}

 

3 - 2) 사각형과 원

 

그럼 사각형과 원의 충돌은 어떻게 확인할 수 있을까?

 

우선 그림으로 먼저 확인하자.

 

AABB - 사각형과 원(변)

 

간단하게 설명하면 다음과 같다. 사각형 각 변에 비교하려는 원의 반지름 정도 되는 스케일값을 가지는 가상의 사각형을 그리고 그 내부에 원의 중점이 들어오게 되면 충돌로 처리하는 것이다.

 

bool IntersectRectCircle(Object rect, Object circle)
{
    Object rectTempW, rectTempH;
    
    rectTempW.SetWorldPos(rect.GetWorldPos());
    rectTempW.scale = Vector2(rect.scale.x + circle.scale.x * 0.5f, rect.scale.y);

    if(IntersectRectCood(rectTempW, circle.GetWorldPos()))
    {
        return true;
    }
    
    rectTempH.SetWorldPos(rect.GetWorldPos());
    rectTempH.scale = Vector2(rect.scale.x, rect.scale.y + circle.scale.x * 0.5f);

    if(IntersectRectCood(rectTempH, circle.GetWorldPos()))
    {
        return true;
    }
    
    return false;
}

하지만 여기서 문제가 생긴다. 이 경우 각 변에서의 충돌은 확인할 수 있지만 꼭짓점으로 충돌이 일어나면 확인할 수 없다. 때문에 꼭짓점에서의 충돌을 별도로 확인해야 한다.

 

이처럼 우리는 이전에 사용했던 원과 원의 공식을 이용해서 두 객체의 중점의 거리가 각 객체의 중점으로부터의 거리 합이 더 작으면 충돌이 일어났음을 확인할 수 있다.

AABB - 사각형과 원

bool IntersectRectCircle(Object rect, Object circle)
{
    Object rectTempW, rectTempH;
    
    rectTempW.SetWorldPos(rect.GetWorldPos());
    rectTempW.scale = Vector2(rect.scale.x + circle.scale.x * 0.5f, rect.scale.y);

    if(IntersectRectCood(rectTempW, circle.GetWorldPos())) // 세로변의 충돌 확인
    {
        return true;
    }
    
    rectTempH.SetWorldPos(rect.GetWorldPos());
    rectTempH.scale = Vector2(rect.scale.x, rect.scale.y + circle.scale.x * 0.5f);

    if(IntersectRectCood(rectTempH, circle.GetWorldPos())) // 가로변의 충돌 확인
    {
        return true;
    }
    
    // 꼭짓점으로부터의 충돌 확인
    Vector2 edge[4];
    
    edge[0] = Vector2(rect.GetWorldPos().x + rect.scale.x * 0.5f, rect.GetWorldPos().y + rect.scale.y * 0.5f);
    edge[1] = Vector2(rect.GetWorldPos().x + rect.scale.x * 0.5f, rect.GetWorldPos().y - rect.scale.y * 0.5f);
    edge[2] = Vector2(rect.GetWorldPos().x - rect.scale.x * 0.5f, rect.GetWorldPos().y - rect.scale.y * 0.5f);
    edge[3] = Vector2(rect.GetWorldPos().x - rect.scale.x * 0.5f, rect.GetWorldPos().y + rect.scale.y * 0.5f);
    
    for(int i =0; i < 4; ++i)
    {
        Vector2 distance = circle.GetWorldPos() - edge[i];

        if (distance.Length() < cc.scale.x * 0.5f)
        {
            return true;
        }
    }
    
    return false;
}

 

여기까지가 AABB에 관한 충돌을 확인한 것이다. 여기에 회전하는 객체와의 충돌을 확인하는 OBB라는 알고리즘이 있지만 이는 나중에 알아보겠다.

'프로그래밍 > 공부' 카테고리의 다른 글

[Game] 객체 가두기  (0) 2023.01.17
[Game] 충돌 (2)  (0) 2023.01.16
[DirectX] 렌더링 파이프라인 - (2)  (0) 2023.01.06
[DirectX] 셰이더(Shader)  (0) 2023.01.04
[DirectX] 렌더링 파이프라인 - (1)  (0) 2023.01.04

댓글