충돌 감지와 2차원에서의 객체 표현 (1)

등록일: 2014. 10. 21

시작하세요! 안드로이드 게임 프로그래밍

  • 마리오 제흐너 지음
  • 유윤선 옮김
  • 832쪽
  • 36,000원
  • 2011년 09월 30일

세계에서 움직이는 객체들이 있다면 이들 객체가 서로 상호작용하는 게 좋다. 이런 상호작용 모델 중 하나가 바로 단순 충돌 감지다. 두 객체가 어떤 식으로든 중첩되면 두 객체는 충돌한 것이다. Mr. Nom 게임에서 Mr. Nom이 자신을 물었는지 잉크 얼룩을 먹었는지 판단할 때도 간단한 충돌 감지를 사용한 바 있다.

충돌 감지는 충돌 반응을 항상 수반한다. 두 객체가 충돌했다고 판단되면 객체의 위치 및/또는 움직임을 적절히 조절해 충돌에 반응해야 한다. 예를 들어 수퍼마리오가 굼바 위에 올라오면, 굼바는 하늘나라로 가고 마리오는 낮은 점프를 하게 된다. 충돌과 충돌 반응을 보여주는 고급 예제로는 둘 이상의 당구공을 들 수 있다. 물론 이 책에서는 이와 같은 충돌 반응까지는 다루지 않는다. 이 책에서 충돌 반응은 주로 객체의 상태를 변경하는 게 전부다(예를 들어 객체가 폭발하거나 죽게 하거나, 동전을 모으고 점수를 설정하는 등). 이런 충돌 반응은 게임 의존적이므로 이 절에서는 다루지 않는다.

그럼 두 객체가 충돌했는지 여부를 어떻게 알 수 있을까? 우선 언제 충돌 검사를 해야 하는지 생각해 봐야 한다. 객체가 앞 절에서 설명한 것 같은 간단한 물리 모델을 따를 경우에는 현재 프레임과 시간 단위에서 모든 객체를 움직인 후 충돌 검사를 수행할 수 있다.

경계 도형

객체의 최종 위치를 지정한 후에는 충돌 검사를 수행할 수 있다. 충돌 검사는 사실 중첩 검사다. 그런데 어느 부분이 중첩될까? 객체는 모두 수학적으로 정의된 형태나 모양을 통해 경계를 표시해야 한다. 이를 나타내는 정확한 용어가 바로 경계 도형이다. 그림 8-8에서는 경계 도형으로 선택한 몇 가지 모양이 나와 있다.

그림 8-8 | Bob 주변의 다양한 경계 모양

그림 8-8에서 경계 도형의 세 타입 속성은 다음과 같다.

  • 삼각형 메시: 몇 개의 삼각형을 사용해 객체의 실루엣에 최대한 가깝게 객체 경계 영역을 설정한다. 이 타입은 가장 많은 저장 공간을 필요로 하며 구성하기도 어렵고 테스트하는 데도 많은 비용이 든다. 하지만 이 타입은 가장 정확한 결과를 제공한다. 렌더링에서 동일한 삼각형을 꼭 사용할 필요는 없고 충돌 감지용으로만 삼각형을 저장하면 된다. 메시는 정점의 리스트로 저장할 수 있는데, 이때 세 정점은 삼각형을 구성한다. 메모리를 절약하기 위해 인덱스를 갖는 정점 리스트를 사용할 수도 있다.
  • 축 정렬된 경계 상자: 축 정렬된 사각형을 통해 객체의 경계를 표시한다. 이 말은 하단과 상단 모서리가 항상 x축으로 정렬되며 좌측과 우측 모서리가 y축 정렬됨을 뜻한다. 이 타입은 테스트를 빠르게 할 수 있지만 삼각형 메시보다는 정확성이 떨어진다. 경계 상자는 보통 좌측 하단 구석에 너비와 높이를 더한 위치 형태로 저장한다(2D의 경우 이를 경계 사각형이라고 부르기도 한다).
  • 경계 원: 객체를 포함할 수 있는 가장 작은 원을 사용해 객체의 경계를 표현한다. 이 방식은 테스트 속도가 가장 빠르지만 가장 정확성이 떨어진다. 원은 보통 중심점과 반지름 형태로 저장한다.

게임의 모든 객체는 위치, 스케일, 방향과 더불어 자신을 포함하는 경계 도형을 갖는다. 물론 객체를 물리적으로 움직일 경우 객체의 위치, 스케일 방향에 따라 경계 도형의 위치, 스케일, 방향을 조절해야 한다.

