Level Transitions in UE5: Open Level, Loading Screens, and Where Your Data Goes

Created: 2026-07-20

From title to stage to results. The difference between Open Level (by Name) and (by Object Reference), what survives a level change and what doesn't, carrying a score over with GameInstance, and a practical workaround for the "my loading screen never shows" problem.

Press a button on the title screen and the stage begins; touch the goal and the results appear. Every finished game needs this skeleton. The node that does the switching is a single one, and it works as soon as you place and connect it.

The trouble starts afterward. The coins you collected in the stage read 0 on the results screen, the picture freezes while loading, and the transition fails the moment you package the build. This article covers the two flavors of Open Level, what is destroyed and what survives when you change levels, and how to handle loading screens.

Three screens (title, stage, and results) connected by arrows, with the score carried along a separate path

What You'll Learn

  • The difference between Open Level (by Name) and (by Object Reference), and which to pick
  • The premise that a level change destroys almost everything
  • Handing your score to GameInstance, the one thing that survives
  • Why loading screen Widgets never appear, and a practical workaround
  • How this differs from Level Streaming
  • Hands-on: connecting title → stage → results with a score carried through

Sponsored

There's Only One Node for Switching

Switching levels takes exactly one node: Open Level. Whether it's a button click or touching the goal, this is the only thing you call.

There is one behavior to keep in mind, though. Calling Open Level does not switch levels on the spot. It queues the transition, and the actual work runs at the end of that frame.

That means any node you connect after Open Level runs just before the current level disappears.

Timeline showing Open Level called partway through one frame, the Set Score node after it running in the level that's about to be destroyed, and level destruction plus new level loading happening at the end of the frame
Open Level (by Object Reference)
  → Set Score = 0     ← Runs in the current level, which vanishes right after. Pointless

Always finish saving scores and cleaning up before you call Open Level. Getting this order backwards is the classic cause of "the data I saved isn't there."

by Name vs. by Object Reference

In UE5, Open Level comes in two versions.

The left node takes the level name as a string, the right one points directly at the asset, contrasted with the risks of typos and missing package entries
NodeHow you specify the levelCharacteristics
Open Level (by Name)Write a name (Name type) in Level NameYou can build the name at runtime. Typos go unnoticed
Open Level (by Object Reference)Point Level directly at a level assetYou pick from a list, so you can't get it wrong, and it follows renames

Use Open Level (by Object Reference) by default. There are two reasons.

  1. You can't misspell it: you just pick from a dropdown, so you'll never write L_Stage1 when you meant L_Stage01
  2. It's guaranteed to be included in the package: because a reference to the asset remains, the map gets pulled into the build along with it

The second one matters most. by Name is nothing but a string, so the build has no way of knowing that map is needed. The result is a hard-to-diagnose bug where things work in the editor but transitions fail only in the packaged executable.

The one case where you have to use by Name is when you build the name dynamically, such as L_Stage01 / L_Stage02. In that case, manually add the maps in question to List of maps to include in a packaged build under Project Settings > Project > Packaging. The packaging article covers this in detail.

PinWhat it does
AbsoluteLeaving it at the default (on) is fine
OptionsExtra information such as ?Difficulty=Hard. You receive it on the GameMode side, which is a bit fiddly, so GameInstance (covered below) is easier for carrying values around

Also note that if you get the level name wrong, the screen may go black or nothing may happen at all. When that occurs, open the Output Log first and check for a load-failure warning (→ the logging article).

Sponsored

A Level Change Destroys Almost Everything

This is the biggest pitfall. When Open Level runs, essentially everything in the current level is destroyed.

On the left, Actors, Widgets, GameMode, PlayerState and more disappearing; on the right, only GameInstance remaining
DestroyedSurvives
Every Actor placed in the levelGameInstance
The player CharacterGameInstance Subsystems
Widgets added to the viewportSave files (on disk)
GameMode / GameState / PlayerState
Level Blueprint variables

Note that even GameMode and PlayerState are rebuilt. That's because they are provided per level. It's also why World Settings' GameMode Override lets you assign a different GameMode to each level (→ the game framework article).

"I stored the score in PlayerState and it was 0 on the results screen" is a common way to get stuck, but that behavior is exactly as designed. PlayerState is a container that lives and dies within a single level.

Handing Data to GameInstance

The one container that survives for as long as the session lasts is GameInstance. Exactly one exists, from the moment the game launches until it quits.

Creating one takes three steps.

  1. In the Content Browser, create a Blueprint Class and choose GameInstance as the parent class (search for it under ALL CLASSES)
  2. Name it BP_GameInstance and add the variables you want to carry over
  3. Set that class under Project Settings > Project > Maps & Modes > Game Instance Class

Skip the third step and nothing happens. When "I made a GameInstance but values aren't retained," check here first.

You can read and write it from anywhere.

Each coin picked up in the stage increases TotalScore on the GameInstance; the value survives the level change and is shown on the results screen
Get Game Instance
  → Cast To BP_GameInstance
  → Set TotalScore = TotalScore + 10

