📘 주제

SceneManager 기반 게임 오브젝트 통합 관리 시스템 설계

이 강의의 핵심 목표는 게임 프로젝트의 구조를 개선하여, 오브젝트를 Game 클래스에서 직접 생성하고 관리하는 방식에서 벗어나, Scene 단위로 구성하고 이를 SceneManager를 통해 관리하는 구조로 리팩토링하는 것이다. 유니티와 유사하게 하나의 Scene에서 다양한 GameObject를 묶고, 게임의 생명 주기 함수를 체계적으로 실행할 수 있도록 설계한다.

추가적으로, 이후 다양한 Manager(Input, Time 등)를 확장하여 엔진 구조의 기본 틀을 완성하게 된다.


📙 개념

개념설명
SceneGameObject들을 묶어 하나의 맵 또는 상태를 구성하는 논리 단위
SceneManagerScene을 선택하고 활성화하며 생명주기를 관리하는 관리자
GameObjectTransform, Component(Camera, MeshRenderer 등)를 조합한 게임 오브젝트
게임 루프 단계Awake → Start → Update → LateUpdate → FixedUpdate 순서로 실행되는 루프
Graphics/Pipeline렌더링 디바이스와 파이프라인. 디바이스 컨텍스트와 연결되어 렌더링 처리
Manager 구조Input, Time, Scene 등 기능별 클래스를 분리해 체계적으로 관리하는 설계
전역 접근Game 클래스를 전역으로 선언(GGame), 매크로(SCENE, TIME 등)를 통해 간편하게 접근

🧾 용어 정리

용어설명
shared_ptr<T>C++ 스마트 포인터로 참조 카운트를 관리해 메모리 자동 해제
Scene, SceneManagerScene은 오브젝트를 담는 단위, SceneManager는 그것을 관리
GameObject게임 오브젝트. Transform 및 다양한 컴포넌트를 포함
Awake~FixedUpdate오브젝트의 생명주기 함수. 순차적으로 호출됨
GGameGame 객체를 전역으로 접근하기 위한 포인터
SCENE, INPUT, TIME각각 SceneManager, InputManager, TimeManager에 접근하기 위한 매크로

🔍 코드 분석

✅ Scene 클래스

class Scene
{
public:
	void Awake();
	void Start();
	void Update();
	void LateUpdate();
	void FixedUpdate();

	void AddGameObject(shared_ptr<GameObject> gameObject);
	void RemoveGameObject(shared_ptr<GameObject> gameObject);
	const vector<shared_ptr<GameObject>>& GetGameObjects();

private:
	vector<shared_ptr<GameObject>> _gameObjects;
};
  • _gameObjects: Scene에 소속된 GameObject를 저장하는 리스트
  • AddGameObject, RemoveGameObject: GameObject를 Scene에 추가하거나 제거
  • Awake~FixedUpdate: 루프 단계별로 GameObject를 순회하며 대응 함수 호출
void Scene::Awake() {
	for (const auto& obj : _gameObjects)
		obj->Awake();
}
  • Awake는 초기화용, Start는 시작 세팅, Update는 매 프레임 호출

✅ SceneManager 클래스

class SceneManager
{
public:
	SceneManager(shared_ptr<Graphics> graphics);
	void Init();
	void Update();
	void LoadScene(wstring sceneName);
	shared_ptr<Scene> GetActiveScene();

private:
	shared_ptr<Scene> LoadTestScene();
	shared_ptr<Graphics> _graphics;
	shared_ptr<Scene> _activeScene;
};
  • _graphics: 렌더링 디바이스 접근용
  • _activeScene: 현재 실행 중인 Scene
  • LoadScene: 현재는 하드코딩된 LoadTestScene()을 통해 테스트 씬을 생성
SceneManager::SceneManager(shared_ptr<Graphics> graphics)
	: _graphics(graphics) {}
void SceneManager::Init() {
	if (_activeScene)
	{
		_activeScene->Awake();
		_activeScene->Start();
	}
}
void SceneManager::Update() {
	if (_activeScene)
	{
		_activeScene->Update();
		_activeScene->LateUpdate();
		_activeScene->FixedUpdate();
	}
}

LoadTestScene 구현

shared_ptr<Scene> SceneManager::LoadTestScene()
{
	auto scene = make_shared<Scene>();

	auto camera = make_shared<GameObject>(_graphics->GetDevice(), _graphics->GetDeviceContext());
	camera->GetOrAddTransform();
	camera->AddComponent(make_shared<Camera>());
	scene->AddGameObject(camera);

	auto cat = make_shared<GameObject>(_graphics->GetDevice(), _graphics->GetDeviceContext());
	cat->GetOrAddTransform();
	cat->AddComponent(make_shared<MeshRenderer>(_graphics->GetDevice(), _graphics->GetDeviceContext()));
	scene->AddGameObject(cat);

	return scene;
}
  • 임시로 고양이와 카메라 오브젝트를 생성하여 Scene에 추가

