Deeper Learning

[WinAPI] 이벤트 지연 처리 본문

Game Development/WinAPI

[WinAPI] 이벤트 지연 처리

Dlaiml 2024. 6. 3. 21:04

한 프레임 단위로 Update, Render가 일어나는 WinAPI 프로젝트에서 객체의 생성, 삭제를 코드 순서대로 처리하려 하였으나 문제가 있다.

 

예를 들어 적 오브젝트를 참조하여 적의 위치를 따라 매 프레임 움직이는 투사체를 구현하였다고 해보자.

체력이 0 이하로 떨어져 어떤 프레임에서 Update 처리를 하며 동적으로 할당한 적 오브젝트가 delete 되었다면 투사체에서 적 오브젝트를 참조하던 포인터는 nullptr가 되어 에러가 발생한다.

 

단순하게 코드의 순서를 바꾸어 객체 삭제 관련 코드를 루프의 끝에 둔다 하면 업데이트(대미지 계산 등 처리) -> 렌더링(화면에 Paint) -> 객체 삭제의 순서가 될 텐데 삭제된 객체가 렌더링 된 상태로 사용자에게 보이며 해당 객체와 관련된 업데이트가 모두 마무리된 상태이다. 이미 사라져야 할 적 오브젝트에게 대미지를 입거나 적 오브젝트가 투사체를 발사하는 문제가 생긴다는 것이다.

 

이를 해결하기 위해 오브젝트 생성, 삭제와 관련된 이벤트 들을 렌더링 이후에 처리하고, 삭제, 생성 객체를 마킹해두면 다음 프레임에 이를 참고할 수 있다.

 

이벤트들을 관리하기 위한 매니저 클래스 EventMgr의 헤더는 다음과 같다.

#pragma once

struct tEvent
{
	EVENT_TYPE eEvent;
	DWORD_PTR lParam;
	DWORD_PTR wParam;
};


class CEventMgr
{
	SINGLE(CEventMgr);

private:
	vector<tEvent> m_vecEvent;
	vector<CObject*> m_vecDead;

public:
	void Update();


private:
	void Execute(const tEvent& _event);

public:
	void AddEvent(const tEvent& _t) { m_vecEvent.push_back(_t); }
};

 

이벤트 정보를 담는 구조체로 구성된 벡터인 vecEvent, 삭제 예정 오브젝트들을 포인터로 구성된 벡터 vecDead가 멤버로 선언되어 있다.

 

// CEventMgr.cpp
void CEventMgr::Update()
{
	for (size_t i = 0; i < m_vecDead.size(); ++i)
	{
		delete m_vecDead[i];
	}
	m_vecDead.clear();

	for (size_t i = 0; i < m_vecEvent.size(); ++i)
	{
		Execute(m_vecEvent[i]);
	}
	m_vecEvent.clear();
}

void CEventMgr::Execute(const tEvent& _event)
{
	switch (_event.eEvent)
	{
	case EVENT_TYPE::CREATE_OBJECT:
	{
		// lParam: Object Addr
		// wParam: Group Type
		CObject* pObj = (CObject*)_event.lParam;
		GROUP_TYPE eType = (GROUP_TYPE)_event.wParam;

		CSceneMgr::GetInstance()->GetCurScene()->AddObject(pObj, eType);
		break;
	}
	case EVENT_TYPE::DELETE_OBJECT:
	{
		// lParam: Object Addr 

		CObject* pDeadObj = (CObject*)_event.lParam;
		pDeadObj->SetDead();
		m_vecDead.push_back(pDeadObj);

		break;
	}
	case EVENT_TYPE::SCENE_CHANGE:
	{
		// lParam: New Scene Addr
		CSceneMgr::GetInstance()->ChangeScene((SCENE_TYPE)_event.lParam);
		break;
	}
	default:
		break;
	}
}

 

Update에서 이전 프레임에 삭제 예정으로 마킹된 Object를 삭제하고 vecEvent 벡터를 순회하며 내부에 저장된 이벤트들에서 Execute 함수를 실행한다.

 

이벤트 타입은 Object 생성, Object 삭제, SCene 변경 타입 3개가 정의되어 있고 switch 문을 사용하여 이를 각각 경우에 맞게 처리한다.

wWinMain 포스팅에서 살펴본 윈도우 프로시저(WndProc)와 유사하게 이벤트를 받고 이벤트 타입에 따라 이를 분기처리, lPrarm, wparam에서 부가 정보를 파악하여 이를 처리하고 있다.

 

DELETE_OBJECT 이벤트 타입의 블록에서는 Object 클래스의 SetDead 함수를 사용하여 클래스 내부에서 객체의 삭제 예정 여부를 나타내는 bool 멤버 값을 true로 바꾸고 해당 객체를 삭제예정 객체 벡터인 m_vecDead에 push 하는 과정이 이루어진다.

 

이렇게 vecDead에 push된 객체들은 다음 프레임의 Update 함수에서 delete 되어 메모리를 반환한다.

 

WinAPI 관련 함수는 microsoft learn, 코드 구조, 이벤트 매니저 설계는 아래 Reference의 유튜브를 주로 참고하였다.

 

Reference

[0] https://learn.microsoft.com/ko-kr/windows/win32/api/

[1] https://www.youtube.com/watch?v=dlFr-OnHlWU&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK

 

 

 

Comments