Programming/Dev

[007] 씬 전환

laivy 2023. 3. 18. 11:52

DevLog

이번 게시글에서는 씬 전환을 구현하려고 한다.

일단은 '로고 씬' → '로그인 씬' 으로의 전환을 구현한다.

 

 

 

 

 

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들을 구현해보려고 한다.