Hi all.
I am testing save and load game asynchronously, because if my savegame Object contains a lot of data then the main thread totally freezes for a few seconds.
The main functions are in a custom UBlueprintFunctionLibrary. They are static methods and call instances of FAutoDeleteAsyncTask and FAsyncTask.
I have used FAutoDeleteAsyncTask successfully to save the game.
//TasksFunctionLibrary.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "SaveGameAsync.h"
#include "TasksFunctionLibrary.generated.h"
UCLASS()
class MYPROJECT2_API UTasksFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "SaveGame")
static bool StartSaveGameAutoDeleteAsync(const FString& SlotName, const int UserIndex, class USaveGame* ToSave)
{
(new FAutoDeleteAsyncTask<FSaveGameAutoDeleteAsync>(ToSave, SlotName, UserIndex))->StartBackgroundTask();
return true;
}
//SaveGameAsync.h
#pragma once
#include "CoreMinimal.h"
#include "Async/Async.h"
#include "Kismet/GameplayStatics.h"
class MYPROJECT2_API FSaveGameAutoDeleteAsync : public FNonAbandonableTask
{
private:
class USaveGame* saveGameInstance = nullptr;
FString SlotName;
int UserIndex;
public:
friend class FAutoDeleteAsyncTask<FSaveGameAutoDeleteAsync>;
FSaveGameAutoDeleteAsync(class USaveGame* SaveObject, const FString& SlotName, const int UserIndex): saveGameInstance(SaveObject),SlotName(SlotName),UserIndex(UserIndex){}
~FSaveGameAutoDeleteAsync() { saveGameInstance = nullptr; }
void DoWork()
{
if (saveGameInstance)
{
UGameplayStatics::SaveGameToSlot(saveGameInstance, SlotName, UserIndex);
}
};
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FSaveGameAutoDeleteAsync, STATGROUP_ThreadPoolAsyncTasks);
}
};
But for loading the game, I need the thread to return a savegame object to the main thread. FAutoDeleteAsyncTask destroys itself at the end and I can’t find a way to return the object. (I tried using Delegates, but it crashed).
Testing with FAsyncTask with the following code to load savegame object, the main thread is frozen:
//TasksFunctionLibrary.h
UFUNCTION(BlueprintCallable, Category = "SaveGame")
static bool LoadSaveGameAsync(const FString& SlotName, const int UserIndex, class USaveGame*& Instance)
{
FAsyncTask<FLoadGameAsync>* MyTask = new FAsyncTask<FLoadGameAsync>(SlotName, UserIndex);
MyTask->StartBackgroundTask();
if (MyTask->IsWorkDone())
{
Instance = MyTask->GetTask().GetLoadGame();
}
if (MyTask->IsDone()){}
MyTask->EnsureCompletion();
delete MyTask;
return true;
}
However, if I comment on IsDone and EnsureCompletion methods, the main thread does not freeze, and I can return a starting object and then delete the FAsyncTask.
//TasksFunctionLibrary.h
UFUNCTION(BlueprintCallable, Category = "SaveGame")
static bool LoadSaveGameAsync(const FString& SlotName, const int UserIndex, class USaveGame*& Instance)
{
FAsyncTask<FLoadGameAsync>* MyTask = new FAsyncTask<FLoadGameAsync>(SlotName, UserIndex);
MyTask->StartBackgroundTask();
if (MyTask->IsWorkDone())
{
Instance = MyTask->GetTask().GetLoadGame();
}
/*if (MyTask->IsDone()){}
MyTask->EnsureCompletion();*/
delete MyTask;
return true;
}
//SaveGameAsync.h
#pragma once
#include "CoreMinimal.h"
#include "Async/Async.h"
#include "Kismet/GameplayStatics.h"
class MYPROJECT2_API FLoadGameAsync : public FNonAbandonableTask
{
private:
class USaveGame* saveGameInstance = nullptr;
FString SlotName;
int UserIndex;
public:
friend class FAsyncTask<FLoadGameAsync>;
FLoadGameAsync(const FString& SlotName, const int UserIndex) : SlotName(SlotName), UserIndex(UserIndex) {}
~FLoadGameAsync() { saveGameInstance = nullptr; };
void DoWork() { saveGameInstance = UGameplayStatics::LoadGameFromSlot(SlotName, UserIndex); }
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FLoadGameAsync, STATGROUP_ThreadPoolAsyncTasks);
}
FORCEINLINE class USaveGame* GetLoadGame() const{ return saveGameInstance; }
};
I don’t know if commenting on these two methods is safe. I don’t know if the FAsyncTask has finished when I call to delete. Any advice? Is this correct?