Get Game Instance can be called from any Blueprint. There's one Cast involved, but GameInstance always exists, so it won't fail.

Note: If you'd rather not write the Cast every time, or you want to split features into separate files, use a GameInstance Subsystem. It has the same lifetime as GameInstance without bloating that one class (→ the Subsystem article).

Keep in mind that GameInstance only survives while the application is running. Quit the game and it's gone. For data that must still be there next launch, write it to disk using the save/load system.

Sponsored

Why Your Loading Screen Never Appears

You'll want to show a "Now Loading" message while loading. The obvious approach looks like this.

Create Widget (WBP_Loading) → Add to Viewport → Open Level

This doesn't work. The Widget is created, but it never once appears on screen.

The reason is that level loading is synchronous.

A timeline showing loading start immediately after the Widget is added, with not a single frame drawn before the switch to the next level

For anything to appear on screen, that frame has to be rendered. But during the load that Open Level kicks off, the game thread is fully occupied with loading and not a single frame is drawn. The Widget you added is destroyed at the switch to the new level, without ever getting a chance to be rendered.

The result is that the player sees a frozen screen for a few seconds. That's what "it freezes while loading" actually is.

Full-scale titles solve this with asynchronous loading and a dedicated loading screen system. Building that as a solo developer is a heavy lift, and often not worth the cost.

A Practical Compromise: Fade to Black First

You can't eliminate the freeze. The practical solution is to make the freeze less noticeable.

What you do is simple: fade the screen fully to black, then call Open Level. Since it freezes while already black, all the player sees is a slightly long fade.

Three steps: fade to black with Start Camera Fade, wait for the fade to finish with Delay, and call Open Level once the screen is fully black
When you want to transition
  → Get Player Camera Manager (Player Index = 0)
  → Start Camera Fade
        From Alpha        = 0.0
        To Alpha          = 1.0
        Duration          = 0.5
        Color             = Black
        Should Fade Audio = true
        Hold When Finished = true
  → Delay (0.5)          ← Wait until the fade finishes
  → Open Level (by Object Reference)

The key is turning on Hold When Finished. With it off, the screen snaps back the instant the fade completes, and the player sees the frozen frame.

Turning on Should Fade Audio fades the sound along with it. A screen that goes dark while footsteps keep playing feels off, so leave this on by default.

In the destination level, fade back in with the reverse.

Event BeginPlay
  → Get Player Camera Manager
  → Start Camera Fade (From Alpha = 1.0, To Alpha = 0.0, Duration = 0.5, Color = Black)

Warning: Start Camera Fade does not cover UMG Widgets. It affects the camera image, so UI stays on top of the black. If you want to hide the HUD too, create a Widget with a fullscreen black Image and raise its opacity with an animation instead (→ the UMG article).

Sponsored

Hands-On: Connecting Title to Results

Stage select in a puzzle game, one run in a roguelite, one race in a racing game. The three-screen skeleton of title → main game → results has the same shape regardless of genre. Here we'll build that loop while carrying a score through it.

The Finished Result

The button on the title screen starts the stage; pick up three coins, touch the goal, and the results screen shows SCORE: 30.

Flow from the title button to the stage, three coins bringing TotalScore to 30, and the value carried from the goal to the results screen showing SCORE: 30

Setup to Reproduce

Create a new project from the Third Person template and prepare the following.

Three levels (create them with File > New Level > Basic and save them in Content/Maps)

LevelContents
L_TitlePlayer Start only
L_Stage01Player Start, three BP_Coin, one BP_Goal. Set World Settings' GameMode Override to BP_ThirdPersonGameMode
L_ResultPlayer Start only

GameInstance

ClassVariableTypeDefault
BP_GameInstance (parent: GameInstance)TotalScoreInteger0

After creating it, set Project Settings > Project > Maps & Modes > Game Instance Class to BP_GameInstance.

Two Actors (both with Actor as the parent class)

ClassComponentsSettings
BP_CoinSphere Collision (Radius 50.0) + Static Mesh (Sphere, Scale 0.5)Collision Preset is OverlapAllDynamic
BP_GoalBox Collision (Box Extent 100 / 100 / 100)Same as above

Also add a bool variable named IsTransitioning (default false) to BP_Goal. It prevents the transition from running twice if the goal is touched repeatedly.

From Title to Stage

Build this in L_Title's Level Blueprint (we'll skip the UI and start with a key press).

Event BeginPlay
  → Get Game Instance → Cast To BP_GameInstance
  → Set TotalScore = 0        ← Reset here so a second playthrough starts fresh

Space Bar (Pressed)
  → Open Level (by Object Reference)  Level = L_Stage01

The key point is resetting the score on the title screen. If you reset it on the results side, you'd wipe the very score that screen is supposed to display the moment it opens.

Coins and the Goal

Here's BP_Coin's event graph.

Event ActorBeginOverlap (Other Actor)
  → Cast To BP_ThirdPersonCharacter (Other Actor)   ← Don't react to anything but the player
  → Get Game Instance → Cast To BP_GameInstance
  → Set TotalScore = TotalScore + 10
  → Print String  (TotalScore)                      ← For checking
  → Destroy Actor

