Saving and Loading Your Game

Overview of how to save and load your game

Choose your operating system:

Windows

macOS

Linux

Choose your implementation method:

Blueprints

C++

The meaning of "saving the game" can vary considerably from one game to the next, but the general idea of enabling players to quit the game and then resume where they left off at a later time is a part of most modern games. Depending on what type of game you're making, you may only need a few basic pieces of information, such as the last checkpoint the player reached and maybe which items the player has found. Or you may need much more detailed information, possibly involving things like a long list of the player's social interactions with other in-game characters, or the current status of a variety of quests, mission objectives, or subplots. Unreal Engine 4 (UE4) features a saving and loading system that revolves around one or more custom SaveGame classes that you create to meet your game's specific needs, including all of the information that you need to preserve across multiple play sessions. The system supports the ability to have multiple saved game files, and to save different SaveGame classes to those files. This is useful for separating globally-unlocked features from playthrough-specific game data.

Creating a SaveGame Object

To create a new SaveGame object, create a new Blueprint Class . When the Pick Parent Class dialog pops up, expand the Custom Classes dropdown, then select SaveGame . You can use the search box to jump directly to SaveGame. Name your new Blueprint MySaveGame.

savegame.png

In your new SaveGame object Blueprint, create variables for any information you would like to save.

In this example, there are also variables declared that will be used to store default values for the SaveSlotName and the UserIndex, so that each class that saves to this SaveGame object will not have to independently set those variables. This step is optional, and will cause there to be one save slot that gets overwritten if the default values are not changed.

SaveGameVariables.png

You can set default values for the variables after the Blueprint is compiled.

Creating a SaveGame Object

The USaveGame class sets up an object that can be used as a target for the saving and loading functions declared in Kismet/GameplayStatics.h .

You can create a new class based on USaveGame using the C++ Class Wizard .

SaveGameCode.png

In this example, the new USaveGame class is called UMySaveGame . In order to use it, add the following lines to your game module's header file, after any other #include directives:

MyProject.h

#include "MySaveGame.h"
#include "Kismet/GameplayStatics.h"

In the header file for your SaveGame object, you can declare any variables you want your SaveGame to store.

UPROPERTY(VisibleAnywhere, Category = Basic)
FString PlayerName;

In this example, there are also variables declared that will be used to store default values for the SaveSlotName and the UserIndex , so that each class that saves to this SaveGame object will not have to independently set those variables. This step is optional, and will cause there to be one save slot that gets overwritten if the default values are not changed.

MySaveGame.h

#pragma once

#include "GameFramework/SaveGame.h"
#include "MySaveGame.generated.h"

/**
 * 
 */
UCLASS()
class [PROJECTNAME]_API UMySaveGame : public USaveGame
{
    GENERATED_BODY()

    public:

    UPROPERTY(VisibleAnywhere, Category = Basic)
    FString PlayerName;

    UPROPERTY(VisibleAnywhere, Category = Basic)
    FString SaveSlotName;

    UPROPERTY(VisibleAnywhere, Category = Basic)
    uint32 UserIndex;

    UMySaveGame();
};

Source

Generally, the SaveGame object's source file does not need any particular code to function, unless your particular save system has additional functionality you would like to set up here.

This example does define the values of SaveSlotName and UserIndex in the class constructor, so they can be read out and used by other gameplay classes.

MySaveGame.cpp

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#include "[ProjectName].h"
#include "MySaveGame.h"

UMySaveGame::UMySaveGame()
{
    SaveSlotName = TEXT("TestSaveSlot");
    UserIndex = 0;
}

Saving A Game

Once you have created a SaveGame class, you can populate it with variables to store your game's data. For example, you might create an integer variable to store the player's score, or a string variable for the player's name. When you save the game, you will transfer that information from the current game world into a SaveGame object, and when loading a game, you will copy it from the SaveGame object to game object like Characters, the Player Controller, or the Game Mode.

