# 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 ```cpp // 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: ```cpp 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 - {doc}`../concepts/groups-and-cycles`: what a Cycle's index and a Group's `RunningGroups` state actually mean at runtime. - {doc}`multiplayer`: save/load runs server-side. A client should never need to call these directly for a networked game. - {doc}`debugging`: `mova.ix.Debug` shows an object's current state, useful for confirming a load actually applied.