학습일지/언리얼

UE5 패키징 & 로딩 스크린

Tsukino Ren 2026. 5. 27. 20:00

패키징 시 에디터 전용 헤더 오류

문제

fatal error C1083: Cannot open include file: 'SNegativeActionButton.h': No such file or directory
fatal error C1083: Cannot open include file: 'AssetSelection.h': No such file or directory

원인

SNegativeActionButton.h, AssetSelection.h 등은 에디터 전용 헤더다.
에디터에서 플레이할 때는 멀쩡히 동작하지만, 패키징 시에는 에디터 모듈이 포함되지 않기 때문에 찾지 못해 빌드가 실패한다.

해결

해당 #include 줄을 제거한다. 실제로 사용 중인 코드라면 에디터 전용으로 분리한다.

// 에디터에서만 사용하는 코드는 이렇게 감싼다
#if WITH_EDITOR
    #include "SNegativeActionButton.h"
#endif

  • Rider IDE에서 회색으로 표시된 include는 실제로 사용하지 않는 헤더다 → 제거 대상
  • 헤더 경로에 Editor, Slate/, UnrealEd/ 가 포함되어 있으면 런타임 코드에 넣으면 안 된다

로딩 무비(Movie Player) 테스트 방법

에디터 Play 버튼으로는 로딩 무비가 재생되지 않는다

에디터 플레이는 에디터 위에서 동작하기 때문에 로딩 스크린이 트리거되지 않는다.

해결: Standalone Game 모드로 테스트

  1. 에디터 플레이 버튼 옆 점 세 개 클릭
  2. Standalone Game 선택

Standalone은 에디터 모듈을 포함한 채로 실행되므로 에디터 전용 헤더도 문제없고, 로딩 무비도 실제로 재생된다.


UE5.5 Movie Player 기반 로딩 스크린 구현

GameInstance에서 구현하는 방법

Build.cs에 모듈 추가

PublicDependencyModuleNames.AddRange(new string[]
{
    "MoviePlayer",
});

PrivateDependencyModuleNames.AddRange(new string[]
{
    "PreLoadScreen",
});

GameInstance.h

virtual void Init() override;

UPROPERTY(EditDefaultsOnly, Category = "Loading")
TSubclassOf<ULoadingWidget> LoadingWidgetClass;

void BeginLoadingScreen();
void EndLoadingScreen(UWorld* InLoadedWorld);

GameInstance.cpp

#include "MoviePlayer.h"
#include "Blueprint/UserWidget.h"

void UMyGameInstance::Init()
{
    Super::Init();
    GetMoviePlayer()->OnPrepareLoadingScreen().AddUObject(this, &UMyGameInstance::BeginLoadingScreen);
    FCoreUObjectDelegates::PostLoadMapWithWorld.AddUObject(this, &UMyGameInstance::EndLoadingScreen);
}

void UMyGameInstance::BeginLoadingScreen()
{
    FLoadingScreenAttributes LoadingScreen;
    LoadingScreen.bAutoCompleteWhenLoadingCompletes = true;
    LoadingScreen.MinimumLoadingScreenDisplayTime = 5.0f;

    if (LoadingWidgetClass)
    {
        ULoadingWidget* LoadingWidgetInstance = CreateWidget<ULoadingWidget>(this, LoadingWidgetClass);
        if (LoadingWidgetInstance)
        {
            LoadingScreen.WidgetLoadingScreen = LoadingWidgetInstance->TakeWidget();
        }
    }

    GetMoviePlayer()->SetupLoadingScreen(LoadingScreen);
}

void UMyGameInstance::EndLoadingScreen(UWorld* InLoadedWorld)
{
    // 로딩 완료 후 처리
}

주의사항

  • SWeakWidget으로 감싸는 방식은 UE5에서 렌더링이 제대로 안 되는 경우가 있다 → TakeWidget() 직접 넘기는 방식을 사용한다
  • GameInstance 블루프린트에서 LoadingWidgetClass에 WBP를 반드시 할당해야 한다
  • 첫 번째 레벨(로비)은 이미 로드된 상태라 로딩 스크린이 뜨지 않는 것이 정상이다

UE5.5에서 로딩 진행률(Progress) 가져오기

결론: 공식적으로 불가능하다

UE4에서는 GetMoviePlayer()->GetLoadingProgress()로 0.0~1.0의 실제 진행률을 가져올 수 있었지만, UE5.5에서는 해당 함수가 제거되었다.

