Skip to content

Using Loaded Data: State vs. Visuals (From 0 to End) ​

The single most common question developers and artists ask when integrating a save system is:

"If a player opens a door and saves the game, when they reload or resume, will the door still be open? How does my actor actually USE the loaded data to update its 3D visuals and animations?"

Understanding the answer requires understanding the fundamental rule of Unreal Engine game architecture:
Separate Data State from Visual Representation.

This guide walks you through the complete lifecycle from Step 0 to the very end, showing you exactly how data flows from a save file into 3D meshes, animations, sounds, and UI.


1. The Core Concept: Data State vs. Visuals ​

When Aegis Save loads a file, it deserializes values directly into your variables in memory:

  • bIsOpen = true
  • CurrentHealth = 45.0
  • RemainingAmmo = 8

However, Unreal Engine does not automatically rotate meshes, play timelines, or update HUD widgets just because a boolean variable changed in memory.

text
┌──────────────────────────────┐              ┌──────────────────────────────┐
│       Data State             │              │     Visual Representation    │
│  (What Aegis Restores)       │              │    (What the Player Sees)    │
├──────────────────────────────┤              ├──────────────────────────────┤
│ • bIsOpen = true             │ ──(You Link)►│ • Door Mesh rotated at 90°   │
│ • CurrentHealth = 45.0       │              │ • Red Health Bar at 45%      │
│ • bIsLit = false             │              │ • Campfire particles disabled│
└──────────────────────────────┘              └──────────────────────────────┘

Aegis Save guarantees that your data is 100% restored before your actor's first gameplay tick (AC-303).
Your Blueprint or C++ code simply links the restored variable to your mesh or timeline.


2. The Complete 0-to-End Walkthrough: Interactive Door ​

Let's follow an interactive dungeon door (BP_DungeonDoor) through its complete lifespan:

Step 0: Authoring the Actor ​

  1. Create an Actor Blueprint BP_DungeonDoor.
  2. Add a StaticMeshComponent for the Door Frame (Root Component).
  3. Add a child StaticMeshComponent for the Door Slab (the moving door).
  4. Add UAegisPersistenceComponent to the actor.
  5. In My Blueprint, create a boolean variable:
    • Name: bIsOpen
    • Advanced Details: Check SaveGame (This tells Aegis to save this variable).

Step 1: During Gameplay (Player Opens the Door) ​

When the player approaches the door and presses E:

  1. The interaction event runs.
  2. Set bIsOpen = true.
  3. Play a Timeline that smoothly rotates the Door Slab from 0° to 90°.
  4. Play a creaking wood audio cue and spawn dust particles.
  5. The door is now visually and logically open.

Step 2: Saving the Game ​

  1. The player reaches an autosave trigger or clicks "Save Game".
  2. Aegis inspects the door actor.
  3. Aegis reads bIsOpen = true and writes it into the compressed binary save container under the door's permanent GUID identity.

Step 3: What Happens on Load? (The 2 Loading Scenarios) ​

In Unreal Engine, a load happens in one of two distinct environments:

Scenario A: Level Open / Map Transition (e.g. Loading from Main Menu) ​

When the player clicks "Continue" in the Main Menu:

  1. Unreal Engine loads the level map package from disk.
  2. The door actor spawns into the world.
  3. During component registration, Aegis detects the door's GUID, reads its saved record from memory, and applies bIsOpen = true before the actor's first tick (AC-303)!
  4. Event BeginPlay executes on the door.
  5. How the door updates visually: Inside BeginPlay, your Blueprint checks bIsOpen. Because Aegis already restored it, bIsOpen is true! Your code immediately sets the Door Slab rotation to 90°.
    • Result: When the player opens their eyes in the level, the door is already open. No snapping, no popping, no delayed animation!

Scenario B: In-Place Checkpoint Reload (e.g. Player Dies Mid-Game) ​

When the player dies and you reload a checkpoint without reloading the level map:

  1. The door actor is already alive in the world. BeginPlay will never run again!
  2. The game calls Load Game from Slot.
  3. Aegis matches live actors in the world and overwrites their variables with the checkpoint values (e.g., if the door was closed at the checkpoint, bIsOpen is set back to false).
  4. How the door updates visually: Aegis automatically invokes the OnAegisPostLoad event on the actor! Inside OnAegisPostLoad, your Blueprint reads the updated bIsOpen variable and snaps the door slab back to 0°.
    • Result: The door instantly closes in-place in under 10 milliseconds without any loading screen!

3. The Universal 3-Point Visual Pattern ​

To handle both Level Opens and In-Place Checkpoint Reloads flawlessly without duplicating logic or playing loud sounds during loading, use this simple 3-point pattern:

text
┌─────────────────────────────────────────────────────────────┐
│               Function: UpdateDoorVisuals                   │
│               Input: bInstant (Boolean)                     │
└──────────────────────────────┬──────────────────────────────┘
                               │
            ┌──────────────────┴──────────────────┐
            ▼                                     ▼
   [bInstant == true]                    [bInstant == false]
   (Used on Load / BeginPlay)            (Used during Player Interaction)
            │                                     │
            ▼                                     ▼
   • Snap Slab Rotation to 90°           • Play Smooth Timeline (0° to 90°)
   • NO Audio Cue!                       • Play Creaking Audio Cue
   • NO Particle Effects!                • Spawn Dust Particles

Complete Blueprint Wiring ​

text
[Event On Player Interact]
       │
       ▼
 [Set bIsOpen = NOT bIsOpen]
       │
       ▼
 [UpdateDoorVisuals (bInstant = false)] ──► Plays timeline, sound & FX smoothly!


[Event BeginPlay]
       │
       ▼
 [UpdateDoorVisuals (bInstant = true)]  ──► Snaps pose immediately on map open!


