- 이전 프레임 충돌 상태 감지.
// CCollisionMgr.h
...
union COLLIDER_ID
{
struct
{
UINT ID1;
UINT ID2;
};
ULONGLONG ID;
};
private:
...
// 충돌체 간의 이전 충돌 상태를 저장하는 맵.
map<ULONGLONG, bool> m_prevCollisionState;
...
union 자료형을 이용해서 두 Collider 의 ID 값 UINT 2개를 붙여서 8byte ULONGLONG 인 ID 를 만듬.
이 ID 값은 두 Collider 만이 가질 수 있는 유일한 값이다. 이걸 map 에 넣어줌.
// CCollider.h
class CObject;
class CCollider : public CComponent
{
private:
static UINT s_iNextID; // 다음에 할당할 고유 ID를 저장하는 정적 멤버 변수.
...
UINT m_iID; // 콜라이더의 고유 ID.
...
private:
void OnCollisionEnter(CCollider* pOther);
void OnCollisionStay(CCollider* pOther);
void OnCollisionExit(CCollider* pOther);
...
friend class CCollisionMgr;
};
id 값은 CCollider 에서 생성자를 통해 생성할 때 static 멤버 변수 값을 1 증가시키면서 m_iID 에 넣어주면 됨.
이론상 Collider 가 UINT 의 최댓값을 넘어갈 정도로 생성되면 중복되지만, 현실적으로 불가능하고 간단한 프로그램이므로 이렇게 해줘도 무방함.
OnCollision~ 이벤트 함수는 일단 private 으로 하고 CCollisionMgr 에게 friend 설정을 해둠.
// CCollisionMgr.cpp
void CCollisionMgr::CollisionGroupUpdate(GROUP_TYPE eType1, GROUP_TYPE eType2)
{
CScene* pCurScene = CSceneMgr::GetInstance()->GetCurScene();
if (pCurScene == nullptr)
return;
const vector<CObject*>& arrObj1 = pCurScene->GetObjByGroup(eType1);
const vector<CObject*>& arrObj2 = pCurScene->GetObjByGroup(eType2);
for(CObject* pObj1 : arrObj1)
{
if (pObj1 == nullptr || pObj1->GetCollider() == nullptr)
continue;
for(CObject* pObj2 : arrObj2)
{
if (pObj2 == nullptr || pObj2->GetCollider() == nullptr)
continue;
// 같은 오브젝트끼리 충돌 검사하지 않도록.
if (pObj1 == pObj2)
continue;
// 충돌 검사.
CCollider* pCol1 = pObj1->GetCollider();
CCollider* pCol2 = pObj2->GetCollider();
ColliderStateUpdate(pCol1, pCol2);
}
}
}
void CCollisionMgr::ColliderStateUpdate(CCollider* pCol1, CCollider* pCol2)
{
UINT id1 = pCol1->GetID(), id2 = pCol2->GetID();
// 항상 ID1 < ID2가 되도록.
if (id1 > id2)
std::swap(pCol1, pCol2);
COLLIDER_ID colID;
colID.ID1 = id1;
colID.ID2 = id2;
// 현재 충돌 상태 확인.
bool isColliding = pCol1->Collision(pCol2);
// 이전 충돌 상태 확인.
map<ULONGLONG, bool>::iterator iter = m_prevCollisionState.find(colID.ID);
// 처음 보는 콜라이더 쌍인지 확인.
if(iter == m_prevCollisionState.end())
{
// 처음 보는 콜라이더 쌍인 경우, 맵에 추가.
m_prevCollisionState.insert(make_pair(colID.ID, isColliding));
if (isColliding)
{
// 충돌 시작 이벤트 처리 가능.
pCol1->OnCollisionEnter(pCol2);
pCol2->OnCollisionEnter(pCol1);
}
return;
}
// 이전에 충돌한 적이 있는 경우.
if (iter->second == true)
{
// 현재도 충돌 중인지 확인.
if (!isColliding)
{
// 더 이상 충돌하지 않음.
iter->second = false;
// 충돌 종료 이벤트 처리 가능.
pCol1->OnCollisionExit(pCol2);
pCol2->OnCollisionExit(pCol1);
}
// 여전히 충돌 중
else
{
// 충돌 지속 이벤트 처리 가능.
pCol1->OnCollisionStay(pCol2);
pCol2->OnCollisionStay(pCol1);
}
}
else
{
// 이전에 충돌하지 않았으나, 현재 충돌하는 경우.
if (isColliding)
{
iter->second = true;
// 충돌 시작 이벤트 처리 가능.
pCol1->OnCollisionEnter(pCol2);
pCol2->OnCollisionEnter(pCol1);
}
}
}
KEY_STATE 를 갱신할 때와 비슷하게,
단순히 if-else 반복하면서 이전 프레임과 상태를 비교하여 갱신해주기만 하면 된다. 코드만 길 뿐...
중요한 점은 COLLIDER_ID.ID 값에 들어가는 두 UINT 값에 작은 값이 ID1, 큰 값이 ID2 에 들어가도록 해줌.
왜냐하면 두 Collider 간의 충돌의 id 를 유일하게 만들기 위해서임.
예를 들어 0001 과 0004 의 충돌이 나왔을 때 이렇게 정렬해주지 않으면
id 값이 00010004 랑 00040001. 2개가 만들어진다. 메모리 낭비임.
'Win32 api' 카테고리의 다른 글
| Win32 api 강의 34화. (0) | 2025.09.20 |
|---|---|
| Win32 api 강의 31 - 33화. (0) | 2025.09.19 |
| Win32 api 강의 27 - 28화. (0) | 2025.09.18 |
| Win32 api 강의 25 - 26화. (0) | 2025.09.17 |
| Win32 api 강의 24화. (0) | 2025.09.17 |