十四、RPGSaveGame & SaveGame

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,

        // ------------------------------------------------------
        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 InventoryData;

    /** Map of slotted items */
    UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = SaveGame)
    TMap 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 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;
    }
}

你可能感兴趣的:(十四、RPGSaveGame & SaveGame)