위치 조절은 쉽게 할 수 있다. 이때는 경계 도형을 적절히 움직이기만 하면 된다. 삼각형 메시의 경우 각 정점을 움직이고, 경계 사각형의 경우 좌측 상단 구석을 움직이며, 경계 원의 경우 중심점을 움직인다.

경계 도형의 스케일 조정은 조금 더 어렵다. 이때는 스케일 조정의 중심이 될 점을 정의해야 한다. 보통 이 점은 객체의 중심에 해당하는 객체의 위치가 된다. 이런 관례를 사용하면 스케일 조정도 쉽게 할 수 있다. 삼각형 메시의 경우 각 정점의 좌표를 스케일 조정한다. 경계 사각형의 경우 너비, 높이, 좌측 하단 구석 위치를 스케일 조정한다. 또 경계 원의 경우 반지름을 스케일 조정한다(원의 중심은 객체의 중심과 같다).

경계 도형을 회전하려면 회전할 중심점을 정의해야 한다. 앞에서 말한 관례를 사용하면(객체의 중심은 회전 중심이다) 회전도 쉽게 할 수 있다. 삼각형 메시의 경우 객체의 중심을 기준으로 모든 정점을 회전시키면 된다. 경계 원의 경우 객체를 아무리 회전하더라도 반지름은 그대로 있으므로 아무것도 할 게 없다. 경계 사각형은 조금 더 복잡하다. 이때는 모든 구석 점을 구성한 후 이를 회전시키고 네 점을 모두 포함하는 축 정렬된 경계 사각형을 찾아야 한다. 그림 8-9에서는 회전 후의 세 경계 도형을 보여준다.

그림 8-9 | 객체의 중심을 회전점으로 사용해 회전한 경계 도형들

삼각형 메시나 경계 원의 회전은 쉬운 반면 축 정렬된 경계 상자의 결과는 그다지 만족스럽지 못하다. 이 그림을 보면 원본 객체의 경계 상자가 회전된 버전보다 더 딱 들어맞는 것을 알 수 있다. 이는 Bob에 대한 경계 도형을 어떻게 설정해야 하는지에 대한 첫 번째 질문을 낳는다.

경계 도형의 구성

이 예제에서는 Bob의 이미지를 기반으로 손으로 경계 도형을 간단히 구성했다. Bob의 이미지는 픽셀로 주어졌으며 세계는 미터로 표현한다. 이 문제의 해결책은 정규화 및 모델 공간에서 찾을 수 있다. 오픈GL을 사용해 Bob을 렌더링할 때 모델 공간에서 Bob에 사용하는 두 개의 삼각형을 떠올려 보자. 사각형은 모델 공간에서 원점을 중심으로 하며 Bob의 텍스처 이미지와 같은 비율(너비/높이)를 갖고 있다(예를 들어 텍스처 맵에서의 32×32 픽셀을 모델 공간에서의 2×2 미터에 대응된다). 이제 Bob의 텍스처를 적용하고 경계 도형의 점이 모델 공간에서 어디에 위치하는지 알 수 있다. 그림 8-10에서는 모델 공간에서 Bob 주변에 경계 도형을 구성할 수 있음을 보여준다.

그림 8-10 | 모델 공간에서의 Bob 주변의 경계 도형

이 과정은 조금 번거롭게 보일 수 있지만 실제 과정은 그다지 어렵지 않다. 먼저 텍스처 매핑이 어떻게 동작하는지 기억해야 한다. 텍스처 공간상의 Bob의 사각형(이 사각형은 두 개의 삼각형으로 이뤄진다)의 각 정점에 대해서는 텍스처 좌표를 지정해야 한다. 실제 이미지의 픽셀 너비나 높이와 상관없이 텍스처 공간에서 텍스처 이미지의 좌측 상단 구석은 (0, 0)에 있으며 좌측 하단 구석은 (1, 1)에 있다. 이미지의 픽셀 공간에서 텍스처 공간으로 변환하려면 다음과 같은 간단한 변형 공식을 사용하면 된다.

u = x / imageWidth
v = y / imageHeight

여기서 u와 v는 이미지 공간에서 x, y로 지정한 픽셀의 텍스처 좌표다. imageWidth와 imageHeight는 이미지의 크기를 픽셀로 지정한다(Bob의 경우는 32 × 32). 그림 8-11에서는 Bob의 이미지 중심이 텍스처 공간에 어떻게 매핑되는지를 보여준다.

