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