Skip to content

Loading System

PlatanoGames edited this page Feb 26, 2026 · 3 revisions

Loading System v1.0 — Loading Screens That Just Work

"The loading screen is the one piece of UI that must work perfectly in the exact moment your game is least capable of supporting UI."


The Problem

Loading screens in Unreal Engine are deceptively difficult. They appear simple — show an image while content loads, hide it when done — but the reality involves a web of interacting concerns that conspire to produce bugs:

Survival. When the engine loads a new level, it destroys almost everything in the current world. Actors, components, widgets — gone. Your loading screen widget was part of that world. It is now a dangling reference. The most common loading screen bug is the loading screen destroying itself during the very operation it is supposed to cover.

Timing. The loading screen must appear before the old level is torn down (otherwise the player sees a black frame) and disappear after the new level is fully ready (otherwise the player sees an incomplete world). These two boundaries are managed by different engine systems with different timing characteristics.

Input capture. While the loading screen is active, the player should not be able to interact with the game world beneath it. But simply disabling input is not enough — you need to remember the previous input state (cursor visibility, input mode, focus) and restore it exactly when the loading screen dismisses. Get this wrong and the player's mouse cursor disappears, or they cannot click anything, or their gamepad stops working.

Shader compilation. Even after the level's assets are loaded, the GPU may still be compiling shaders for materials it has never seen before. If the loading screen dismisses based on "assets loaded" alone, the player sees a level where every new material causes a visible hitch for the first few seconds.

Progress accuracy. Players expect progress bars to reflect reality. A progress bar that sits at 95% for 30 seconds and then jumps to 100% is worse than no progress bar at all. The loading screen must combine multiple progress sources (asset loading, shader compilation, minimum display time) into a single, honest percentage.

Reentry. What happens if a new loading request arrives while a loading screen is already active? What if the same level is requested twice? What if a network error occurs during an online game's loading sequence? Each of these is a valid scenario that production code must handle.

Stuck states. The worst loading screen bug is the one that never resolves. The loading screen appears and stays forever. The player's only option is to force-quit. This happens when a prerequisite system fails silently and the loading screen never receives its "you can dismiss now" signal.

Most projects address these issues one at a time, as they are discovered in QA. The result is a loading screen implementation that grows in complexity with every bug fix until no one on the team fully understands its state machine. PGX addresses all of these from the architecture level.


How PGX Solves It

PGX implements loading screens as a persistent overlay system with a deterministic state machine, configurable visual strategies, and multiple safety mechanisms.

1. Persistent Overlays

The loading screen is not a widget in the game world. It is a persistent overlay that exists outside the normal actor lifecycle. When a level is destroyed and reconstructed, the loading screen survives because it was never part of the level's object graph. This eliminates the entire category of "loading screen destroys itself" bugs.

2. Six-State Deterministic Pipeline

  +------+     +-----------+     +----------+
  | Idle |---->| Preparing |---->| FadingIn |
  +------+     +-----------+     +----------+
     ^                                |
     |                                v
  +----------+     +---------------+  +--------+
  | FadingOut|<----| WaitingClose  |<-| Active |
  +----------+     +---------------+  +--------+

Each state has explicit entry conditions, exit conditions, and timeout guards:

  • Preparing — Resolve the loading profile from the requested context tag. Validate the profile. Capture current input state for later restoration. Timeout: configurable (default 5 seconds). If profile resolution fails, transition to Idle with error.
  • FadingIn — Execute the fade-in animation. Begin capturing input. Duration is per-profile.
  • Active — Loading screen is fully visible. Track progress from asset loading and shader compilation. Display visual content (image, slideshow, animation, video, or custom). This is where the loading screen spends most of its time.
  • WaitingClose — Loading is complete, but the close policy may require waiting. Three policies: automatic (dismiss immediately), manual-only (wait for explicit close call), auto-with-skip (dismiss automatically but allow the player to skip early via button press). Timeout: configurable (default 20 seconds) to prevent stuck states.
  • FadingOut — Execute the fade-out animation. Restore captured input state. Duration is per-profile.

3. Six Visual Strategies

Not every loading screen looks the same. The system supports six visual types:

Type Behavior
Minimal Solid color with optional spinner. Zero asset dependencies.
Static Image Single image, optionally with progress bar.
Slideshow Rotating images with crossfade. Good for tips/lore.
Animated Material A material instance drives the visuals. Supports arbitrary shader effects.
Video Pre-rendered video playback during loading.
Custom Developer provides their own visual implementation.

The visual type is specified per loading profile, so different contexts can have different loading screen styles. The main menu might use Minimal. A story mission might use Video. A fast-travel might use Static Image.

4. Combined Progress Calculation

