Concept: The Cinematic Preview Pipeline

The showcase feature is three decoupled roles joined by one struct. Your game implements a provider (what does the character look like?), a showcase scene hosts a director (apply that look to a stand), and the manager ferries FCrimsonLoadingCharacterInfo from one to the other at the right moment. The plugin never touches your equipment system, and your equipment system never knows a loading screen exists.

The flow on every show

  1. ShowLoadingScreen fires (a condition became true - often the travel itself).
  2. The manager queries the first local player for a provider: PlayerController, then Pawn, then each Pawn component - first result with a valid leader mesh wins.
  3. The result is cached (GetCachedCharacterInfo) before the old world is torn down - by the time the widget exists, the pawn is usually gone.
  4. Every actor in the current world implementing ICrimsonLoadingCinematicInterface receives OnLoadingScreenStarted(CharacterInfo).
  5. The content-ready delegates broadcast the same struct to non-actor listeners.
  6. On hide, the same actors receive OnLoadingScreenEnded (and the visibility delegates fire).

Cached vs live character info

GetterSourceUse when
GetCachedCharacterInfoSnapshot taken when the screen openedAnywhere during loading - the pawn may already be destroyed
GetLocalPlayerCharacterInfoLive provider query right nowOutside loading - menus, character screens, pre-travel checks
Timing trap: late-loading directors
Step 4 notifies actors in the world that is current at show time. A director living in a seamless-travel transition map (or any level that loads after the screen opened) misses the call. Have it pull GetCachedCharacterInfo in BeginPlay when GetLoadingScreenDisplayStatus is true - the snapshot is exactly what the event would have delivered.

Why a struct and not a pawn reference

During a non-seamless load the old world - pawn, equipment components, everything - is destroyed. A pointer to any of it would dangle. FCrimsonLoadingCharacterInfo copies out only what the showcase needs: mesh assets, material assets, an anim class, transforms. Assets outlive worlds, so the snapshot stays valid for the whole load. This is also why the provider is queried immediately at show time rather than when the widget appears.

Design rules encoded in the data

  • Exactly one leader entry (bIsLeader = true) - it drives the skeleton; the director calls SetLeaderPoseComponent on all others.
  • A null `Mesh` is meaningful: the slot exists but is empty (bare head), and the director hides that component rather than erroring.
  • AdditionalActors are routed by your ActorTypeTag convention (e.g. CrimsonShowcase.Actor.Mount) to pre-placed showcase actors - the plugin spawns nothing.
  • Only the first local player is showcased; split-screen players are ignored by design.
  • IsValid() / GetLeaderComponent() on the struct are C++ helpers; Blueprint checks the MeshComponents array directly (all fields are readable and writable).

Where the picture comes from

The director only poses the scene. Getting pixels onto the loading screen is the render-target pipeline: a SceneCapture2D in the showcase world writes to the render target named in Project Settings, bUseLivePreview keeps world rendering alive during the load, and the widget reads GetShowcaseRenderTarget into a dynamic material. See How-To: Live Render Target Preview for the setup and the world-survival constraint (seamless-travel transition map or always-loaded sublevel).