Win32 API 기초 : Collider (4)
Start 씬에서 두 개의 그룹을 받으면
그 두 그룹은 서로 충돌 가능한 그룹으로 볼 것입니다.
그래서 Start 씬을 세팅하기 위한 함수
Enter의 46번째 줄에 있는 코드를 보시면
콜리전 매니저가 플레이어와 몬스터 그룹을
인자로 받는 CheckGroup 함수를 호출하고 있죠.
CheckGroup 함수는 콜리전 매니저의 멤버 함수로
두 그룹을 인자로 받습니다.
일단 왼쪽 인자를 행으로,
오른쪽 인자를 열로 봅니다.
근데 두 값을 비교했을 때
왼쪽의 값이 더 크다면
왼쪽 인자의 값을 열로,
오른쪽 인자의 값을 행으로 보게 됩니다.
즉 더 작은 값을 행으로, 큰 값을 열로 하는 거죠.
그리고 n번 행과 1을 열의 값만큼
시프트 연산한 값을 & 연산합니다.
이 연산의 의미는 만약 n번 행에
특정 그룹이 이미 충돌하도록 설정된 상태에서
특정 그룹을 다시 충돌하도록 설정하면 오히려
체크가 해제되게 하겠다는 것이죠.
( 마치 체크 박스와 같은 것이죠. 체크 박스가 빈 상태에서
클릭하면 체크되고 체크된 상태에서 다시 체크하면
빈 상태로 돌아가는 것처럼 말이죠. )
만약 2번 그룹과 3번 그룹을 인자로 받았다고 하면
2번 행에서 3번째 비트에 해당하는 곳과
& 연산을 진행하여 값이 1이 나왔다면
이미 2번 그룹과 3번 그룹이 충돌되도록 설정된 상태이기
때문에 1이 3번 왼쪽 시프트 연산된 상태인 값을
~ 연산으로 반전시킨 후 2번 그룹에 & 연산하여 대입합니다.
그러면 3번째 비트가 0이 되면서 해제될 것입니다.
근데 2번 그룹과 3번 그룹이 아직 충돌 설정이
되지 않은 상태라면 | 연산을 하여 대입시키면
2번 그룹의 3번째 비트가 1이 되겠죠.
이로써 2번 그룹과 3번 그룹은 충돌 가능한 상태가 되는 것이죠.
콜리전 매니저도 프레임마다 업데이트하면서
충돌 가능 그룹들에 대한 충돌 처리를 해주어야겠죠.
그래서 for 문을 통해 32개의 그룹이 자기 자신을 포함한
다른 그룹들 모두 하나씩 충돌 가능한지 체크를 할 것입니다.
중요한 점은 26번째 for 문입니다.
for 문 안에 있는 초기화 구문에서
iCol을 iRow로 초기화해 주고 있습니다.
0번 그룹은 0 ~ 31번 그룹과 충돌 가능한지
모두 비교를 할 겁니다.
근데 1번 그룹은 1 ~ 31번 그룹만 충돌 검사를
진행하면 됩니다. 어차피 0번 그룹과의 비교는
앞서 0번 그룹이 모든 그룹과 검사를 할 때 이미
검사를 했었기 때문이죠.
그래서 1번 그룹이 검사할 차례가 되면
이미 체크한 0번 그룹은 넘어가도 되는 겁니다.
자기 자신과의 비교부터
진행하면 된다는 규칙이 나오죠.
그래서 iCol을 iRow로 초기화해 주어,
0번 그룹이 비교를 진행할 때 0번 비트부터,
1번 그룹이 비교를 진행할 때에는 1번 비트부터
체크할 수 있도록 하는 겁니다.
그렇게 비교를 진행하다가
n번 그룹과 특정 비트( 그룹 )를
& 연산하여 1이 나오면,
즉 두 그룹이 서로 충돌 가능한 상태로
설정되어 있다면 두 그룹을 CollisionGroupUpdate
함수의 인자로 넘깁니다.
CollisionGroupUpdate 함수에서는
우선 GetCurScene 함수를 통해 현재 진행되고 있는 씬을 불러옵니다.
그리고 GetArrObject 함수를 통해
현재 씬에 존재하는, 인자로 받은
두 그룹에 해당하는 오브젝트가 포함된 배열을 받아 옵니다.
이때 GetArrObject 함수를 통해 오브젝트를 넘겨줄 때
쓸데없이 복사가 일어나지 않도록 레퍼런스로 반환합니다.
레퍼런스로 넘겼다는 것은 받은 쪽에서 오브젝트가
들어있는 배열을 참조한다는 것이죠.
이때 배열에 들어있는 오브젝트의 정보를
바꾸거나 하는 실수를 방지하기 위해 const 키워드를
붙여 넘겨주는 것이죠.
다시 CollisionGroupUpdate 함수로 돌아와서
GetArrObject 함수를 통해 얻어낸 두 그룹의
오브젝트 배열을 각각 다른 vector를 통해 참조합니다.
그리고 for 문을 통해
두 vector가 참조하고 있는 배열에 들어있는
오브젝트들을 IsCollision 함수에 인자로 넘깁니다.
for 문 속 if 문에서 GetCollider 함수를 통해
해당 오브젝트가 Collider를 보유하고 있지 않으면
해당 오브젝트는 체크하지 않고 다음 오브젝트를 체크합니다.
또한 두 오브젝트가 서로 같다면,
즉 서로 같은 그룹의 서로 같은 오브젝트라면
이 역시 체크하지 않고 다음 오브젝트를 체크하게 되죠.
플레이어 자기 자신과의 충돌이나
몬스터1과 몬스터1의 충돌 등은
체크할 필요가 없으니 넘어가는 것이죠.
아직 구현하지는 않았지만
IsCollision 함수에서 인자로 받은 두 오브젝트의
충돌을 감지하고 그에 맞는 동작을 하게 될 겁니다.
키 입력을 구현할 때처럼
이전 상황도 따져 가면서 충돌을 구현해야겠죠.
이전에는 충돌했으나 현재에는 충돌하고 있지 않은 상태,
이전에도 충돌했고 현재에도 충돌하고 있는 상태,
이전에도 충돌하지 않았고 현재에도 충돌하고 있지 않은 상태 등을
따져 봐야 하는 것이죠.
만약 씬에 100개의 오브젝트가 존재하며
42번 오브젝트와 97번 오브젝트의 충돌 상태를
확인하려고 합니다.
두 오브젝트는 이전에도 현재에도 충돌하고 있지 않은 상태입니다.
생각해 보면 42번 오브젝트는
나머지 99개의 오브젝트와의
충돌 관계를 갖고 있어야 합니다.
97번 오브젝트는 물론, 다른 오브젝트들도
모두 다른 오브젝트와의 충돌 관계를
갖고 있습니다.
이처럼 수많은 두 오브젝트의 충돌 상황을
모두 갖고 있으며 빠르게 탐색할 수 있는 것이
다양하게 있지만 이진 탐색 트리, 즉 map을
활용하도록 할 겁니다.
42번 오브젝트와 97번 오브젝트의 충돌 관계를
키-값이 5인 노드에 저장한다거나,
52번 오브젝트와 13번 오브젝트의 충돌 관계를
키-값이 10인 노드에 저장한다거나 하는 것이죠.
이를 위해서는 각 오브젝트의
Collider마다 ID가 존재해야 할 것입니다.
그래서 Collider의
ID값을 저장할 멤버 m_iID를
추가하였습니다.
그리고 static 변수, 즉 정적 변수
g_iNextID를 추가하였습니다.
정적 변수이기 때문에 데이터 영역에 존재하며
마치 전역 변수처럼 사라지지 않고 유지되는 변수이죠.
정적 변수는 한 번만 초기화를 진행할 수 있으며
헤더 파일에 선언되었다면 값을 초기화할 수 없습니다.
( 헤더 파일은 여러 소스 파일에 포함될 수 있기 때문에
헤더 파일에서 초기화하면 각 소스 파일에서 초기화가
발생해야 하므로 이를 방지한 것입니다. )
그래서 CCollider 소스 파일에서
0으로 초기화해 주는 모습입니다.
Collider가 생성될 때
m_iID의 값을 g_iNextID로
초기화한 후 g_iNextID의 값을 하나 올립니다.
이렇게 하면 각 Collider가
각자 고유한 m_iID값을 가지게 되겠죠.
강의 출처 : https://www.youtube.com/watch?v=USIWiONMWII&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=28