Programming/Dev
[007] 씬 전환
laivy
2023. 3. 18. 11:52

이번 게시글에서는 씬 전환을 구현하려고 한다.
일단은 '로고 씬' → '로그인 씬' 으로의 전환을 구현한다.
SceneManager::SceneManager() : m_scene{}
{
LogoScene::Instantiate();
if (LogoScene::IsInstanced())
{
LogoScene::GetInstance()->OnCreate();
SetScene(LogoScene::GetInstance());
}
}
현재, 씬 매니저 클래스가 생성될 때
로고 씬으로 시작되게 되어있다.
enum class FadeType { NONE, FADEIN, FADEOUT };
struct FadeInfo
{
FadeType type{ FadeType::NONE };
std::chrono::time_point<std::chrono::high_resolution_clock> startTime;
std::chrono::time_point<std::chrono::high_resolution_clock> endTime;
std::function<void()> callback{};
FLOAT alpha{ 1.0f };
};
씬을 전환할 때 페이드 인/아웃 효과를 주면서 전환하기 위해 위와 같은 구조체를 선언했다.
콜백 함수를 멤버 변수로 추가해서 페이드 인/아웃이 끝날 때 콜백 함수를 호출하려고 한다.
void SceneManager::SetFadeOut(FLOAT second, const std::function<void()>& callback)
{
m_fadeInfo.type = FadeType::FADEOUT;
m_fadeInfo.startTime = std::chrono::high_resolution_clock::now();
m_fadeInfo.endTime = m_fadeInfo.startTime + std::chrono::milliseconds{ static_cast<UINT>(second * 1000) };
m_fadeInfo.callback = callback;
m_fadeInfo.alpha = 1.0f;
}
void LogoScene::Update(FLOAT deltaTime)
{
static BOOL isFirstUpdate{ TRUE };
if (isFirstUpdate)
{
SceneManager::GetInstance()->SetFadeOut(3.0f,
[]()
{
LoginScene::Instantiate();
auto sm{ SceneManager::GetInstance() };
sm->SetScene(LoginScene::GetInstance());
sm->SetFadeIn(3.0f);
LogoScene::Destroy();
}
);
isFirstUpdate = FALSE;
}
}
로고 씬의 첫번째 업데이트 때 로그인 씬으로 넘어가도록 했다.
페이드 아웃은 3초 동안 진행되며 끝나면 로그인 씬으로 넘어가고 3초 동안 페이드 인 되게 했다.
void SceneManager::UpdateFadeEffect()
{
using namespace std::chrono;
using clock = high_resolution_clock;
if (m_fadeInfo.type == FadeType::NONE)
return;
auto curr{ clock::now() };
milliseconds fadeDuration{ duration_cast<milliseconds>(m_fadeInfo.endTime - m_fadeInfo.startTime) }; // 페이드가 진행되는 총 시간
milliseconds timePassed{ duration_cast<milliseconds>(curr - m_fadeInfo.startTime) }; // 페이드 시작 후 지금까지 흐른 시간
if (m_fadeInfo.type == FadeType::FADEIN)
m_fadeInfo.alpha = 1.0f - static_cast<float>(timePassed.count()) / static_cast<float>(fadeDuration.count());
else if (m_fadeInfo.type == FadeType::FADEOUT)
m_fadeInfo.alpha = static_cast<float>(timePassed.count()) / static_cast<float>(fadeDuration.count());
if (curr > m_fadeInfo.endTime)
{
// 콜백에서 m_fadeInfo를 변경할 수 있기 때문에 초기화 후 콜백 함수를 호출한다.
auto callback{ m_fadeInfo.callback };
m_fadeInfo = {};
callback();
}
}
SetFadeOut, SetFadeIn 함수를 호출하면 위의 업데이트 코드가 작동한다.
페이드 시작, 끝 시간을 이용해서 현재의 알파값을 계산하고 페이드가 끝났다면 콜백 함수를 호출한다.
void SceneManager::RenderFadeEffect(const ComPtr<ID2D1DeviceContext2>& d2dContext) const
{
if (m_fadeInfo.type == FadeType::NONE)
return;
auto brush{ BrushPool::GetInstance()->GetBrush(BrushPool::BLACK) };
brush->SetOpacity(m_fadeInfo.alpha);
d2dContext->SetTransform(MATRIX::Identity());
d2dContext->FillRectangle(RECTF{ 0.0f, 0.0f, 1920.0f, 1080.0f }, brush);
brush->SetOpacity(1.0f);
}
페이드 인/아웃 효과는 D2D를 이용하여 화면을 가득 채우는 사각형을 그리는 방법으로 구현했다.
굳이 최종 렌더링 결과를 D3D12 리소스로 복사하고 컴퓨터 셰이더를 이용하는 방법보단
이 방법이 훨씬 간편하고 성능 상 차이도 없을 것 같아서 이렇게 했다.

잘 작동한다.
이제 잠깐 진행이 멈춰있던 UI들을 구현해보려고 한다.