[Event On Aegis Post Load] (From Aegis Persistence Events Interface)
       │
       ▼
 [UpdateDoorVisuals (bInstant = true)]  ──► Snaps pose on in-place checkpoint reload!

4. How to Implement the Aegis Persistence Events Interface ​

To receive the OnAegisPostLoad callback in Blueprint or C++:

In Blueprint: ​

  1. Open your Actor Blueprint (BP_DungeonDoor).
  2. In the toolbar, click Class Settings.
  3. In the Details panel on the right, find Interfaces $\rightarrow$ Implemented Interfaces.
  4. Click Add and select Aegis Persistence Events.
  5. In your My Blueprint panel under Interfaces, double-click On Aegis Post Load (or right-click in the event graph and add Event On Aegis Post Load).
  6. Connect it directly to your UpdateDoorVisuals (bInstant = true) function!

In C++: ​

Implement IAegisPersistenceEvents on your actor class:

cpp
// DungeonDoor.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Identity/AegisPersistenceComponent.h"
#include "Identity/AegisPersistenceEvents.h"
#include "DungeonDoor.generated.h"

UCLASS()
class YOURGAME_API ADungeonDoor : public AActor, public IAegisPersistenceEvents
{
    GENERATED_BODY()

public:
    ADungeonDoor();

protected:
    virtual void BeginPlay() override;

    // --- IAegisPersistenceEvents Interface ---
    virtual void OnAegisPostLoad_Implementation() override;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    TObjectPtr<UStaticMeshComponent> DoorFrameMesh;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    TObjectPtr<UStaticMeshComponent> DoorSlabMesh;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    TObjectPtr<UAegisPersistenceComponent> Persistence;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Door")
    bool bIsOpen = false;

    void UpdateVisuals(bool bInstant);
};

// DungeonDoor.cpp
#include "DungeonDoor.h"

ADungeonDoor::ADungeonDoor()
{
    DoorFrameMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Frame"));
    RootComponent = DoorFrameMesh;

    DoorSlabMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Slab"));
    DoorSlabMesh->SetupAttachment(RootComponent);

    Persistence = CreateDefaultSubobject<UAegisPersistenceComponent>(TEXT("Persistence"));
    Persistence->SetScope(EAegisScope::World);
}

void ADungeonDoor::BeginPlay()
{
    Super::BeginPlay();

    // 1. On Level Open: Aegis has already restored bIsOpen before BeginPlay (AC-303).
    // Apply visual state instantly without sound or delay.
    UpdateVisuals(/*bInstant=*/ true);
}

void ADungeonDoor::OnAegisPostLoad_Implementation()
{
    // 2. On In-Place Reload: Checkpoint reloads update live actors without BeginPlay.
    // Apply visual state instantly!
    UpdateVisuals(/*bInstant=*/ true);
}

void ADungeonDoor::UpdateVisuals(bool bInstant)
{
    const FRotator TargetRotation = bIsOpen ? FRotator(0.f, 90.f, 0.f) : FRotator::ZeroRotator;

    if (bInstant)
    {
        // Snap immediately, no audio, no timeline
        DoorSlabMesh->SetRelativeRotation(TargetRotation);
    }
    else
    {
        // Play smooth timeline animation + sound cue
        // (e.g. trigger timeline component or play sound at location)
    }
}

5. What About Audio, Particles, and Death Effects? ​

A common pitfall in save systems is the "Deafening Reload":
If you have 40 torches and 15 doors in a level, and their visual update logic plays a sound effect, loading a save will play 55 sound cues at the exact same instant!

The Golden Rules for Clean Loading: ​

  1. Never play Sound Cues or Spawn Emitters inside BeginPlay or OnAegisPostLoad. Always gate sound and particle effects behind bInstant == false (or player interaction events).
  2. Use WasDestroyedByLoad for Destroyed Actors:
    If an enemy or destructible chest spawns death particles or drops loot in AActor::EndPlay:
    text
    [Event End Play] ──► [Was Destroyed By Load (Aegis)] ──► Branch
                         ├── True:  Do nothing (actor was dead in the save)
                         └── False: Spawn Loot & Play Death VFX (normal gameplay kill)

6. Updating UI (Health Bars, Ammo, Quest HUD) on Load ​

Just like 3D world actors, your Screen UI / UMG HUD needs to reflect loaded player stats.

Method 1: On Player Pawn OnAegisPostLoad ​

When your Player Character receives OnAegisPostLoad:

text
[Event On Aegis Post Load] (Inside BP_PlayerCharacter)
       │
       ▼
[Get Player HUD Widget] ──► [Update Health Bar (CurrentHealth / MaxHealth)]
                        ──► [Update Ammo Counter (CurrentAmmo)]

Method 2: Subsystem Global Delegate (OnLoadCompleted) ​

Your HUD Widget can bind to the global subsystem delegate on construction:

text
[Event Construct] (Inside WBP_GameHUD)
       │
       ▼
[Get Aegis Subsystem] ──► [Assign On Load Completed]
                                │
                                ▼
                    [Event: HandleLoadCompleted]
                                │
                                ▼
                    [Refresh All HUD Elements]

Summary Checklist ​

To make any actor visually and mechanically react to loaded data:

  • [x] Variable marked with SaveGame.
  • [x] Create an UpdateVisuals(bInstant) function.
  • [x] Call UpdateVisuals(true) in Event BeginPlay (for level opens & stream-ins).
  • [x] Implement Aegis Persistence Events and call UpdateVisuals(true) in Event On Aegis Post Load (for in-place checkpoint reloads).
  • [x] Gate sound cues, dust particles, and smooth timelines behind bInstant == false.

Next Steps ​

Aegis Save — Enterprise World Persistence for Unreal Engine 5.