✅ Game 클래스와 매크로 구성

class Game
{
public:
	Game();
	~Game();
	void Init(HWND hwnd);
	void Update();
	void Render();

	shared_ptr<SceneManager> GetSceneManager();
	shared_ptr<InputManager> GetInputManager();
	shared_ptr<TimeManager> GetTimeManager();
	shared_ptr<Pipeline> GetPipeline();

private:
	HWND _hwnd;
	shared_ptr<Graphics> _graphics;
	shared_ptr<Pipeline> _pipeline;
	shared_ptr<SceneManager> _scene;
	shared_ptr<InputManager> _input;
	shared_ptr<TimeManager> _time;
};
extern unique_ptr<Game> GGame;
#define GAME GGame
#define SCENE GAME->GetSceneManager()
#define INPUT GAME->GetInputManager()
#define TIME  GAME->GetTimeManager()
  • GGame 전역 인스턴스 생성 → 매크로로 간편하게 Manager 접근

✅ Game.cpp 구성

void Game::Init(HWND hwnd)
{
	_hwnd = hwnd;
	_graphics = make_shared<Graphics>(hwnd);
	_pipeline = make_shared<Pipeline>(_graphics->GetDeviceContext());

	_input = make_shared<InputManager>();
	_input->Init(hwnd);

	_time = make_shared<TimeManager>();
	_time->Init();

	_scene = make_shared<SceneManager>(_graphics);
	_scene->Init();

	SCENE->LoadScene(L"Test");
}
void Game::Update()
{
	_graphics->RenderBegin();

	TIME->Update();
	INPUT->Update();
	SCENE->Update();

	_graphics->RenderEnd();
}
  • 게임 루프 순서: Time → Input → Scene Update → Render
  • SceneManager는 내부에서 Scene의 오브젝트들 순회 업데이트

좋습니다. 이어서 SceneManager 시스템 통합 학습 교재 – Part 2를 완성해드리겠습니다. 이 파트에서는 InputManager, TimeManager의 구현과 게임 전체 프레임 흐름, 그리고 MeshRenderer의 실제 렌더링 적용까지의 통합 구조를 설명합니다. 역시 모든 내용을 한 글자도 빠짐없이 분석하고 재구성했습니다.


📘 주제 (Part 2)

Input/Time 시스템 통합 및 렌더링 흐름 구성

이번 파트는 앞서 구성한 Scene, SceneManager, GameObject 구조 위에, 입력(Input) 처리와 시간(Time) 관리 시스템을 추가하고, 게임의 프레임 루프 안에서 렌더링 흐름까지 통합하는 과정이다.

특히 렌더링을 위한 MeshRenderer의 업데이트 로직과, 전역 접근을 위한 매크로 구성까지 포함된다.


📙 개념

개념설명
InputManager키보드/마우스 입력을 감지하고 상태를 저장 및 갱신하는 클래스
TimeManager시간 경과 측정 및 FPS 계산을 처리하는 클래스
MeshRendererGameObject의 Mesh를 화면에 렌더링하는 컴포넌트
Update 루프게임에서 매 프레임마다 실행되는 함수 호출 구조
전역 접근 매크로INPUT, TIME 등의 매크로를 통해 Manager 클래스에 쉽게 접근 가능

🧾 용어 정리

용어설명
VK_Windows API의 키보드 입력 상수
POINT마우스 좌표를 담는 WinAPI 구조체
QueryPerformanceCounter고해상도 타이머를 사용하는 시간 측정 함수
Camera::S_MatView, S_MatProjection전역 카메라 View/Projection 행렬
RenderBegin/RenderEnd그래픽 렌더링의 시작과 끝 처리
CopyDataConstantBuffer에 데이터 복사하는 함수 (GPU로 전달)

🔍 코드 분석


✅ InputManager 클래스

InputManager.h

enum class KEY_TYPE { UP = VK_UP, DOWN = VK_DOWN, W = 'W', A = 'A', ... };
enum class KEY_STATE { NONE, PRESS, DOWN, UP, END };

class InputManager
{
public:
	void Init(HWND hwnd);
	void Update();

	bool GetButton(KEY_TYPE key);
	bool GetButtonDown(KEY_TYPE key);
	bool GetButtonUp(KEY_TYPE key);
	const POINT& GetMousePos();

private:
	KEY_STATE GetState(KEY_TYPE key);
	HWND _hwnd;
	vector<KEY_STATE> _states;
	POINT _mousePos = {};
};
  • KEY_TYPE: 키의 종류 정의 (방향키, WASD, 마우스 클릭 등)
  • KEY_STATE: 키 입력의 상태 정의 (NONE, PRESS, DOWN, UP)
  • GetButton 계열 함수: 외부에서 상태 체크 시 사용
  • POINT: 마우스 위치