Progress is not just "how much of the level has loaded." It is a weighted combination:

  Total = (1 - PSOWeight) * AssetProgress + PSOWeight * PSOProgress

Where:

  • AssetProgress is the level asset loading percentage from the engine.
  • PSOProgress is the shader precompilation percentage (if a PSO system is active and the profile is configured to wait for it).
  • PSOWeight is a configurable value per loading profile, defaulting to 0.3.

A timeout prevents the combined progress from stalling forever if shader compilation encounters errors. After the timeout, the PSO portion is treated as complete regardless of actual state.

5. Input State Capture and Restoration

When the loading screen begins fading in, the system captures a snapshot of the current input state:

  • Cursor visibility
  • Input mode (game-only, UI-only, game-and-UI)
  • Focus state

When the loading screen finishes fading out, the captured state is restored exactly. This prevents the "my cursor disappeared after loading" and "I can't click anything after the loading screen" bugs that plague many implementations.

6. Watchdog Timers

Two safety timers prevent stuck states:

  • Preparing Timeout (default 5s) — If profile resolution takes too long, the system abandons the attempt and returns to Idle with an error. This catches broken asset references.
  • WaitingClose Timeout (default 20s) — If the close signal never arrives (e.g., a system fails silently), the loading screen force-closes after the timeout. The player is never permanently stuck.

7. Reentry Policies

Three policies handle overlapping loading requests:

Policy Behavior
Ignore If a loading screen is already active, discard the new request.
Restart Dismiss the current loading screen and start the new one.
Queue Complete the current loading screen, then execute the new request.

Architecture (ASCII Diagram)

+------------------------------------------------------------------+
|                   Loading System v1.0                             |
+------------------------------------------------------------------+
|                                                                   |
|  +------------------+     +------------------+   +--------------+ |
|  | Profile          |     | State Machine    |   | Visual       | |
|  | Resolution       |     |                  |   | Renderer     | |
|  |                  |     | 6-state          |   |              | |
|  | Context tag ->   |---->| deterministic    |-->| 6 strategies | |
|  | Loading Profile  |     | pipeline         |   | Minimal to   | |
|  | DA               |     | with timeouts    |   | Custom       | |
|  +------------------+     +------------------+   +--------------+ |
|                                |                                  |
|                                v                                  |
|  +------------------+     +------------------+   +--------------+ |
|  | Input Manager    |     | Progress Engine  |   | Safety       | |
|  |                  |     |                  |   | Mechanisms   | |
|  | Capture state    |     | Asset +          |   |              | |
|  | on FadingIn      |     | PSO weighted     |   | 2 watchdogs  | |
|  | Restore on       |     | combination      |   | Reentry      | |
|  | FadingOut        |     |                  |   | policies     | |
|  +------------------+     +------------------+   +--------------+ |
|                                                                   |
|  +-------------------------------------------------------------+ |
|  | Delegates (5 dynamic + 4 native)                             | |
|  |                                                               | |
|  | OnStarted | OnProgress | OnCompleted | OnFailed               | |
|  | OnStateChanged                                                | |
|  +-------------------------------------------------------------+ |
+------------------------------------------------------------------+

Capabilities

Loading Control

Capability Description
Request by context Show a loading screen using the profile associated with a context tag
Force close Immediately dismiss the loading screen regardless of state
Request skip Ask the loading screen to dismiss (only honored by AutoWithSkip policy)

State Query

Capability Description
Active check Whether any loading screen is currently visible
Current state Which of the 6 states the system is in
Current context The context tag of the active loading screen
Elapsed time How long the current loading screen has been active
Visual type Which of the 6 visual strategies is currently rendering

Progress

Capability Description
Combined progress Weighted total of asset loading and shader compilation
Asset progress Raw asset loading percentage
PSO progress Raw shader compilation percentage
Status message Human-readable description of current loading activity

Profile Management

Capability Description
Validate profile Check whether a context tag maps to a valid, loadable profile
Profile count How many loading profiles were auto-discovered
List context tags All context tags with registered loading profiles

History

Each completed loading sequence is recorded:

Field Description
Context tag Which profile was used
Visual type Which visual strategy was displayed
Total duration Wall-clock time from request to dismiss
Per-phase durations Time spent in each state machine phase
Result code Success, timeout, force-closed, cancelled, etc.
Flags Whether PSO wait was active, whether timeout occurred, whether player skipped

Configuration

Loading Profile Data Assets

The primary configuration surface. Each profile defines how a specific loading context should behave:

Visual Configuration:

  • Visual type (Minimal / Static Image / Slideshow / Material / Video / Custom)
  • Background image or material reference
  • Slideshow images with interval timing
  • Spinner style and position
  • Progress bar visibility and style

