Save & load

UMovaIxSaveLibrary saves and restores the live state of every interactive object in the level via two Blueprint-callable functions, no per-object save logic to write.

The two functions

// Snapshot every InteractiveComponent's state into a slot.
UMovaIxSaveLibrary::SaveInteractiveGame(WorldContextObject, "Slot1");

// Restore every InteractiveComponent's state from a slot. False if missing.
UMovaIxSaveLibrary::LoadInteractiveGame(WorldContextObject, "Slot1");

Both are UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject")), so they're available from any Blueprint with a world context: a GameInstance, a save/load menu widget, a pause menu. SlotName defaults to "InteractiveSave" if you only need one slot.

What's persisted

SaveInteractiveGame walks every UMovaIxComponent and writes one FMovaIxActorSave per actor, keyed by actor GUID:

Field

What it captures

Components

Each animated component's relative transform and visibility (FMovaIxComponentSave).

CurrentStates

The named state each target currently resolves to.

CycleIndices

Where each ActionCycles entry currently sits, so the next press moves on correctly instead of replaying from the start.

InitialStates

Each animated component's @Zero (original, resting) pose, keyed by component path. Without it, a saved-open object is treated as its own zero on load, and relative animations drift further open instead of returning to true rest.

RunningGroups

Runtime keys of every action group still playing at save time: re-executed on load so an endless interaction resumes (a switched-on fan spins again).

RunningGroupTimes

Per-running-group ElapsedTime, keyed the same way as RunningGroups. With it, a half-open door reopens half-open, and a looping fan resumes mid-cycle at the phase it was left at, not from Time 0.0.

CarriedObjects

Anything a running group was carrying (bCarryObjects): the carried actor, which target component held it, its relative transform, and whether it was simulating physics before the carry parked it. Restored by re-attaching directly instead of re-scanning for overlaps, since a re-scan on load runs against the target's rest pose and can pick up the wrong object.

On load, a group is restarted first and only scrubbed forward to its saved RunningGroupTimes phase afterward: carried objects are re-derived from the saved attach data, not from a fresh overlap scan, for the same reason.

Save-on-quit / load-on-continue

A minimal C++ pattern for a pause menu's Quit and a main menu's Continue:

void UMyPauseMenu::OnQuitToMenu()
{
    UMovaIxSaveLibrary::SaveInteractiveGame(this, TEXT("Slot1"));
    // ... then travel to the main menu.
}

void UMyMainMenu::OnContinuePressed()
{
    // ... after loading the correct level ...
    const bool bOk = UMovaIxSaveLibrary::LoadInteractiveGame(this, TEXT("Slot1"));
    if (!bOk)
    {
        // No save for this slot: fall back to a fresh level, don't hard-fail.
    }
}

The same two calls work unchanged from Blueprint: drop Save Interactive Game / Load Interactive Game under the Mova Interact category and wire them to your Quit / Continue buttons.

Tip

Call LoadInteractiveGame after the level has finished loading and every IC has run its own BeginPlay (which may itself auto-detect a cycle's starting index from the level's placed pose). Calling it earlier (while actors are still spawning in) restores against components that don't exist yet and drops them with no error.

LoadInteractiveGame logs how many actors it restored ("LoadInteractiveGame: restored %d/%d actor(s) from '%s'"): check the log first if a load looks like it did nothing.

SaveInteractiveGame always returns true once the snapshot is written; LoadInteractiveGame returns false only when the named slot doesn't exist or isn't an UMovaIxSaveGame, so a missing-save case (first ever launch) is easy to branch on without a try/catch.

See also

  • Groups & Cycles: what a Cycle's index and a Group's RunningGroups state actually mean at runtime.

  • Multiplayer: save/load runs server-side. A client should never need to call these directly for a networked game.

  • Debug overlay & troubleshooting: mova.ix.Debug shows an object's current state, useful for confirming a load actually applied.