BP_Goal fades to black before switching levels.

A node graph running from BP_Goal's overlap through the double-trigger guard Branch, the camera fade, Delay, and Open Level
Event ActorBeginOverlap (Other Actor)
  → Cast To BP_ThirdPersonCharacter (Other Actor)
  → Branch (Condition = IsTransitioning)
      True  → (do nothing)
      False ↓
  → Set IsTransitioning = true
  → Get Player Camera Manager (0)
  → Start Camera Fade (From 0.0 → To 1.0 / Duration 0.5 / Black / Fade Audio / Hold When Finished)
  → Delay (0.5)
  → Open Level (by Object Reference)  Level = L_Result

Displaying It on the Results Screen

Read the carried-over value in L_Result's Level Blueprint.

Event BeginPlay
  → Get Player Camera Manager (0)
  → Start Camera Fade (From 1.0 → To 0.0 / Duration 0.5 / Black)
  → Get Game Instance → Cast To BP_GameInstance
  → Print String ("SCORE: " + TotalScore)   Duration = 10.0

Print String is just for checking. To put it on the actual screen, pass this value to a Text in a Widget.

Verifying It

Open L_Title, hit Play, and press Space.

  • Collect all three coins and reach the goal → the screen darkens over 0.5 seconds and the results show SCORE: 30
  • Reach the goal without collecting anySCORE: 0
  • Collect only twoSCORE: 20
  • Return to the title and play again → it starts from 0 again, with no carryover

If something goes wrong, here's how to narrow it down.

  • Results always show 0 → Game Instance Class isn't set in Project Settings, so the default GameInstance is being used
  • Coins don't increase the scoreSet TotalScore is targeting a variable on the coin itself or the Level Blueprint instead of the GameInstance
  • It switches without fadingDelay is shorter than Duration, or Hold When Finished is off
  • The goal transitions twice → the IsTransitioning Branch is missing, or your logic is connected to the True side
  • Works in the editor but black in the package → you specified the level by name and the map isn't included in the package
  • You can't move in the stageGameMode Override isn't set in L_Stage01's World Settings

Now try retargeting Set TotalScore from the GameInstance to a Level Blueprint variable. The Print String in the stage still counts up correctly, but nothing survives to the results screen. Seeing "almost everything is destroyed" for yourself makes it much easier to decide where values belong.

Two things to take away.

  • Decide where a value lives before you write it: if it crosses levels, GameInstance; if it only matters inside one level, PlayerState or GameState is enough. Moving it later is a chore, so ask "does this cross levels?" before you create the variable
  • Always guard transitions against double firing: overlaps and buttons can both fire multiple times in one frame. A single flag like IsTransitioning prevents doubled fades and conflicting transitions

For polishing the look of the results screen, continue to the UMG article; for keeping a high score across launches, the save/load article.

Sponsored

How This Differs From Level Streaming

There's one more level-related feature: Level Streaming. Their roles are clearly different, so when in doubt, choose by the following.

Open LevelLevel Streaming
What it doesSwitchesConnects
Previous levelDestroyedStays as it is
Good forTitle, stage select, resultsLarge maps, seamless movement indoors
PlayerRebuiltStays the same

Even for something like "enter the dungeon entrance and move inside," Open Level is enough if it's fine for the screen to change during loading. Conversely, if you want the space beyond the door to look continuous, that's Level Streaming. The Level Streaming article covers it in detail.

Bonus: Good to Know for Later

  • Restarting the same level also uses Open Level: build a retry by passing the result of Get Current Level Name to Open Level (by Name). Actor state is fully reset, which is more reliable than writing your own reset logic. That said, if you don't want players redoing the whole stage on every death, keep the level loaded and move them back to a recovery point instead (see Checkpoints and respawning)
  • Change which level opens at startup: as you approach completion, set Project Settings > Maps & Modes > Game Default Map to L_Title. Leave it on a test level and your packaged build will open straight into a work-in-progress screen
  • You can change the GameMode per level: World Settings' GameMode Override is a per-level setting. Assigning a UI-only GameMode to the title and your normal one to the stage makes switching input modes much easier
  • Don't put too much in the Level Blueprint: what's in a Level Blueprint is specific to that level and can't be reused. Once your transition logic grows, move it to a GameMode or a GameInstance Subsystem (→ Level Blueprints vs. Blueprint Classes)
  • Keep maps in one place: settling on a folder like Content/Maps saves you hunting both when specifying maps in packaging settings and when reopening them (→ the asset management article)

Summary

  • Switching takes one node, Open Level. But it runs at the end of the frame, so do your cleanup before calling it
  • Use by Object Reference by default. No typos, and it's guaranteed to be in the package
  • A level change rebuilds Actors, Widgets, GameMode, and PlayerState from scratch
  • The only survivor is GameInstance. Put values you want carried over there
  • Loading screen Widgets never get a chance to be drawn, so complete a fade to black first, then call Open Level
  • Open Level to switch, Level Streaming to connect

In the game you're building now, which numbers need to survive across stages? Take a moment to check where those values are currently stored.