UE4/C++: USaveData 系のセーブデータの Slot 群をすべて取得する方法
USaveData
派生型のセーブデータを作り UGameplayStatics::SaveGameToSlot
, UGameplayStatics::LoadGameFromSlot
を用いてセーブとロードを実装する手法を用いるとそれ自体の実装労力は節約できて嬉しい。しかし、これらの API は UE4 プロジェクトの開発時に文字列型の Slot
が確定している前提で設計されているらしく、実行時にユーザーが任意の Slot
名称を与えるユースケースではその一覧の取得を行う手段が無い(UE4-4.21現在)。
このためもあってか、一般的に多くの UE4 プロジェクトでは、Slot
を開発時に幾つかの固定の名称か、あるいは連番を文字列化して処理する仕組みが採用される例が多いように見える。実際にゲームとして流通するアプリでもセーブデータがスロット0番からN番のように連番で用意されている例を多く見かける(個人の感想です)。一般的に幾つかのセーブスロットを用意する方式で構わないアプリではそうした対応で、簡単かつユーザーにとっても十分で実用にも問題無く快適に使用できる。
しかし、そうではない、ユーザーに可能な限り任意性の高い Slot
名称を与えたい仕組みが必要なアプリを作りたい場合もある。このような場合には、次の様にして実行中の UE4 アプリのセーブデータの Slot
の一覧を取得する実装をプロジェクトへ実装する:
// MySaveDataUtility.h #pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "MySaveDataUtility.generated.h" UCLASS() class UMySaveDataUtility : public UBlueprintFunctionLibrary { GENERATED_BODY() public: UFUNCTION( BlueprintCallable, BlueprintPure ) static TArray< FString > GetSaveSlots(); };
// MySaveDataUtility.cpp #pragma once #include "MySaveDataUtility.h" #include "Runtime/Engine/Public/PlatformFeatures.h" #include "Runtime/Core/Public/HAL/PlatformFilemanager.h" #include "Runtime/Core/Public/Misc/LocalTimestampDirectoryVisitor.h" #include "Runtime/Engine/Classes/Kismet/GameplayStatics.h" DEFINE_LOG_CATEGORY_STATIC( MySaveDataUtility , Log, All ); TArray< FString > UMySaveDataUtility::GetSaveSlots() { TArray< FString > Slots; if ( ! IPlatformFeaturesModule::Get().GetSaveGameSystem() ) { UE_LOG( MySaveDataUtility, Error, TEXT( "GetSaveSlots: GetSaveGameSystem was failed." ) return { }; } TArray< FString > Empty; IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); FLocalTimestampDirectoryVisitor Visitor( PlatformFile, Empty, Empty, false ); PlatformFile.IterateDirectory( *( FPaths::ProjectSavedDir() / TEXT( "SaveGames" ) ), Visitor ); for ( TMap< FString, FDateTime >::TIterator i( Visitor.FileTimes ); i; ++i) { const FString FilePath = i.Key(); const FString FileName = FPaths::GetCleanFilename( FilePath ); if ( FPaths::GetExtension( FileName, false ).Equals( TEXT( "sav" ), ESearchCase::IgnoreCase) ) Slots.Emplace( FPaths::GetBaseFilename( FileName ) ); } return Slots; }
セーブデータのディレクトリーを浚い、 .sav
拡張子のファイルベース名部分を Slot
名称として回収してセーブデータ群の Slot
の一覧を取得している。泥臭い(´・ω・`)
別法としてはそもそも Slot
を使わずに UGameplayStatics::SaveGameFromMemory
, UGameplayStatics::LoadGameFromMemory
を使い、 UE4 が用意する Saved/SaveGames
へのファイル保存の仕組みを使わずにプログラマーが直接ファイルシステムなり保存先のデータベースなりオンラインのストレージなり何なりの制御を実装してセーブデータをシリアライズする方法もある。と、言っても、そのような必要な必要がある場合は、そもそも USaveData
を使わずに UE4 とは独立したセーブデータのシリアライザーを実装した方が善い場合が多いかもしれない。