First, use the Create Save Game Object node to create an object based on your SaveGame class. Make sure you set the Save Game Class input pin's dropdown to the class you've created, which is MySaveGame in this example. The Create Save Game Object node will automatically change its output pin type to match the type you specify with the Save Game Class input pin. This enables you to use it directly, without a Cast To node. You may want to save the resulting object to a variable by using Promote to Variable so that you can easily reuse the object you just created later on.

SaveGameBP_1.png

Now that the Save Game Instance holds your custom SaveGame object, you can send information to it. For example, you can set the Player Name field to "PlayerOne". Continue to set fields in your SaveGame object until it contains all of the data you want to store in the saved game file.

SaveGameBP_2.png

When the SaveGame object is fully populated, use the ASync Save Game To Slot node to finish saving the game. You will also need to provide a file name and a user ID. The file name and user ID in this example will be the default values created earlier. Execution will continue from the top pin immediately, and from the second pin once the savegame operation is complete. The output pins will not be valid until the second pin has executed.

SaveGameBP_3.png

Async Save Game To Slot is the recommended way to save your game, due to its ability to avoid hitches even when saving larger amounts of data. However, if your savegame data is small, or if you are saving from a menu or pause screen, you can save the game with the Save Game To Slot node, shown below, instead.

SaveGameBP_4.png

The following screenshot shows the entire Blueprint process for saving a game with the MySaveGame class:

Click the image above to enlarge.

First, call CreateSaveGameObject (from the UGameplayStatics library) to get a new UMySaveGame object. Once you have the object, you can populate it with the data you want to save. Finally, call SaveGameToSlot or AsyncSaveGameToSlot to write the data out to your device.

Asynchronous Saving

AsyncSaveGameToSlot is the recommended method for saving the game. Running asynchronously prevents a sudden framerate hitch, making it less noticeable to players and avoiding a possible certification issue on some platforms. When the save process is complete, the delegate (of type FAsyncSaveGameToSlotDelegate ) will be called with the slot name, the user index, and a bool indicating success or failure.

if (UMySaveGame* SaveGameInstance = Cast<UMySaveGame>(UGameplayStatics::CreateSaveGameObject(UMySaveGame::StaticClass())))
{
    // Set up the (optional) delegate.
    FAsyncSaveGameToSlotDelegate SavedDelegate;
    // USomeUObjectClass::SaveGameDelegateFunction is a void function that takes the following parameters: const FString& SlotName, const int32 UserIndex, bool bSuccess
    SavedDelegate.BindUObject(SomeUObjectPointer, &USomeUObjectClass::SaveGameDelegateFunction);

    // Set data on the savegame object.
    SaveGameInstance->PlayerName = TEXT("PlayerOne");

    // Start async save process.
    UGameplayStatics::AsyncSaveGameToSlot(SaveGameInstance, SlotNameString, UserIndexInt32, SavedDelegate);
}

Synchronous Saving

SaveGameToSlot is sufficient for small SaveGame formats, and for saving the game while paused or in a menu. It's also easy to use, as it simply saves the game immediately and returns a bool indicating success or failure. For larger amounts of data, or for auto-saving game while the player is still actively interacting with your game world, AsyncSaveGameToSlot is a better choice.

if (UMySaveGame* SaveGameInstance = Cast<UMySaveGame>(UGameplayStatics::CreateSaveGameObject(UMySaveGame::StaticClass())))
{
    // Set data on the savegame object.
    SaveGameInstance->PlayerName = TEXT("PlayerOne");

    // Save the data immediately.
    if (UGameplayStatics::SaveGameToSlot(SaveGameInstance, SlotNameString, UserIndexInt32))
    {
        // Save succeeded.
    }
}

Binary Saving

You can transfer a SaveGame object to memory with the SaveGameToMemory function. This function only offers synchronous operation, but is faster than saving to a drive. The caller provides a reference to a buffer (a TArray<uint8>& ) where the data will be stored. On success, the function returns true.

TArray<uint8> OutSaveData;
if (UGameplayStatics::SaveGameToMemory(SaveGameObject, OutSaveData))
{
    // The operation succeeded, and OutSaveData now contains a binary represenation of the SaveGame object.
}