그림 8-11 | 이미지 공간에서 텍스처 공간으로의 매핑

텍스처는 모델 공간에서 정의한 사각형에 적용된다. 그림 8-10에서는 좌측 상단 구석이 (-1, 1)이고 우측 하단 구석이 (1, -1)인 예를 보여주고 있다. 이 세계에서는 미터를 단위로 사용하므로 사각형은 너비와 높이가 각각 2미터다. 추가로 여기서는 좌측 상단 구석이 텍스처 좌표 (0, 0)을 갖고 있으며 우측 하단 구석이 텍스처 좌표 (1, 1)을 갖고 있음을 알고 있으므로 전체 텍스처를 Bob에게 매핑시킨다. 하지만 이어지는 절에서 보겠지만 매번 이렇게 되는 것은 아니다.

그럼 텍스처로부터 모델 공간에 매핑하는 일반적인 방법을 만들어 보자. 텍스처와 모델 공간에 있는 축 정렬된 사각형으로 매핑을 제한한다면 작업이 훨씬 쉬울 것이다. 이 말은 텍스처 공간의 축 정렬된 사각형 영역이 모델 공간의 축 정렬된 사각형으로 매핑된다는 뜻이다. 이런 변형을 적용하려면 모델 공간에서의 사각형 너비와 높이 및 텍스처 공간에서의 사각형 너비와 높이를 알아야 한다. Bob 예제의 경우 모델 공간에서는 2×2 사각형을 갖고 있고 텍스처 공간에서는 1×1 사각형을 갖고 있다(왜냐하면 전체 텍스처를 사각형에 매핑하므로). 또 각 공간에서의 각 사각형의 좌측 상단 구석 좌표도 알아야 한다. 모델 공간의 사각형의 경우 이 좌표는 (-1, 1)이고 텍스처 공간의 사각형의 경우 이 좌표는 (0, 0)이다(거듭 말하지만 일부 영역이 아니라 전체 텍스처를 매핑하고 있다). 이 정보와 모델 공간의 매핑할 픽셀의 u/v 좌표를 알고 있으면 다음 두 공식을 사용해 변형을 적용할 수 있다.

