본문 바로가기

Win32 api

Win32 api 강의 29 - 30화.

- 이전 프레임 충돌 상태 감지.

// 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