SaveGame
引擎提供USaveGame类来保存和加载游戏数据。
参考官方文档:
Saving Your Game
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Object.h"
#include "SaveGame.generated.h"
/**
* This class acts as a base class for a save game object that can be used to save state about the game.
* When you create your own save game subclass, you would add member variables for the information that you want to save.
* Then when you want to save a game, create an instance of this object using CreateSaveGameObject, fill in the data, and use SaveGameToSlot, providing a slot name.
* To load the game you then just use LoadGameFromSlot, and read the data from the resulting object.
*
* @see https://docs.unrealengine.com/latest/INT/Gameplay/SaveGame
*/
UCLASS(abstract, Blueprintable, BlueprintType)
class ENGINE_API USaveGame : public UObject
{
/**
* @see UGameplayStatics::CreateSaveGameObject
* @see UGameplayStatics::SaveGameToSlot
* @see UGameplayStatics::DoesSaveGameExist
* @see UGameplayStatics::LoadGameFromSlot
* @see UGameplayStatics::DeleteGameInSlot
*/
GENERATED_UCLASS_BODY()
};
RPGSaveGame
摘自官网:
ARPG uses native structures for saving the player’s inventory (which includes souls/experience) to disk, using the class URPGSaveGame. Generally, any critical information should be saved using native structures because this allows the use of native versioning and fix-up code. For URPGSaveGame, this is implemented using the ERPGSaveGameVersion enum and fix-up code in the Serialize function. The reason for this is that user-defined structs can accidentally be modified at any point in time. If a developer were to rename or delete a field, it would result in a player’s save game losing data and potentially causing that player saved data to become completely broken. In general, any critical data should be implemented using native structures with versioning.
保存数据时我们通常用native structures(也就是c++数据结构)来避免数据被篡改。为了兼容过去的保存数据,我们通常要加入版本管理。如这里的ERPGSaveGameVersion。
头文件:
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "ActionRPG.h"
#include "Items/RPGItem.h"
#include "GameFramework/SaveGame.h"
#include "RPGSaveGame.generated.h"
/** List of versions, native code will handle fixups for any old versions */
/** 描述不同的保存版本差异*/
namespace ERPGSaveGameVersion
{
enum type
{
// Initial version
Initial,
// Added Inventory
AddedInventory,
// Added ItemData to store count/level
AddedItemData,
// -----<new versions must be added before this line>-------------------------------------------------
VersionPlusOne,
LatestVersion = VersionPlusOne - 1
};
}
/** Object that is written to and read from the save game archive, with a data version */
/**
被UPROPERTY()标注的变量,当调用Super::Serialize() 时,会被自动序列化
*/
UCLASS(BlueprintType)
class ACTIONRPG_API URPGSaveGame : public USaveGame
{
GENERATED_BODY()
public:
/** Constructor */
URPGSaveGame()
{
// Set to current version, this will get overwritten during serialization when loading
SavedDataVersion = ERPGSaveGameVersion::LatestVersion;
}
/** Map of items to item data */
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = SaveGame)
TMap<FPrimaryAssetId, FRPGItemData> InventoryData;
/** Map of slotted items */
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = SaveGame)
TMap<FRPGItemSlot, FPrimaryAssetId> SlottedItems;
/** User's unique id */
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = SaveGame)
FString UserId;
protected:
/** Deprecated way of storing items, this is read in but not saved out */
UPROPERTY()
TArray<FPrimaryAssetId> InventoryItems_DEPRECATED;
/** What LatestVersion was when the archive was saved */
UPROPERTY()
int32 SavedDataVersion;
/** Overridden to allow version fixups */
virtual void Serialize(FArchive& Ar) override;
};
源文件:
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "RPGSaveGame.h"
#include "RPGGameInstanceBase.h"
// 记得调用Super::Serialize();
void URPGSaveGame::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
// 如果是读取数据,且保存版本不是最新版本,则把过去保存的数据拷贝到新的数据结构中
if (Ar.IsLoading() && SavedDataVersion != ERPGSaveGameVersion::LatestVersion)
{
if (SavedDataVersion < ERPGSaveGameVersion::AddedItemData)
{
// Convert from list to item data map
for (const FPrimaryAssetId& ItemId : InventoryItems_DEPRECATED)
{
InventoryData.Add(ItemId, FRPGItemData(1, 1));
}
InventoryItems_DEPRECATED.Empty();
}
SavedDataVersion = ERPGSaveGameVersion::LatestVersion;
}
}