씬 매니저로 관리하기

2025. 4. 9. 00:05원티드_언리얼RPG 2기/WinApi

여태까지 적이 등장하는 화면 , 플레이어 이동 , 배경을 모두 backbuffer를 생성하고 backbuffechdc 를 받아서 처리했다. 

렌더링한 것들이 화면에 겹쳐 나오게 할 수 밖에 없었는데, 어떻게 하면 씬을 만들어서 관리를 할 수 있을까? 

정답은 씬 매니저다. 씬 매니저를 만들어보자.

씬 매니저란?

씬 매니저는 게임의 다양한 화면(씬)을 관리하고 전환하는 핵심 컴포넌트입니다. 씬 간의 전환을 부드럽게 처리하고, 게임 상태를 효율적으로 관리합니다.

주요 기능

  1. 씬 등록: 게임에서 사용할 다양한 씬을 등록
  2. 씬 전환: 현재 씬에서 다른 씬으로 전환
  3. 씬 업데이트: 현재 활성화된 씬의 업데이트 처리
  4. 씬 렌더링: 현재 활성화된 씬의 화면 출력

구현 구조

  • 싱글톤 패턴: 전역적인 접근을 위한 싱글톤 디자인 패턴 적용
  • 맵 컨테이너: 씬 이름(키)으로 빠르게 씬에 접근할 수 있는 맵 구조 사용
  • GameObject 상속: 모든 씬은 GameObject를 상속하여 일관된 인터페이스 제공
#pragma once
#include "GameObject.h"

class Image;
class EnemyManager;
class BattleScene : public GameObject
{
private:
	Image* backGround;
	EnemyManager* enemyManager;

public:
	virtual HRESULT Init() override;
	virtual void Release() override;
	virtual void Update() override;
	virtual void Render(HDC hdc) override;

	BattleScene() {};
	virtual ~BattleScene() {};
};
#include "BattleScene.h"
#include "Image.h"
#include "EnemyManager.h"
#include "CommonFunction.h"

HRESULT BattleScene::Init()
{
	SetClientRect(g_hWnd, WINSIZE_X, WINSIZE_Y);

	backGround = new Image();
	if (FAILED(backGround->Init(TEXT("Image/BackGround.bmp"), WINSIZE_X, WINSIZE_Y)))
	{
		MessageBox(g_hWnd,
			TEXT("Image/backGround.bmp 생성 실패"), TEXT("경고"), MB_OK);
		return E_FAIL;
	}

	enemyManager = new EnemyManager();
	enemyManager->Init();

	return S_OK;
}

void BattleScene::Release()
{
	if (enemyManager)
	{
		enemyManager->Release();
		delete enemyManager;
		enemyManager = nullptr;
	}

	if (backGround)
	{
		backGround->Release();
		delete backGround;
		backGround = nullptr;
	}
}

void BattleScene::Update()
{
	enemyManager->Update();
}

void BattleScene::Render(HDC hdc)
{
	backGround->Render(hdc);
	enemyManager->Render(hdc);
}


기존코드들(적, 배경생성) 을 battlescene.cpp와 헤더파일에 옮겼다. 

 

씬 매니저 클래스를 만들어보자.

#pragma once
#include "Singleton.h"
#include "config.h"

class GameObject;
class SceneManager : public Singleton<SceneManager>
{
private:
	map<string, GameObject*> mapScenes;

public:
	static GameObject* currentScene;
	static GameObject* loadingScene;
	static GameObject* nextScene;

	void Init();
	void Release();
	void Update();
	void Render(HDC hdc);

	HRESULT ChangeScene(string key);
	GameObject* AddScene(string key, GameObject* scene);
};

 

#include "SceneManager.h"
#include "GameObject.h"

GameObject* SceneManager::currentScene = nullptr;
GameObject* SceneManager::loadingScene = nullptr;
GameObject* SceneManager::nextScene = nullptr;

void SceneManager::Init()
{
}

void SceneManager::Release()
{
	map<string, GameObject*>::iterator iter;
	for (iter = mapScenes.begin(); iter != mapScenes.end(); iter++)
	{
		if (iter->second)
		{
			iter->second->Release();
			delete iter->second;
			iter->second = nullptr;
		}
	}
	mapScenes.clear();
	ReleaseInstance();
}

void SceneManager::Update()
{
	if (currentScene)
	{
		currentScene->Update();
	}
}

void SceneManager::Render(HDC hdc)
{
	if (currentScene)
	{
		currentScene->Render(hdc);
	}
}

HRESULT SceneManager::ChangeScene(string key)
{
	auto iter = mapScenes.find(key);	// nextScene
	if (iter == mapScenes.end())
	{
		return E_FAIL;
	}

	if (iter->second == currentScene)
	{
		return S_OK;
	}

	if (SUCCEEDED(iter->second->Init()))
	{
		if (currentScene)
		{
			currentScene->Release();
		}
		currentScene = iter->second;
		return S_OK;
	}
	return E_FAIL;
}

GameObject* SceneManager::AddScene(string key, GameObject* scene)
{
	if (scene == nullptr)
	{
		return nullptr;
	}

	auto iter = mapScenes.find(key);
	if (iter != mapScenes.end())
	{
		return iter->second;
	}

	mapScenes.insert(make_pair(key, scene));

    return scene;
}

 

씬을 (씬의 이름, 장면)을 키와 값으로 입력받는 map에 저장한다. 씬 전환을 위한 changeScene과  씬 추가를 위한 AddScene을 만들었다. 

맵을 활용한 씬 관리

씬 매니저에서는 각 씬을 (씬의 이름, 장면)을 키와 값으로 입력받는 map 컨테이너에 저장합니다. 이 방식의 장점은 문자열 키를 통해 원하는 씬에 빠르게 접근할 수 있다는 점입니다.

 

HRESULT 반환 타입과 예외 처리

HRESULT는 Windows API에서 함수의 성공 또는 실패를 나타내는 데 사용되는 32비트 정수 타입입니다. 성공 또는 실패를 표현할 때 주로 다음 값을 사용합니다:

  • S_OK: 함수가 성공적으로 실행됨 (0 값)
  • E_FAIL: 일반적인 실패 상태 (0x80004005 값)

이런 방식의 예외 처리는 C++의 try-catch 방식보다 가볍고, Windows API와의 일관성을 유지할 수 있어 게임 프로그래밍에서 자주 사용됩니다.

A키와 D키입력으로 씬이 전환되고 윈도우 창사이즈까지 바뀐다.