C++ Rookiss Part2 게임 수학과 DirectX12 : Scene 복습
- game.cpp에서 그린 모른 것 들을 Scene으로 관리하여 정리.
- Update문을 하나로 통합
- 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
로 모두 바꾸면 된다.
이 다음 시간은 카메라를 만들 것인데 카메라를 공부하려면 선수지식으로 게임 수학을 알아야한다.
게임 수학의 벡터, 행렬, 좌표계를 이해하고 넘어가자!