UE5.5 기준 IGameMoviePlayer에서 사용 가능한 함수:

  • IsLoadingFinished() - 로딩 완료 여부만 bool로 반환
  • IsMovieCurrentlyPlaying() - 무비 재생 중 여부
  • ForceCompletion() - 강제 완료

대안: 타이머 기반으로 흉내내기

// LoadingWidget.h
UPROPERTY(BlueprintReadOnly)
float LoadingProgress = 0.0f;

float ElapsedTime = 0.0f;
float ExpectedLoadTime = 5.0f; // 예상 로딩 시간
// LoadingWidget.cpp
void ULoadingWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
    Super::NativeTick(MyGeometry, InDeltaTime);

    ElapsedTime += InDeltaTime;
    // 0.95f로 막아서 로딩 끝나기 전에 꽉 차는 거 방지
    LoadingProgress = FMath::Clamp(ElapsedTime / ExpectedLoadTime, 0.0f, 0.95f);

    if (GetMoviePlayer()->IsLoadingFinished())
    {
        LoadingProgress = 1.0f;
    }
}

실무에서는?

대부분의 게임도 가짜 프로그래스바를 사용한다. 시간 기반으로 천천히 채우다가 로딩이 끝나면 1.0으로 채우는 방식이 일반적이다. 실제 진행률 연동이 필요하다면 레벨 스트리밍 방식으로 맵 구조를 설계해야 한다.


MinimumLoadingScreenDisplayTime의 역할

로딩 화면을 최소 N초 동안 유지한다. 단순히 시간을 보장하는 것 외에도 실질적인 역할이 있다.

왜 필요한가?

맵 자체는 빠르게 로드되더라도 텍스처, 사운드, 에셋들이 스트리밍으로 뒤늦게 로드되는 경우가 있다. 로딩 화면 없이 바로 게임이 시작되면:

  • 차량 소리가 화면보다 먼저 재생되거나
  • 텍스처가 뭉개져 보이는 팝인(Pop-in) 현상이 발생한다

MinimumLoadingScreenDisplayTime을 넉넉하게 설정하면 이런 문제를 방지할 수 있다.


트러블슈팅 요약

문제원인해결
패키징 시 SNegativeActionButton.h / AssetSelection.h 못 찾음 에디터 전용 헤더를 런타임 코드에 include 해당 include 제거
에디터 Play에서 로딩 무비 안 뜸 에디터 플레이는 로딩 스크린 트리거 안 됨 Standalone Game 모드로 테스트
로딩 스크린 위젯이 안 보임 SWeakWidget 래핑 문제 TakeWidget() 직접 전달 방식으로 변경
GetLoadingProgress() 빨간 줄 UE5.5에서 해당 함수 제거됨 타이머 기반으로 대체
로딩 끝나도 소리/텍스처가 뒤늦게 로드됨 에셋 스트리밍 타이밍 문제 MinimumLoadingScreenDisplayTime 넉넉하게 설정

요약 및 정리

  • 패키징과 에디터 플레이는 다르다. 에디터에서 멀쩡히 돌아가도 패키징하면 터지는 경우가 있다. 에디터 전용 헤더(Slate/, UnrealEd/, Editor 포함 경로)는 런타임 코드에 넣으면 안 된다.
  • 로딩 스크린 테스트는 Standalone Game 모드로 한다. 에디터 플레이로는 로딩 스크린이 트리거되지 않는다.
  • UE5.5에서 실제 로딩 진행률은 가져올 수 없다. GetLoadingProgress()가 제거되었으며, IsLoadingFinished()만 남아있다. 진행률이 필요하다면 타이머 기반으로 흉내내거나 레벨 스트리밍 방식을 사용해야 한다.
  • MinimumLoadingScreenDisplayTime은 단순 최소 시간 보장 그 이상이다. 에셋 스트리밍 타이밍을 맞춰줘서 팝인 현상이나 사운드가 먼저 재생되는 문제를 방지한다.
  • Rider에서 회색 include는 안 쓰는 헤더다. 패키징 전에 회색 include를 정리하는 습관을 들이면 패키징 오류를 미리 방지할 수 있다.

마무리

  • 인트로 씬 제작 후 로딩 스크린과 자연스럽게 연결
  • 레벨 스트리밍 방식으로 전환하여 실제 진행률 연동 구현 검토
  • 로딩 완료 후 EndLoadingScreen에서 추가 초기화 처리 (날씨, 시간 등 디지털트윈 세팅 적용)
  • 패키징 전 Rider에서 회색 include 정리하는 것을 체크리스트화
  • MinimumLoadingScreenDisplayTime 값을 프로젝트 세팅에서 조절 가능하도록 변수화