SaveGameToSlot の保存先を変える方法を考えてみます
Twitter で、SaveGameToSlot の保存先を変えるにはどうすればいいか、という話題が出ていたので
ちょっとやり方を考えてみました
思いついたのは次の 2 つです
FGenericSaveGameSystem
のGetSaveGamePath
を直接書き換えてエンジンビルドする- 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);
};
で、 SaveGameToSlot
と SaveDataToSlot
の内容をいい感じにコピーしてきます
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 版や DoesSaveGameExist
、LoadGameFromSlot
等は各自実装してください
あと、確認したのは Windows のみです
Linux 等のデスクトップ向けなら問題ないと思いますが、モバイルの場合はファイルシステムが異なるため実装はおすすめしません
参考 #
ISaveGameSystem(UnrealEngine Documentation)
https://docs.unrealengine.com/4.27/en-US/API/Runtime/Engine/ISaveGameSystem/