mx = (u – minU) / (tWidth) * mWidth + minX
my = (1 – ((v – minV) / (tHeight)) * mHeight - minY

변수 u와 v는 픽셀 공간에서 텍스처 공간으로의 마지막 변형에서 계산한 좌표 값이다. 변수 minU와 minV는 텍스처 공간으로부터 매핑할 영역의 좌측 상단 구석 좌표다. 변수 tWidth와 tHeight는 텍스처 공간 영역의 너비와 높이다. 변수 mWidth와 mHeight는 모델 공간 사각형의 너비와 높이다. 변수 minX와 minY는 모델 공간의 사각형의 좌측 상단 구석 좌표다. 끝으로 mx와 my는 모델 공간에서 변형된 좌표다.

이 공식에서는 u/v 좌표를 가지고 이를 0부터 1 영역으로 매핑한 후 모델 공간에서 스케일과 위치를 조정한다. 그림 8-12에서는 텍스처 공간의 텍셀을 보여주고 이 텍셀이 어떻게 모델 공간의 사각형으로 매핑되는지 보여준다. 양 측면에서는 tWidth, tHeight, mWidth, mHeight를 각각 볼 수 있다. 각 사각형의 좌측 상단 구석은 텍스처 공간의 (minU, minV)와 모델 공간의 (minX, minY)에 대응된다.

그림 8-12 | 텍스처 공간으로부터 모델 공간으로 매핑

앞의 두 공식을 사용하는 대신 픽셀 공간에서 모델 공간으로 직접 넘어갈 수도 있다.

mx = ((x/imageWidth) – minU) / (tWidth) * mWidth + minX
my = (1 – (((y/imageHeight) – minV) / (tHeight)) * mHeight – min

두 공식을 사용하면 텍스처 매핑을 통해 사각형에 매핑할 이미지를 기반으로 객체의 경계 도형을 계산할 수 있다. 삼각형 메시의 경우 이 작업이 조금 더 번거롭다. 경계 사각형과 경계 원의 경우는 훨씬 쉽다. 보통은 이런 어려운 길을 택하지 않고 경계 사각형이 오픈GL ES를 통해 렌더링할 객체의 사각형과 같은 비율을 갖게끔 텍스처를 만들기 위해 노력한다. 이렇게 하면 객체의 이미지 크기로부터 경계 사각형을 직접 구성할 수 있기 때문이다. 경계 원의 경우도 마찬가지다. 하지만 여기서 이런 공식을 설명한 이유는 모델 공간의 사각형으로 매핑될 이미지가 있을 경우 경계 도형을 임의로 구성하는 법을 보여주기 위해서다.

이로써 2D 객체에 딱 들어맞는 경계 도형을 구성하는 법을 알아 보았다. 그래픽 자원을 생성하고 게임 세계에서의 객체 단위와 크기를 정의할 때는 이런 경계 도형의 크기를 직접 정의해야 한다는 사실을 꼭 기억하자.

게임 객체의 어트리뷰트

이제 Bob은 더 뚱뚱해졌다. 이제 렌더링에 사용할 메시(Bob의 이미지 텍스처에 대한 사각형 매핑)뿐 아니라 일정한 형태로 경계를 담고 있는 두 번째 데이터 구조도 갖게 됐다. 모델 공간에서 Bob의 매핑 버전에 따라 경계를 모델링할 경우 실제 경계가 Bob의 사각형을 매핑하는 텍스처 영역과 무관하다는 사실에 주의하자. 물론 경계 도형을 생성할 때는 텍스처의 Bob 이미지 외곽선과 최대한 근접하게 만들도록 노력해야 한다. 하지만 텍스처 이미지의 크기는 32×32이든 128×128 픽셀 크기이든 상관은 없다. 세계에 존재하는 객체는 다음과 같은 세 가지 어트리뷰트 그룹을 갖게 된다.

  • 위치, 방향, 스케일, 속도, 가속도. 이들 속성을 사용하면 앞 절에서 살펴본 물리 모델을 적용할 수 있다. 물론 일부 객체는 정적이므로 위치, 방향, 스케일만 갖고 있다. 때로는 방향과 스케일을 지정하지 않을 수도 있다. 객체의 위치는 그림 8-10처럼 보통 모델 공간에서의 원점과 일치한다. 이렇게 하면 일부 계산이 더 쉬워지는 효과가 있다.
  • 경계 도형(보통 객체의 중심을 기준으로 모델 공간에서 구성)은 그림 8-10에 보이는 것처럼 객체의 위치와 일치하며 객체의 방향 및 스케일을 따라간다. 경계 도형은 세계에서 객체의 경계 범위를 지정하고 크기를 정의한다. 이런 경계 도형은 얼마든지 복잡하게 만들 수 있다. 예를 들어 여러 경계 도형을 합친 형태로 경계 도형을 만들 수도 있다.
  • 그래픽 표현. 그림 8-12에서 볼 수 있듯이 여전히 두 개의 삼각형을 사용해 Bob에 대한 사각형을 구성하고 이미지를 이 사각형에 텍스처 매핑한다. 이 사각형은 그림 8-10에서 볼 수 있듯 모델 공간에서 정의하지만 꼭 경계 사각형과 일치하지 않아도 된다. 오픈GL ES로 보내는 Bob의 그래픽 사각형은 Bob의 경계 사각형보다는 약간 크다.

이런 속성을 따로 떼어내 살펴보면 모델-뷰-컨트롤러(MVC) 패턴을 다시 적용할 수 있다.

  • 모델은 위치, 스케일, 회전, 속도, 가속도, 경계 도형 등으로 구성된 Bob의 물리적인 속성들을 갖고 있다. Bob의 위치, 스케일, 방향은 세계 공간에서 경계 도형이 어느 곳에 위치하는지를 결정한다.
  • 뷰에서는 Bob의 그래픽 표현(예를 들어 모델 공간에서 정의한 두 개의 텍스처 매핑된 삼각형)을 받아들이고 Bob의 위치, 회전값, 스케일에 따라 세계 공간에서 이를 렌더링한다. 이때 앞에서 한 것처럼 오픈GL ES의 매트릭스 연산을 사용할 수 있다.
  • 컨트롤러는 사용자 입력값(예를 들어 왼쪽 버튼을 누르면 Bob이 왼쪽으로 움직이는 등) 및 (앞 절에서 대포에 적용한 것처럼) 중력 가속도 같은 물리적인 힘에 따라 Bob의 물리적인 속성을 업데이트한다.

물론 Bob의 경계 사각형과 텍스처로 된 그래픽 표현 사이에는 어느 정도의 상관성은 있다. 경계 도형 자체가 그래픽 표현을 기반으로 하기 때문이다. 따라서 이 MVC 패턴은 완전히 깔끔한 것은 아니지만 그래도 여전히 사용할 만하다.