Saving the Inventory

Documentation Unreal Engine Player Combat Inventory Save

Save the inventory in your game's own Save Game. The plugin saves nothing itself: Export Snapshot hands over each stack with its place, state and belt slot, and Import Snapshot puts it all back.


Set it up

5 minutes
  1. 1

    Make a Save Game

    Create BP_MySaveGame, a Blueprint child of Save Game, with a variable Inventory of type UPC Inventory Snapshot.
  2. 2

    Save and load in the player controller

    In BP_UPC_PlayerController (or your own), save on K and load on L:
    Player controller Blueprint · K saves the inventory
    K
    Pressed
    Released
    Key
    Get Controlled Pawn
    Targetself
    Return Value
    Get Component by Class
    Target
    Component ClassUPC Inventory
    Return Value
    Export Snapshot
    Target is UPC Inventory
    Target
    Return Value
    Create Save Game Object
    Save Game ClassBP_MySaveGame
    Return Value
    SET
    Target is BP My Save Game
    Target
    Inventory
    Save Game to Slot
    Save Game Object
    Slot NameSlot0
    User Index0
    Return Value
    Player controller Blueprint · L loads it
    L
    Pressed
    Released
    Key
    Load Game from Slot
    Slot NameSlot0
    User Index0
    Return Value
    Cast To BP_MySaveGame
    Object
    Cast Failed
    As BP My Save Game
    Get Controlled Pawn
    Targetself
    Return Value
    Get Component by Class
    Target
    Component ClassUPC Inventory
    Return Value
    Inventory
    Target
    Inventory
    Import Snapshot
    Target is UPC Inventory
    Target
    Snapshot
  3. 3

    Press Play, save, and load

    In LVL_UPC_Demo, press F at the hallway loot chest, draw a weapon and press K. Stop, press Play again and press L: the same items return in the same places, belt slots and hand. The chest is full again, since only the player's inventory was saved.

Which stash a snapshot holds

The demo's Stash is a container of the player's inventory, so the player's snapshot saves it with everything else. A stash on a loot chest or any actor with a UPC World Inventory is different: it keeps its items in its own inventory, which exports and imports with its own snapshot (no belt slots, nothing in hand). A level save keeps one snapshot per such chest; the quick start's player snapshot leaves them out.

When to save and load

Both nodes run on the server (your one machine in single player).
  • Save while the controller still possesses the character, so the snapshot holds belt slots; on death, save before it unpossesses.
  • Load from the player controller's On Possess or later (a load menu). The character's Event Possessed is too early: it runs before the belt binds, so no belt slots restore.
  • A load replaces everything the inventory holds, and the inventory then skips its Starting Items, even when the load runs before Begin Play (an On Possess load at map start does).

Keep old saves working

When you move or rename an item asset, keep its redirector or add a CoreRedirect, so older saves still find it. The import leaves out an item that fails to load, and one warning names every such path. A changed config or a lower Max Stack Size needs nothing from you: an item whose saved place is gone or taken goes to the first free place in its container, or past its end with a warning. A stack saved larger than its item's Max Stack Size comes back as several (20 arrows saved, then a Max Stack Size of 10, load as two stacks of 10). A saved container the config no longer lists sends its rows to the first carried container that takes new items.

Debug it

LogUPCInventory warns when an import finds no belt for its slots (call from On Possess or later), when saved items fail to load (each path named), and when a row sits past a container's end.
AdvancedWhat an import does
  1. The belt empties and every stack leaves (a bag's items before the bag, so nothing spills); anything drawn is put away except the unarmed fallback.
  2. Each row returns with exactly its saved stat tags, in its saved place when that still exists, is allowed and is free; a bag returns before its rows.
  3. The belt takes its saved slots in one change, within each slot's Filter and copy limit; with Auto Populate from Inventory each item takes its saved slot, not the first free one.
  4. Each drawn row is drawn at once, without animation, into its saved places, the moveset carrier first.
  • Rules and Access Tags never turn a saved item away; status tags such as Encumbered follow from the result.
  • The snapshot holds the inventory's own items only: it leaves out an item a belt slot or the hand held from outside the inventory, which an import puts away, and a belt slot's wish to bring an item back once its place frees.
  • A bag's rows return in the bag's container under its current Container Tag; if the bag is gone or lost its Container fragment, they go to the first carried container taking new items.
AdvancedWhat a snapshot holds
A UPC Inventory Snapshot holds one row per stack:
FieldHolds
ItemThe item, saved by asset path and loaded again on import.
CountUnits in the stack.
Stat TagsThe stack's own state, such as durability or charges.
PlacementContainer, place and turn; place -1 for an item the belt or hand held outside its container.
Bag RowFor a stack in a bag, the bag's row; else -1.
Belt SlotIts belt slot, or -1.
Drawn, Places, Combat DriverIn hand or worn, the body places taken (right ring, not left), and whether it carried the moveset.
MultiplayerWhat runs where
Export and import run on the server; the owning player's belt, hand and inventory screen follow about a round trip later. A client asking to save sends its own server call to whatever runs the save on the server.
C++C++
SaveGame->Inventory = Inventory->ExportSnapshot();          // UPROPERTY(SaveGame) FUPCInventorySnapshot Inventory;
UGameplayStatics::SaveGameToSlot(SaveGame, TEXT("Slot0"), 0);
 
UMySaveGame* Loaded = Cast<UMySaveGame>(UGameplayStatics::LoadGameFromSlot(TEXT("Slot0"), 0));
Inventory->ImportSnapshot(Loaded->Inventory);
  • FUPCInventorySnapshot holds TArray<FUPCInventorySnapshotEntry> Entries; every field carries the SaveGame flag. In an OnPossess override, call ImportSnapshot after Super::OnPossess, which binds the belt.