InputManager.cpp

void InputManager::Init(HWND hwnd)
{
	_hwnd = hwnd;
	_states.resize(KEY_TYPE_COUNT, KEY_STATE::NONE);
}
void InputManager::Update()
{
	if (_hwnd != ::GetActiveWindow()) {
		std::fill(_states.begin(), _states.end(), KEY_STATE::NONE);
		return;
	}

	BYTE asciiKeys[KEY_TYPE_COUNT] = {};
	::GetKeyboardState(asciiKeys);

	for (uint32 key = 0; key < KEY_TYPE_COUNT; key++) {
		bool isDown = asciiKeys[key] & 0x80;
		KEY_STATE& state = _states[key];

		if (isDown)
			state = (state == KEY_STATE::PRESS || state == KEY_STATE::DOWN) ? KEY_STATE::PRESS : KEY_STATE::DOWN;
		else
			state = (state == KEY_STATE::PRESS || state == KEY_STATE::DOWN) ? KEY_STATE::UP : KEY_STATE::NONE;
	}

	::GetCursorPos(&_mousePos);
	::ScreenToClient(_hwnd, &_mousePos);
}
  • 키보드 입력을 상태별로 갱신하며, 마우스 위치도 로컬 좌표로 갱신

✅ TimeManager 클래스

TimeManager.h

class TimeManager
{
public:
	void Init();
	void Update();

	uint32 GetFps();
	float GetDeltaTime();

private:
	uint64 _frequency = 0;
	uint64 _prevCount = 0;
	float _deltaTime = 0.f;

	uint32 _frameCount = 0;
	float _frameTime = 0.f;
	uint32 _fps = 0;
};

TimeManager.cpp

void TimeManager::Init()
{
	::QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&_frequency));
	::QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&_prevCount));
}
void TimeManager::Update()
{
	uint64 currentCount;
	::QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&currentCount));

	_deltaTime = (currentCount - _prevCount) / static_cast<float>(_frequency);
	_prevCount = currentCount;

	_frameCount++;
	_frameTime += _deltaTime;

	if (_frameTime > 1.f) {
		_fps = static_cast<uint32>(_frameCount / _frameTime);
		_frameCount = 0;
		_frameTime = 0.f;
	}
}
  • 초당 프레임 수(FPS) 계산 및 현재 프레임 경과 시간 계산

✅ MeshRenderer::Update()

void MeshRenderer::Update()
{
	_cameraData.matView = Camera::S_MatView;
	_cameraData.matProjection = Camera::S_MatProjection;
	_cameraBuffer->CopyData(_cameraData);

	_transformData.matWorld = GetTransform()->GetWorldMatrix();
	_transformBuffer->CopyData(_transformData);

	Render(GGame->GetPipeline());
}
  • 카메라 정보와 월드 좌표계를 셰이더 버퍼에 전달
  • Render()를 호출해 실제 렌더링 실행

✅ Game 클래스의 프레임 루프 통합

void Game::Update()
{
	_graphics->RenderBegin();

	TIME->Update();      // 시간 갱신
	INPUT->Update();     // 입력 갱신
	SCENE->Update();     // Scene 갱신 (모든 오브젝트 업데이트)

	_graphics->RenderEnd();
}
  • Game::Update() 안에서 모든 시스템이 한 프레임 단위로 실행
  • 구조는 다음과 같다:
[ 프레임 루프 흐름 ]
1. RenderBegin()
2. TimeManager::Update()
3. InputManager::Update()
4. SceneManager::Update() → Scene::Update() → GameObject::Update() ...
5. RenderEnd()

✅ 핵심

  1. SceneManager를 중심으로 오브젝트가 Scene 단위로 구성되고, 생명 주기가 체계적으로 관리됨
  2. InputManager는 키보드, 마우스를 감지하고, TimeManager는 델타 타임 및 FPS를 계산
  3. 모든 시스템은 Game::Update()에서 하나의 프레임 루프 내에서 실행됨
  4. GGame을 통한 전역 접근 및 SCENE, INPUT, TIME 매크로로 호출 간소화
  5. MeshRendererUpdate() 안에서 카메라/변환 정보를 복사하고 Render() 수행
  6. 게임 오브젝트의 생성, 생명주기 실행, 렌더링까지 모든 흐름이 자동화되어, 추후 Scene만 교체하면 게임 환경 전환이 가능
  7. 이후 Scene 간 전환, 리소스 로딩, 충돌 감지, UI 시스템 등으로 확장 가능한 구조로 설계됨

profile
李家네_공부방

0개의 댓글