メインコンテンツへスキップ
  1. 記事/

SaveGameToSlotの保存先を変えてみる

UnrealEngine UE4.26 SaveGame

SaveGameToSlot の保存先を変える方法を考えてみます

Twitter で、SaveGameToSlot の保存先を変えるにはどうすればいいか、という話題が出ていたので
ちょっとやり方を考えてみました

思いついたのは次の 2 つです

  • FGenericSaveGameSystemGetSaveGamePath を直接書き換えてエンジンビルドする
  • Plugin で別の SaveGameToSlot 関数を作り、その中で保存先を変える

前者はガッツリエンジン改造です
ただ、変えたいのは 1 行だけなので、エンジン改造を厭わない人なら余裕ではないかと思います
影響範囲もそれほどなさそうですし…

後者はそこそこ C++を書く必要があります
といっても、 GamePlayStatics.h にある SaveGameToSlot 関連のコードをいくつか持ってくる必要がある、という程度です
あとは、既存の SaveGameToSlot を使っている部分を全部置き換える必要があるぐらいでしょうか
難易度はそこまで高くありません

書き換えたいところ #

/Engine/Source/Runtime/Engine/Public/SaveGameSystem.h 内の FGenericSaveGameSystem::GetSaveGamePath の内容です

	/** Get the path to save game file for the given name, a platform _may_ be able to simply override this and no other functions above */
	virtual FString GetSaveGamePath(const TCHAR* Name)
	{
		return FString::Printf(TEXT("%sSaveGames/%s.sav"), *FPaths::ProjectSavedDir(), Name);
	}

FPaths::ProjectSaveDir() のところを、別のディレクトリに置き換えてしまえば、好きなところに移動できますね

Plugin で関数作成 #

SaveGameSystem を用意 #

まず、 FGenericSaveGameSystem を継承して、 GetSaveGamePath の部分をオーバーライドしましょう
今回は FPaths::ProjectDir()、つまりプロジェクトルートにしてみます


#pragma once

#include "CoreMinimal.h"

#include "SaveGameSystem.h"
#include "Misc/Paths.h"

/**
 *
 */
class HACKEDSAVEGAME_API FHackedSaveGameSystem : public FGenericSaveGameSystem
{

protected:

protected:
  // 上書き
	virtual FString GetSaveGamePath(const TCHAR* Name) override {
		return FString::Printf(TEXT("%sSaveGames/%s.sav"), *FPaths::ProjectDir(), Name);
	}

};

※パッケージしたときの Launcher(Windows だと WindowsNoEditor の直下にできる exe のこと)と同じ階層にしたい場合は FPaths::LaunchDir() のほうがよいかも

SaveGameToSlot 関連のコードをコピー #

BlueprintFunctionLibrary を作り、/Engine/Source/Runtime/Engine/Classes/Kismet/GameplayStatics.h から、必要な部分をコピーしてきます 今回は SaveGame だけの検証なので、 SaveDataToSlot だけが必要です

それと同時に、呼び出し用の関数を別途定義しておきましょう
サンプルでは SaveGameToSlotHacked としています

以下 Header


#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "HackedSaveGameLibrary.generated.h"

class USaveGame;


/**
 *
 */
UCLASS()
class HACKEDSAVEGAME_API UHackedSaveGameLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()


public:

	/**
	 * Save the contents of the buffer to a platform-specific save slot/file
	 * @param InSaveData		Data to save
	 * @param SlotName			Name of save game slot to save to.
	 * @param UserIndex			For some platforms, master user index to identify the user doing the saving.
	 * @return					Whether we successfully saved this information
	 */
	static bool SaveDataToSlot(const TArray<uint8>& InSaveData, const FString& SlotName, const int32 UserIndex);

	// セーブ先を変えるSaveGameToSlot
	UFUNCTION(BlueprintCallable, Category = "HackedSaveGame")
		static bool SaveGameToSlotHacked(USaveGame* SaveGameObject, const FString& SlotName, const int32 UserIndex);

};

で、 SaveGameToSlotSaveDataToSlot の内容をいい感じにコピーしてきます
SaveGameToSlot の途中で出てくる SaveGameToMemory に関しては特に問題がないので、元の GameplayStatics のものを流用しましょう


#include "HackedSaveGameLibrary.h"

#include "Kismet/GameplayStatics.h"
#include "SaveGameSystem.h"
#include "HackedSaveGameSystem.h"

ISaveGameSystem* GetSaveGameSystem()
{
    static FHackedSaveGameSystem SaveSystem;
    return &SaveSystem;
}

bool UHackedSaveGameLibrary::SaveGameToSlotHacked(USaveGame* SaveGameObject, const FString& SlotName, const int32 UserIndex)
{
    // SaveGameToSlotの内容をベースにする

    TArray<uint8> ObjectBytes;
    if (UGameplayStatics::SaveGameToMemory(SaveGameObject, ObjectBytes)) {
        return UHackedSaveGameLibrary::SaveDataToSlot(ObjectBytes, SlotName, UserIndex);
    }
    return false;
}


bool UHackedSaveGameLibrary::SaveDataToSlot(const TArray<uint8>& InSaveData, const FString& SlotName, const int32 UserIndex)
{

    // SaveGameSystemは独自実装のものを使う
    ISaveGameSystem* SaveSystem = GetSaveGameSystem();
    // ISaveGameSystem* SaveSystem = IPlatformFeaturesModule::Get().GetSaveGameSystem();

    if (SaveSystem && InSaveData.Num() > 0 && SlotName.Len() > 0)
    {
        return SaveSystem->SaveGame(false, *SlotName, UserIndex, InSaveData);
    }

    return false;

}

使ってみる #

ビルドできたら使ってみましょう
SaveGameToSlot の部分を置き換えるだけです

使い方

注意 #

このサンプルでは SaveGameToSlot しかカバーしてません
Async 版や DoesSaveGameExistLoadGameFromSlot 等は各自実装してください

あと、確認したのは Windows のみです
Linux 等のデスクトップ向けなら問題ないと思いますが、モバイルの場合はファイルシステムが異なるため実装はおすすめしません

参考 #

ISaveGameSystem(UnrealEngine Documentation)
https://docs.unrealengine.com/4.27/en-US/API/Runtime/Engine/ISaveGameSystem/

Related

Wildcardについての覚書
UnrealEngine UE4.26 Blueprints
StaticMeshのPivot変更について
UnrealEngine UE4.26 StaticMesh Plugins
LandscapeのSubSectionについて
UnrealEngine UE4.26 Landscape