Useful specifiers for Blueprint - part 2

Since the last post I've learnt about few another cool specifiers that can help with creating rich Blueprint nodes in C++.

ExpandEnumAsExecs (in and out)

In the last post I have shown that you can create a BP node with multiple outputs. But you can actually create a node with multiple inputs as well!
UENUM(BlueprintType)
enum class EInExecType : uint8
{
    InType1,
    InType2,
    InType3
};

UENUM(BlueprintType)
enum class EOutExecType : uint8
{
    OutType1,
    OutType2,
    OutType3
};

UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Exec,Then"))
void MultipleExecsFuncion(EInExecType Exec, EOutExecType& Then);
Important: Do not use spaces near the comma (like "Exec, Then"), otherwise it won't work.


DeterminesOutputType & DynamicOutputParam

These params are used to dynamically change the return type of the function to match the desired type given as an input parameter. Normally you'd have to return an object of the base class and cast it later.
UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "ActorClass"))
AActor* CreateActorOfClass(TSubclassOf<AActor> ActorClass);

UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "ActorClass", DynamicOutputParam = "NewActors"))
void CreateManyActorsOfClass(TSubclassOf<AActor> ActorClass, TArray<AActor*>& NewActors);
As you can notice the type of the Return Value is the same as it was defined in Actor Class param.

TitleProperty

When this meta property is used with the array of structs it will take a value from this struct and will use it as a title for the entry. It improves the readability when editing arrays in the editor.
USTRUCT(BlueprintType)
struct FMyStruct
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    FString StructId;
    ...
};

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (TitleProperty = "StructId"))
TArray<FMyStruct> MyStructs;
This array will use "StructId" as a title for it's entries:
 

Naming output pins

Normally when you make a function like this:
UFUNCTION(BlueprintCallable)
bool GetBoolResult();

The output pin will always be named "Return Value". To change it - the node must be declared in a different way:
UFUNCTION(BlueprintCallable)
void GetBoolResult(UPARAM(DisplayName = "MyBoolResult") bool& bResult);
This code will create a node with a nicely named output pin:


Blueprint Async Action

This is more complicated example, because it requires to create a whole new class. It allows to create a BP nodes that can execute asynchronous actions like http request.

I'm going to show the complete code and describe it using comments.

AsyncRequest.h:
// Declare delegate which will be used as an output for the node.
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnComplete, bool, Success);

// Create class that inherits UBlueprintAsyncActionBase.
UCLASS()
class MYPROJECT_API UAsyncRequest : public UBlueprintAsyncActionBase
{
    GENERATED_BODY()

public:

    // Delegate which will be used as an output for the node.
    UPROPERTY(BlueprintAssignable)
    FOnComplete OnComplete;

    // Actual node function. Must return an object of thic class.
    UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject"))
    static UAsyncRequest* AsyncRequest(UObject* WorldContextObject, FString InURL);

    // Overridden method which will be launched asynchronously.
    void Activate() override;

protected:

    // Place to save an URL parameter
    FString Proxy_URL;
};
AsyncRequest.cpp:
UAsyncRequest* UAsyncRequest::AsyncRequest(UObject* WorldContextObject, FString InURL)
{
    // Create new object of this class, pass all given parameters and return.
    UAsyncRequest* Proxy = NewObject<UAsyncRequest>();
    Proxy->Proxy_URL = InURL;
    return Proxy;
}

void UAsyncRequest::Activate()
{
    // Activate will run after this class object is created.
    // Prepare request and run it.
    FHttpRequestRef HttpRequest = FHttpModule::Get().CreateRequest();
    HttpRequest->SetURL(Proxy_URL);
    HttpRequest->SetVerb(TEXT("GET"));
    
    TWeakObjectPtr<ThisClass> WeakThis(this);
    HttpRequest->OnProcessRequestComplete().BindLambda([WeakThis](FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded)
    {
        if (ThisClass* StrongThis = WeakThis.Get())
        {
            // Run OnComplete delegate to exit this node.
            StrongThis->OnComplete.Broadcast(bSucceeded);
        }
    });

    HttpRequest->ProcessRequest();
}
Result:

Conclusion

I hope you'll find any of these tips helpful. Cheers! <3