본문 바로가기

Win32 api

Win32 api 강의 31 - 33화.

- CObject 수정.

// CCObject.h
#pragma once
class CCollider;
class CObject
{
private:
    wstring m_strName;      // 오브젝트 이름.
    ...

public:
    inline const wstring& GetName() const { return m_strName; }
    inline void SetName(const wstring& name) { m_strName = name; }
    ...

    virtual void OnCollisionEnter(CCollider* pOther) {}
    virtual void OnCollisionStay(CCollider* pOther) {}
    virtual void OnCollisionExit(CCollider* pOther) {}
    ...

 

오브젝트 클래스에 이름 변수랑 충돌 3종 이벤트 콜백 함수를 virtual 로 선언. 

파생 클래스에서 각자 충돌에 대한 행동을 처리하도록 함.

 

- Event Manager 설계.

// define.h
enum class EVENT_TYPE
{
    CREATE_OBJECT,
    DELETE_OBJECT,
    SCENE_CHANGE,

    END
};

 

한 프레임 중간에 오브젝트를 생성/삭제한다거나 Scene 을 변경한다거나 등등

이벤트가 프레임 처리 도중에 발생하면 동시성에 문제가 생김.

A 라는 오브젝트가 삭제가 됐는데 한 프레임 시간 내에서 누구는 이걸 모르고, 누구는 이걸 알아서 관련된 처리를 하면 

일관성이 없음.

 

그래서 Event 를 한 프레임이 끝나고 나면 한번에 처리해주는 Event Manager 클래스가 필요함.

// CEventMgr.h
#pragma once
struct tEvent
{
    EVENT_TYPE      eType;
    DWORD_PTR       lParam;
    DWORD_PTR       wParam;
};
class CEventMgr
{
    SINGLETON(CEventMgr);
private:
    vector<tEvent> m_vecEvent;

public:
    void update();
};

 

일단 설계만 해둔 상태.

 

- CreateObject 구현.

// func.h
#pragma once
class CObject;
void CreateObject(CObject* pObj, GROUP_TYPE eGroup);

 

func.h 헤더 파일에 공용으로 쓸 함수들을 선언함.

// func.cpp
...
void CreateObject(CObject* pObj, GROUP_TYPE eGroup)
{
    tEvent event = {};
    event.eType = EVENT_TYPE::CREATE_OBJECT;
    event.lParam = (DWORD_PTR)pObj;
    event.wParam = (DWORD_PTR)eGroup;

    CEventMgr::GetInstance()->AddEvent(event);
}

 

윈도우 api 에서 lParam, wParam 을 쓰는 것과 같은 원리임. 

여기서 DWORD_PTR 은 내부적으로 ULONG_PTR 이며

#if defined(_WIN64)
...
    typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
#else
...
    typedef _W64 unsigned long ULONG_PTR, *PULONG_PTR;

 

빌드된 운영체제에 따라 8 or 4 바이트로 정의된다. 즉 운영체제 상관없이 주솟값을 받아올 수 있음. 사실 WPARAM 이랑 비슷한 듯...

 

// CEventMgr.cpp
...
void CEventMgr::Execute(const tEvent& event)
{
    switch (event.eType)
    {
    case EVENT_TYPE::CREATE_OBJECT:
    {
        // lParam: CObject*, wParam: GROUP_TYPE
        CObject* pObj = (CObject*)event.lParam;
        GROUP_TYPE eGroup = (GROUP_TYPE)event.wParam;

        CSceneMgr::GetInstance()->GetCurScene()->AddObject(pObj, eGroup);
    }
    ...
    }
}

void CEventMgr::update()
{
    // 등록된 모든 이벤트 처리.
    for(size_t i = 0; i < m_vecEvent.size(); ++i)
    {
        Execute(m_vecEvent[i]);
    }

    // 처리된 이벤트 벡터 초기화.
    m_vecEvent.clear();
}

 

이후 CEventMgr 에서 update 마다 모인 이벤트들을 처리하고 버려줌.

 

- DeleteObject 구현.

// CObject.h
#pragma once
class CCollider;
class CObject
{
private:
    ...
    bool m_bDelete;    // 삭제 여부.

public:
    ...
    inline bool IsDeleted() const { return m_bDelete; }

private:
    inline void MarkForDeletion() { m_bDelete = true; }
    ...
    friend class CEventMgr;
};

 

오브젝트를 삭제하는 과정은 조금 더 세세함. 바로 삭제하지 않고 Marking 한다. 그리고 다음 프레임 EventMgr 의 업데이트에서 실제로 삭제함. 

이렇게 유예 기간을 줌으로써 살아있는 다른 오브젝트들은 마킹된 오브젝트를 보고 그에 따른 처리를 할 시간이 주어지고,

안정적으로 메모리를 해제할 수 있음.

// CEventMgr.cpp
void CEventMgr::Execute(const tEvent& event)
{
    switch (event.eType)
    {
    ...
    case EVENT_TYPE::DELETE_OBJECT:
    {
        // lParam: CObject*
        CObject* pObj = (CObject*)event.lParam;
        if(pObj == nullptr || pObj->IsDeleted())
            return;

        pObj->MarkForDeletion();
        m_vecDeadObj.push_back(pObj);
    }
        break;
    ...
    }
}

void CEventMgr::update()
{
    // 이전 프레임에서 Delete 마킹된 오브젝트들 실제로 삭제.
    for (size_t i = 0; i < m_vecDeadObj.size(); ++i)
    {
        delete m_vecDeadObj[i];
    }
    m_vecDeadObj.clear();
    ...
}

 

DeleteObject 는 CreateObject 보다 더 간단함. 삭제할 오브젝트의 주솟값만 lParam 에 넘겨준다. 

// func.cpp
void DeleteObject(CObject* pObj)
{
    tEvent event = {};
    event.eType = EVENT_TYPE::DELETE_OBJECT;
    event.lParam = (DWORD_PTR)pObj;
    event.wParam = 0;

    CEventMgr::GetInstance()->AddEvent(event);
}

 

- CollisionMgr A/S

// CCollisionMgr.cpp
void CCollisionMgr::ColliderStateUpdate(CCollider* pCol1, CCollider* pCol2)
{
    ...
    // 처음 보는 콜라이더 쌍인지 확인.
    if(iter == m_prevCollisionState.end())
    {
        // 둘 중 하나라도 Delete 마킹된 경우, 맵에 추가하지 않고 종료.
        if (pCol1->GetOwner()->IsDeleted() || pCol2->GetOwner()->IsDeleted())
            return;
        ...
    }

    // 둘 중 하나라도 Delete 마킹된 경우, 맵에서 제거 후 종료.
    if (pCol1->GetOwner()->IsDeleted() || pCol2->GetOwner()->IsDeleted())
    {
        if (iter->second == true)
        {
            // 이전에 충돌 중이었으나, 이제는 삭제되므로 충돌 종료 이벤트 처리.
            pCol1->OnCollisionExit(pCol2);
            pCol2->OnCollisionExit(pCol1);
        }
        m_prevCollisionState.erase(iter);
        return;
    }
    ...

 

이전 강의에서, 충돌 상태를 late_update 하는 Collision Manager 함수에 IsDeleted 상태를 확인하는 코드를 추가해줘야 함.

 

지금 이벤트 콜백 함수들의 호출 순서를 보면

void CCore::progress()
{
    update();

    late_update();

    render();

    // Event Update
    CEventMgr::GetInstance()->update();
}

 

이벤트 처리를 마지막에 해주기 때문에, 실제 Delete 가 일어날 때까지 오브젝트는 충돌 처리를 끝내주고 사라져야 함.

 

- Scene A/S

마찬가지로 Scene 클래스에도 IsDeleted 상태를 확인하는 코드를 추가해줘야 함.

// CScene.cpp
void CScene::update()
{
    for (UINT i = 0; i < (UINT)GROUP_TYPE::END; ++i)
    {
        for (CObject* obj : m_arrObj[i])
        {
            if (obj == nullptr || obj->IsDeleted())
                continue;
            obj->update();
        }
    }
}
... // late_update 는 그대로 처리해준다. 
void CScene::render(HDC hDC)
{
    for (UINT i = 0; i < (UINT)GROUP_TYPE::END; ++i)
    {
        vector<CObject*>::iterator iter = m_arrObj[i].begin();
        for(; iter != m_arrObj[i].end();)
        {
            CObject* obj = *iter;
            if (obj == nullptr || obj->IsDeleted())
            {
                // 삭제된 오브젝트를 벡터에서 제거.
                iter = m_arrObj[i].erase(iter);
            }
            else
            {
                obj->render(hDC);
                ++iter;
            }
        }
    }
}

'Win32 api' 카테고리의 다른 글

Win32 api 강의 35 - 36화.  (0) 2025.09.21
Win32 api 강의 34화.  (0) 2025.09.20
Win32 api 강의 29 - 30화.  (0) 2025.09.18
Win32 api 강의 27 - 28화.  (0) 2025.09.18
Win32 api 강의 25 - 26화.  (0) 2025.09.17