1. game.cpp에서 그린 모른 것 들을 Scene으로 관리하여 정리.
  2. Update문을 하나로 통합
  3. Input, Timer를 Singleton으로 변경

Manager 필터를 만들고 SceneManager, Scene 클래스 생성

SceneManager은 싱글톤 패턴으로 만들 것인데 싱글톤 패턴을 만드는 것을 매크로 한다.

#define DECLARE_SINGLE(type)		            \
private:							            \
	type() {}						            \
	~type() {}						            \
public:								            \
	static type* GetInstance()		            \
	{								            \
		static type instance;		            \
		return &instance;			            \
	}								            \

원래 define은 한줄만 읽는데 \ 를 붙이면 아래 줄까지 이어서 읽는다.

싱글톤 패턴을 만드는 걸 보면 static으로 유일성을 챙기고 있는 걸 볼 수 있다. (스택 메모리 아님!)


🚀 Scene

#pragma once

class GameObject;

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

	void AddGameObject(shared_ptr<GameObject> gameObject);
	void RemoveGameObject(shared_ptr<GameObject> gameObject);

private:
	vector<shared_ptr<GameObject>> _gameObjects;
};

vector<shared_ptr<GameObject>> _gameObjects; 로 gameObjects를 들고 있고 구조는 간단하다.

#include "pch.h"
#include "Scene.h"
#include "GameObject.h"

void Scene::Awake()
{
	for (const shared_ptr<GameObject>& gameObject : _gameObjects)
	{
		gameObject->Awake();
	}
}

void Scene::Start()
{
	for (const shared_ptr<GameObject>& gameObject : _gameObjects)
	{
		gameObject->Start();
	}
}

void Scene::Update()
{
	for (const shared_ptr<GameObject>& gameObject : _gameObjects)
	{
		gameObject->Update();
	}
}

void Scene::LateUpdate()
{
	for (const shared_ptr<GameObject>& gameObject : _gameObjects)
	{
		gameObject->LateUpdate();
	}
}

void Scene::AddGameObject(shared_ptr<GameObject> gameObject)
{
	_gameObjects.push_back(gameObject);
}

void Scene::RemoveGameObject(shared_ptr<GameObject> gameObject)
{
	auto findIt = std::find(_gameObjects.begin(), _gameObjects.end(), gameObject);
	if (findIt != _gameObjects.end())
	{
		_gameObjects.erase(findIt);
	}
}

Awake, Start, Update, LateUpdate에서의 for문은 C#에서 foreach랑 비슷한 문법으로 내용물을 수정하지 않을 때 사용 가능하다. 가시성이 좋다.


🚀 SceneManager

#pragma once

class Scene;

class SceneManager
{
	DECLARE_SINGLE(SceneManager);

public:
	void Update();
	void LoadScene(wstring sceneName);

public:
	shared_ptr<Scene> GetActiveScene() { return _activeScene; }

private:
	shared_ptr<Scene> LoadTestScene();

private:
	shared_ptr<Scene> _activeScene;

};

DECLARE_SINGLE(SceneManager); 으로 싱글톤인걸 확인 가능. 나머지는 가볍게 훝어보면 되는 구조.

#include "pch.h"
#include "SceneManager.h"
#include "Scene.h"
#include "Engine.h"
#include "Material.h"

#include "GameObject.h"
#include "MeshRenderer.h"

void SceneManager::Update()
{
	if (_activeScene == nullptr) return;

	_activeScene->Update();
	_activeScene->LateUpdate();
}

void SceneManager::LoadScene(wstring sceneName)
{
	// TODO : 기존 Scene 정리
	// TODO : 파일에서 Scene 정보 로드

	_activeScene = LoadTestScene();

	_activeScene->Awake();
	_activeScene->Start();
}

LoadTestScene이 길어서 분리함. 위에만 따로 보면 update에서는 현재 씬 체크 후 있으면

_activeScene->Update(); _activeScene->LateUpdate(); 해줌.

