Unreal Engine has multiple methods to implement Multithread and/or latent actions. In this tutorial, we will focus on using the UBlueprintAsyncActionBase. When we use this method, Unreal Engine will automatically create blueprint nodes for our latent/asynchronous functionality.
As per the documentation, “BlueprintCallable factory functions for classes which inherit from UBlueprintAsyncActionBase will have a special blueprint node created for it”
“Factory Functions” means functions that can create and return an instance of our class.
We need to follow a few rules and steps to get this working and understand it. I’ll cover them below.
Inherit From UBlueprintAsyncActionBase
First, we need to create a new class to wrap our asynchronous task. This class will contain any internal variables that we need to store and use while our task is running.
The class needs to inherit from UBlueprintAsyncActionBase
It’s worth noting that you can have one or more static factory functions that can create an instance of your class, particularly if their functionality is related. For example, “Save File” and “Load File” as asynchronous functions can both be hosted under the same class, but have their own unique way of initializing the class’s variables.
Take a look at the UAsyncActionHandleSaveGame implementation.
Using a static factory function as the constructor for our class is a convenient way to initialize and store any internal variables we need while the task is executing.
A Note On Garbage Garbage Collection
An important thing to keep in mind is that the class we created is a UObject, which means that it is subject to garbage collection. As our asynchronous task is essentially related functions, executing within a UObject, we need to be aware that our task object will eventually be garbage collected if it doesn’t have a proper owner that is still alive.
To prevent this from happening prematurely, There are two ways:
We can manually call the “RegisterWithGameInstance()” and “SetReadyToDestroy()” functions that come with the class. Register will keep it from being garbage collected and SetReadyToDestroy() will mark it for garbage collection. I.e we are done using it.
Alternatively, we can use the “AddToRoot()” and “RemoveFromRoot()” functions, which allow us to control the lifetime of our task object. When are done doing our asynchronous task, we just need to “RemoveFromRoot()” and garbage collection will take care of deleting it for us.
Create A Blueprint Callable Factory Function
Next, we need to create a static function inside this class that is marked with the “BlueprintCallable” keyword. This function will act as a constructor for our class, allowing us to create and initialize an instance of the class from within a Blueprint graph.
The name we give to this function will determine what it is called within the Blueprint graph, so it’s important to choose a clear and descriptive name that accurately reflects what the function/task does.
UCLASS()
class CUSTOM_API UCustomAsyncBase : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category=Asyncs)
UCustomAsyncBase* MyCustomAsynFunc()
};
Set BlueprintInternalUseOnly to true
When we mark our factory function as “BlueprintCallable“, the engine will automatically create a node for our function within the Blueprint editor.
However, it’s important to note that if we inherit from UBlueprintAsyncActionBase, the engine will also create a node for the factory function of that class. This means that we may end up with two nodes with the same name in the editor.
To avoid this, we can add the meta tag “BlueprintInternalUseOnly = true” to our factory function. This tells the engine that our function is intended for internal use only and should not be exposed directly in the Blueprint graph. This means that we can disable the creation of the first node triggered by “BlueprintCallable” and only leave the one triggered by the UBlueprintAsyncActionBase class.
UCLASS()
class CUSTOM_API UCustomAsyncBase : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category=Asyncs,meta=(BlueprintInternalUseOnly="true))
UCustomAsyncBase* MyCustomAsynFunc()
};