Timing Configuration:

  • Fade-in duration
  • Fade-out duration
  • Fade curve (linear, ease-in, ease-out, custom)
  • Minimum display time (ensures the loading screen is visible long enough to read)

Behavior Configuration:

  • Close policy (Automatic / ManualOnly / AutoWithSkip)
  • Reentry policy (Ignore / Restart / Queue)
  • Whether to wait for shader compilation
  • PSO weight in combined progress calculation
  • PSO wait timeout

Safety Configuration:

  • Preparing timeout
  • WaitingClose timeout

Loading Config Data Asset

Global configuration:

  • Default close policy
  • Default reentry policy
  • Default timeout values
  • History buffer size

Network Failure Handling

The system binds to the engine's network failure and travel failure delegates. When a network error occurs during a loading sequence, the system handles it according to the active profile's failure policy rather than leaving the loading screen in an undefined state.


Editor Tooling

Loading Inspector

A dedicated editor panel with four sections:

Current Status — Real-time display of the state machine with color coding (Gray=Idle, Blue=Preparing, Cyan=FadingIn, Yellow=Active, Orange=WaitingClose, Purple=FadingOut). Shows context tag, visual type, elapsed time, and combined progress.

Profile Catalog — Browse all discovered Loading Profile assets. View visual type, close policy, timing settings, and PSO integration configuration for each profile. One-click navigation to any profile asset.

Loading History — Scrollable log of past loading sequences with timing breakdowns per phase. Identifies slow sequences and highlights those that hit timeouts or were force-closed. Essential for performance tuning.

Debug Controls — Buttons for simulating loading requests, force-closing active screens, and triggering PSO simulation (for testing combined progress without actual shader compilation). These controls are editor-only and do not appear in shipping builds.

Console Commands

10 commands for runtime control and diagnostics:

Command Purpose
pgx.loading.status Current state, context, elapsed time, progress
pgx.loading.request Trigger a loading screen by context tag
pgx.loading.close Force-close active loading screen
pgx.loading.skip Request skip (if policy allows)
pgx.loading.history Show loading history with timing
pgx.loading.profiles List discovered profiles
pgx.loading.config Show global configuration values
pgx.loading.debug Toggle debug overlay (phase/timing)
pgx.loading.simulate Simulate a loading sequence (no actual load)
pgx.loading.simulate.pso Simulate PSO integration progress

Integration Points

With Level Transitions

This is the primary integration. When the level transition system begins a transition for a level that has "show loading screen" enabled, the loading screen activates automatically. The loading screen dismisses when the transition completes. The developer does not need to manage this coordination manually.

With Shader Compilation

When a loading profile has PSO wait enabled, the loading screen queries shader compilation progress and factors it into the combined progress bar. The PSO timeout ensures the loading screen will eventually dismiss even if shader compilation stalls.

With Game State

When configured, the loading screen system sets a "Loading" game state tag on activation and clears it on dismissal. This allows other systems to query whether a loading screen is active through the game state system rather than coupling directly to the loading screen.

With Network Systems

The system binds to engine network failure and travel failure delegates. When a network error interrupts a loading sequence, the loading screen transitions to its failure path rather than staying active indefinitely.

Blueprint Access

15 Blueprint nodes across 4 categories:

Category Nodes Purpose
Core 3 Request, force close, skip
Query 6 Active check, state, context, progress, elapsed time, visual type
Advanced 3 Profile validation, discovered count, registered tags
Debug 2 History, last duration

Why This Matters

Loading screens are one of the few UI elements that every game has and every player sees. They are also one of the most technically challenging because they operate during the precise moment when the engine is tearing down and rebuilding the game world.

The most common approach — a widget-based loading screen created in the level — is fundamentally broken by design. The level transition will destroy it. The second most common approach — a persistent widget added to the viewport — works better but still requires manual state management, input handling, progress tracking, and safety guards.

PGX makes loading screens a solved problem. The state machine is deterministic. Visual strategies are swappable without code changes. Progress is accurate because it accounts for both asset loading and shader compilation. Input state is captured and restored. Watchdog timers prevent stuck states. Reentry policies handle edge cases. Network failures are caught. Everything is configured through Data Assets with no initialization code required.

For the player, this means loading screens that always appear, always show accurate progress, and always dismiss. For the developer, this means never debugging loading screen bugs again.

The history system also serves as a powerful profiling tool. "Our Forest level loading screen averages 4.2 seconds, 1.8 seconds of which is shader compilation. If we pre-warm those shaders, we can cut loading time by 43%." This kind of data-driven optimization is impossible without instrumentation, and PGX provides it out of the box.

Clone this wiki locally