LoadScene은 일단 테스트로 LoadTestScene을 현재 씬에 넣고 Awake, Start를 해줌

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

	// Test Object
	shared_ptr<GameObject> gameObject = make_shared<GameObject>();

	vector<Vertex> vec(4);
	vec[0].pos = Vec3(-0.5f, 0.5f, 0.5f);
	vec[0].color = Vec4(1.f, 0.f, 0.f, 1.f);
	vec[0].uv = Vec2(0.f, 0.f);
	vec[1].pos = Vec3(0.5f, 0.5f, 0.5f);
	vec[1].color = Vec4(0.f, 1.f, 0.f, 1.f);
	vec[1].uv = Vec2(1.f, 0.f);
	vec[2].pos = Vec3(0.5f, -0.5f, 0.5f);
	vec[2].color = Vec4(0.f, 0.f, 1.f, 1.f);
	vec[2].uv = Vec2(1.f, 1.f);
	vec[3].pos = Vec3(-0.5f, -0.5f, 0.5f);
	vec[3].color = Vec4(0.f, 1.f, 0.f, 1.f);
	vec[3].uv = Vec2(0.f, 1.f);

	// 삼각형의 감기 순서는 시계 방향
	vector<uint32> indexVec;
	{
		indexVec.push_back(0);
		indexVec.push_back(1);
		indexVec.push_back(2);
	}

	{
		indexVec.push_back(3);
		indexVec.push_back(0);
		indexVec.push_back(2);
	}

	gameObject->Init(); // Transform

	shared_ptr<MeshRenderer> meshRenderer = make_shared<MeshRenderer>();

	{
		shared_ptr<Mesh> mesh = make_shared<Mesh>();
		mesh->Init(vec, indexVec);
		meshRenderer->SetMesh(mesh);
	}

	{
		shared_ptr<Shader> shader = make_shared<Shader>();
		shared_ptr<Texture> texture = make_shared<Texture>();
		shader->Init(L"..\\Resources\\Shader\\default.hlsli");
		texture->Init(L"..\\Resources\\Texture\\veigar.jpg");

		shared_ptr<Material> material = make_shared<Material>();
		material->SetShader(shader);
		material->SetFloat(0, 0.3f);
		material->SetFloat(1, 0.4f);
		material->SetFloat(2, 0.3f);
		material->SetTexture(0, texture);
		meshRenderer->SetMaterial(material);
	}

	gameObject->AddComponent(meshRenderer);

	scene->AddGameObject(gameObject);

	return scene;
}

기존에 game.cpp에서 가지고 있던 것들을 여기에 옮김.

그리고 gameObject를 전역에서 스택으로 옮기고 그것을 관리할 Scene을 생성함.

gameObject의 구성이 완료 됐다면 AddGameObject로 gameObject를 집어 넣고 scene을 반환하는 함수.


🚀 짧아진 game.cpp

#include "pch.h"
#include "Game.h"
#include "Engine.h"
#include "SceneManager.h"

void Game::Init(const WindowInfo& info)
{
	GEngine->Init(info);

	GET_SINGLE(SceneManager)->LoadScene(L"TestScene");
}

void Game::Update()
{
	GEngine->Update();
}

결과적으로 우리는 GEngine->Init(info); 을 한 뒤 씬을 선택해서 로드가 Init.

Update에서 GEngine->Update(); 만 호출하는데

void Engine::Update()
{
	GET_SINGLE(Input)->update();
	GET_SINGLE(Timer)->Update();

	Render();

	ShowFps();
}

이렇게 변경됨. 여기서보면 Input, Timer도 싱글톤으로 바뀐걸 볼 수 있음. 이건 나중에 설명.

Render도 변경 사항이 있는데 아래와 같음

void Engine::Render()
{
	RenderBegin();

	GET_SINGLE(SceneManager)->Update();

	RenderEnd();
}

RenderBegin, RenderEnd 사이에는 진짜 그릴 물체를 넣어야 했는데 우리의 SceneManager의 Update에서 모든 것을 해줌.


🚀 Input, Timer Singleton

Input, Timer를 Manager 필터로 옮기고 Singleton으로 변경하는 작업. Engine에서 직접 만들었던 것들을 모두 지우고 DECLARE_SINGLE을 이용해서 싱글톤 패턴으로 만든다.

이제 _input _timer 부분들은 GET_SINGLE 로 모두 바꾸면 된다.

이 다음 시간은 카메라를 만들 것인데 카메라를 공부하려면 선수지식으로 게임 수학을 알아야한다.

게임 수학의 벡터, 행렬, 좌표계를 이해하고 넘어가자!