Game Services | Saved Games: Scripting
This section provides a guide to work with the Saved Games scripting API of the Game Services module.
You can access the Saved Games API via the SavedGames property of the GameServices class under the EasyMobile namespace. The API is intended to be used after the initialization has completed (the user has logged in to Game Center or Google Play Games). Failure to do so is a programming error.
Working with saved games involves the following operations:
Operation | Description |
---|---|
Open | A saved game must be opened before it can be used for read or write operation. If you attempt to open a non-existing saved game, a new one will be created and will be opened automatically. You must resolve any conflicts associated with a saved game when opening it. You can have the conflicts resolved automatically using one of the default strategies, or implement your own strategy to resolve them manually. |
Write | Update the data associated with a saved game, the saved game must be open before writing, and it will be closed automatically after the operation has finished. |
Read | Retrieve the data associated with a saved game, the saved game must be open. |
Delete | Delete a saved game from the cloud service |
Opening Saved Game
You can open a saved game using either the OpenWithAutomaticConflictResolution or the OpenWithManualConflictResolution method. Both methods open a saved game with the specified name, or create a new one if none exists. The saved game returned in their callbacks will be open which means it can be used for read or write operation. The difference between the two is whether saved game conflicts, if any, will be resolved automatically or manually.
If the current platform is Google Play Game Services, these methods use the data source specified in the module settings.
Open With Automatic Conflict Resolution
As its name suggests, when opening a saved game using this method, any outstanding conflicts will be resolved automatically using the resolution strategy specified in the module settings.
// Put this on top of the script
using EasyMobile;
// To store the opened saved game.
private SavedGame mySavedGame;
// Open a saved game with automatic conflict resolution
void OpenSavedGame()
{
// Open a saved game named "My_Saved_Game" and resolve conflicts automatically if any.
GameServices.SavedGames.OpenWithAutomaticConflictResolution("My_Saved_Game", OpenSavedGameCallback);
}
// Open saved game callback
void OpenSavedGameCallback(SavedGame savedGame, string error)
{
if (string.IsNullOrEmpty(error))
{
Debug.Log("Saved game opened successfully!");
mySavedGame = savedGame; // keep a reference for later operations
}
else
{
Debug.Log("Open saved game failed with error: " + error);
}
}
Open With Manual Conflict Resolution
If the saved game being opened has outstanding conflicts, they will be resolved manually using the specified conflict resolution function. This function must be implemented by you and it is where you provide your custom conflict resolution strategy, in case none of the default strategies suits your needs. The function will be invoked automatically when a conflict is encountered while opening a saved game and can be invoked multiple times if the saved game has more than one outstanding conflict. Therefore it must be designed to handle multiple invocations.
The conflict resolution function receives the Base and Remote versions of the conflicting saved game (please check out this GameOn! - Saved Games In-Depth (Part 2) YouTube video by Google Developers for an excellent explanation on the concepts of "base" and "remote"). These passed saved games are all open. If OpenWithManualConflictResolution was invoked with prefetchDataOnConflict set to true, the binary data associated with these saved games will loaded and passed to the conflict resolution function too. Use the return value of this function to determine whether the base or the remote will be chosen as the canonical version of the saved game.
The callback will be invoked when all conflicts (if any) have been resolved and the operation finishes.
// Put this on top of the script
using EasyMobile;
// To store the opened saved game.
private SavedGame mySavedGame;
// Open a saved game with manual conflict resolution
void OpenSavedGame()
{
// Open a saved game named "My_Saved_Game" and resolve any outstanding conflicts manually using
// the specified resolution function.
GameServices.SavedGames.OpenWithManualConflictResolution(
"My_Saved_Game",
true, // prefetchDataOnConflict
MyConflictResolutionFunction,
OpenSavedGameCallback
);
}
// The conflict resolution function.
// baseGame and remoteGame are all open.
// If OpenWithManualConflictResolution was invoked with prefetchDataOnConflict set to true,
// baseData and remoteData will contain the binary data associated with baseGame and remoteGame respective.
// They will be null otherwise.
// In this function you can perform required calculation, comparison between two versions, etc. to decide
// which one will be the canonical version of the saved game. Use the return value to indicate your decision.
SavedGameConflictResolutionStrategy MyConflictResolutionFunction(SavedGame baseGame, byte[] baseData,
SavedGame remoteGame,byte[] remoteData)
{
{
// Perform whatever required calculation, comparison, etc. on the two versions
// and their associated data to help you decide which version should be chosen.
...
// After determining the canonical version, use the return value to indicate your choice
return SavedGameConflictResolutionStrategy.UseBase; // use the base version
// If you want to select the remote version instead, just change it to
// return SavedGameConflictResolutionStrategy.UseRemote;
}
}
// Open saved game callback
void OpenSavedGameCallback(SavedGame savedGame, string error)
{
if (string.IsNullOrEmpty(error))
{
Debug.Log("Saved game opened successfully!");
mySavedGame = savedGame; // keep a reference for later operations
}
else
{
Debug.Log("Open saved game failed with error: " + error);
}
}
In case you want to merge the data from different versions, simply specify either the base or the remote as the chosen version. Once all conflicts are resolved and the saved has been opened successfully, perform a write operation using the merge data.
Writing Saved Game Data
To commit new data to a saved game, use the WriteSavedGameData method. As mention earlier, the saved game must be open before writing or the operation will fail. When this method completes successfully, the data is durably persisted to disk and will eventually be uploaded the the cloud (in practice, this process happens very quickly unless the device doesn't have a network connection). After the operation finishes, the saved game will be closed automatically. This is to force it to be opened once again (thus resolving any outstanding conflicts) before another commit can be made.
// Put this on top of the script
using EasyMobile;
// Updates the given binary data to the specified saved game
void WriteSavedGame(SavedGame savedGame, byte[] data)
{
if (savedGame.IsOpen)
{
// The saved game is open and ready for writing
GameServices.SavedGames.WriteSavedGameData(
savedGame,
data,
(SavedGame updatedSavedGame, string error) =>
{
if (string.IsNullOrEmpty(error))
{
Debug.Log("Saved game data has been written successfully!");
}
else
{
Debug.Log("Writing saved game data failed with error: " + error);
}
);
}
else
{
// The saved game is not open. You can optionally open it here and repeat the process.
Debug.Log("You must open the saved game before writing to it.");
}
}
Beside the binary data, you can also update the metadata (properties) of a saved game. Just use the overloading version of WriteSavedGameData that accepts a SavedGameInfoUpdate struct.
Some saved game properties are only available on a certain platform, please review the Game Service > Module Configuration > Saved Game section for detailed information.
// Put this on top of the script
using EasyMobile;
// Updates the binary data AND the properties of a saved game
void WriteSavedGame(SavedGame savedGame, byte[] data)
{
if (savedGame.IsOpen)
{
// The saved game is open and ready for writing
// Prepare the updated metadata of the saved game
SavedGameInfoUpdate.Builder builder = new SavedGameInfoUpdate.Builder();
builder.WithUpdatedDescription("New_Description");
builder.WithUpdatedPlayedTime(TimeSpan.FromMinutes(30)); // update the played time to 30 minutes
SavedGameInfoUpdate infoUpdate = builder.Build();
GameServices.SavedGames.WriteSavedGameData(
savedGame,
data,
infoUpdate, // update saved game properties
(SavedGame updatedSavedGame, string error) =>
{
if (string.IsNullOrEmpty(error))
{
Debug.Log("Saved game data has been written successfully!");
}
else
{
Debug.Log("Writing saved game data failed with error: " + error);
}
);
}
else
{
// The saved game is not open. You can optionally open it here and repeat the process.
Debug.Log("You must open the saved game before writing to it.");
}
}
Reading Saved Game Data
To read a saved game data, use the ReadSavedGameData method. The saved game must be open before reading. The callback will be invoked when the operation finishes and will receive the retrieved data as a byte array, which can be empty if the saved game has no data committed previously.
// Put this on top of the script
using EasyMobile;
// Retrieves the binary data associated with the specified saved game
void ReadSavedGame(SavedGame savedGame)
{
if (savedGame.IsOpen)
{
// The saved game is open and ready for reading
GameServices.SavedGames.ReadSavedGameData(
savedGame,
(SavedGame game, byte[] data, string error) =>
{
if (string.IsNullOrEmpty(error))
{
Debug.Log("Saved game data has been retrieved successfully!");
// Here you can process the data as you wish.
if (data.Length > 0)
{
// Data processing
...
}
else
{
Debug.Log("The saved game has no data!");
}
}
else
{
Debug.Log("Reading saved game data failed with error: " + error);
}
);
}
else
{
// The saved game is not open. You can optionally open it here and repeat the process.
Debug.Log("You must open the saved game before reading its data.");
}
}
Deleting Saved Game
To delete a saved game, simply call the DeleteSavedGame method.
// Put this on top of the script
using EasyMobile;
// Deletes a saved game
void DeleteSavedGame(SavedGame savedGame)
{
GameServices.SavedGames.DeleteSavedGame(savedGame);
}
Fetching All Saved Games
When implement the saved games feature in your game, chances are you will want to show the user a list of existing saved games for them to choose from. In such case, you can use the FetchAllSavedGames method to retrieve all know saved games. A callback will be invoked when the method completes, receiving an array of saved games which can be empty if no saved game was created before. Note that all the returned saved games are NOT open.
If the current platform is Google Play Game Services, this method retrieves saved games from the data source specified in the module settings.
// Put this on top of the script
using EasyMobile;
// Fetches all know saved games.
void FetchSavedGames()
{
GameServices.SavedGames.FetchAllSavedGames(
(SavedGame[] games, string error) =>
{
if (string.IsNullOrEmpty(error))
{
Debug.Log("Fetched saved games successfully! Got " + games.Length + " saved games.");
// Here you can show a UI to display these saved games to the user...
}
else
{
Debug.Log("Fetching saved games failed with error " + error);
}
}
);
}
[Android] Built-in Saved Game UI
On Android, the Saved Games feature of Google Play Game Services offers a built-in UI from which the user can open, select or delete a saved games. You can show this UI by calling the ShowSelectSavedGameUI method. A callback will be invoked when the UI is closed, receiving the selected saved game if any.
This method is a no-op on iOS/Game Center platform.
// Put this on top of the script
using EasyMobile;
// Shows the GPGS built-in saved game UI.
void ShowGPGSSavedGameUI()
{
GameServices.SavedGames.ShowSelectSavedGameUI(
"Select Saved Game", // UI title
5, // maximum number of displayed saved games
true, // allow creating saved games
true, // allow deleting saved games
(SavedGame game, string error) =>
{
if (string.IsNullOrEmpty(error))
{
Debug.Log("You selected saved game: " + game.Name);
}
else
{
Debug.Log(error);
}
}
);
}