You can also save binary data directly to a file, similar to the SaveGameToSlot function, by calling SaveDataToSlot with the buffer (a const TArray<uint8>& ) and the slot name and user ID information. As with SaveGameToMemory , this function only offers synchronous operation, and returns a bool to indicate success or failure.

if (UGameplayStatics::SaveDataToSlot(InSaveData, SlotNameString, UserIndexInt32))
{
    // The operation succeeded, and InSaveData has been written to the save file defined by the slot name and user ID we provided.
}

On development platforms, saved game files use the .sav extension and appear in the project's Saved\SaveGames folder. On other platforms, particularly consoles, this varies to accommodate the specific file system.

Loading A Game

To load a saved game, you must provide the save slot name and user ID that you used when you saved it. If the SaveGame you specified exists, the Engine will populate your SaveGame object with the data it contains and return it as a base SaveGame (class USaveGame ) object. You can then cast that object back to your custom SaveGame class and access the data. Depending on what kind of data your SaveGame type contains, you may want to keep a copy of it, or simply use the data and discard the object.

As with saving, you can load synchronously or asynchronously. If you have a large amount of data, or wish to use a loading screen or animation during load time, we recommend the asychronous method. For small amounts of data that load quickly, a synchronous method exists.

To load syncronously, use Load Game From Slot . This node is straightforward and will return a valid SaveGame object if the slot name and user ID you provide identifies a valid SaveGame file. The game will stop while the load operation is in progress.

LoadGameBP.png

Async Load Game From Slot works in roughly the same way, but has two execution output pins. The first pin executes when the load operation begins, and the second executes when it completes. The variable output pins will not be valid until the second pin executes. The Success pin indicates whether or not the load operation succeeded, but you can also pass the returned object to an Is Valid node, or treat failure from the Cast To node to as a catch-all for any error in the loading process.

LoadGameBPAsync.png

Asynchronous Loading

When loading asynchronously with AsyncLoadGameFromSlot , you must provide a callback delegate in order to receive the data that the system loads.

// Set up the delegate.
FAsyncLoadGameFromSlotDelegate LoadedDelegate;
// USomeUObjectClass::LoadGameDelegateFunction is a void function that takes the following parameters: const FString& SlotName, const int32 UserIndex, USaveGame* LoadedGameData
LoadedDelegate.BindUObject(SomeUObjectPointer, &USomeUObjectClass::LoadGameDelegateFunction);
UGameplayStatics::AsyncLoadGameFromSlot(SlotName, 0, LoadedDelegate);

Synchronous Loading

The LoadGameFromSlot function will create and return a USaveGame object if it succeeds.

// Retrieve and cast the USaveGame object to UMySaveGame.
if (UMySaveGame* LoadedGame = Cast<UMySaveGame>(UGameplayStatics::LoadGameFromSlot(SlotName, 0)))
{
    // The operation was successful, so LoadedGame now contains the data we saved earlier.
    UE_LOG(LogTemp, Warning, TEXT("LOADED: %s"), *LoadedGame->PlayerName);
}

Binary Loading

You can load SaveGame data from a file in raw, binary form with LoadDataFromSlot . This function is very similar to LoadGameFromSlot , except that it does not create a SaveGame object. Only synchronous operation is available for this type of loading.

TArray<uint8> OutSaveData;
if (UGameplayStatics::LoadDataFromSlot(OutSaveData, SlotNameString, UserIndexInt32))
{
    // The operation succeeded, and OutSaveData now contains a binary represenation of the SaveGame object.
}

You can also convert this binary data to a SaveGame object by calling LoadGameFromMemory . This is a synchronous call, and returns a new USaveGame object upon success, or a null pointer on failure.

if (UMySaveGame* SaveGameInstance = Cast<UMySaveGame>(LoadGameFromMemory(InSaveData)))
{
    // The operation succeeded, and SaveGameInstance was able to cast to type we expected (UMySaveGame).
}
Help shape the future of Unreal Engine documentation! Tell us how we're doing so we can serve you better.
Take our survey
Dismiss