Si bien lo que se entiende por guardar la partida puede ser muy distinto entre un juego y otro, la idea general de permitir a los jugadores abandonar la partida y, más tarde, reanudarla justo por donde lo han dejado es algo que forma parte de los videojuegos más modernos. En esta línea, según el tipo de juego que estés desarrollando, es posible que solo necesites algunos datos básicos, como el último punto de control al que ha llegado el jugador o los objetos que este haya encontrado. No obstante, hay casos en los que hará falta contar información más detallada, como muy probablemente listas de las interacciones sociales del jugador con otros personajes del juego o el estado actual de algunas misiones, objetivos o historias secundarias.
Unreal Engine (UE) incluye un sistema de guardado y carga que se basa en una o varias clases personalizadas de SaveGame que puedes crear según las necesidades específicas del juego. Aquí, por ejemplo, entraría toda la información que tengas que conservar en múltiples sesiones de juego. El sistema permite tener varios archivos de partidas guardadas y guardar distintas clases *SaveGame* en ellos. Esto resulta útil para separar funciones desbloqueadas en todo el mundo de datos específicos del juego.
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.
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:
#include "MySaveGame.h"
#include "Kismet/GameplayStatics.h"Header
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.
#pragma once
#include "GameFramework/SaveGame.h"
#include "MySaveGame.generated.h"
/**
*
*/
UCLASS()
class [PROJECTNAME]_API UMySaveGame : public USaveGame
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.
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "[ProjectName].h"
#include "MySaveGame.h"
UMySaveGame::UMySaveGame()
{
SaveSlotName = TEXT("TestSaveSlot");
UserIndex = 0;
}Cómo guardar una partida
Cuando hayas creado una clase *SaveGame*, puedes añadirle variables para almacenar los datos del juego. Por ejemplo, podrías crear una variable de entero para guardar la puntuación del jugador o una variable de cadena para su nombre. Cuando guardes la partida, transferirás dicha información desde el mundo del juego hasta un objeto SaveGame que, más tarde, al cargar la partida, copiarás en objetos del juego como personajes, el PlayerController o el GameMode.
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");
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.
}En las plataformas de desarrollo, los archivos de partidas guardadas usan la extensión .sav y aparecerá en la carpeta Saved\SaveGames del proyecto. En otras, en cambio, sobre todo en consolas, esto dependerá del sistema de archivo específico.
Cómo cargar una partida
Para cargar una partida guardada, primero debes indicar el nombre de la ranura de guardado y el ID de usuario que has usado para guardarla. En caso de que exista la clase SaveGame, el motor agregará todos los datos que contiene al objeto SaveGame y lo devolverá en forma de objeto SaveGame base (de clase USaveGame). De esta forma, podrás volver a convertirlo en la clase *SaveGame* que tenías antes y acceder a los datos. Según el tipo de datos que contenga el *SaveGame*, tal vez te interese guardar una copia o simplemente usar los datos y eliminar el objeto.
Tal y como sucede a la hora de guardar, puedes cargar una partida de forma síncrona o asíncrona. Nuestra recomendación es que, si dispones de una gran cantidad de datos o si quieres incluir una pantalla de carga o una animación mientras se carga, uses mejor el método asíncrono. No obstante, también tienes un método síncrono para cantidades pequeñas de datos que se carguen rápidamente.
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).
}