camenduru / UEStudy

2 stars 1 forks source link

Delegates and Lambdas #3

Closed camenduru closed 1 year ago

camenduru commented 3 years ago

https://dawnarc.com/2020/01/ue4lambda-notes/ https://jdelezenne.github.io/Codex/UE4/Delegates%20and%20Lambas.html https://qiita.com/unknown_ds/items/2c148256f8f6066d1405 https://www.orfeasel.com/understanding-lambda-expressions/ https://benui.ca/unreal/tfunctionref/ https://answers.unrealengine.com/questions/533323/what-difference-between-delegates.html https://gist.github.com/getnamo/c9ca2095d449381bdeaa https://dokuro.moe/ue4-cpp-how-to-use-delegate/

camenduru commented 3 years ago
//Convenience Wrapper functions

//Using TaskGraph
FGraphEventRef RunLambdaOnGameThread(TFunction< void()> InFunction)
{
    return FFunctionGraphTask::CreateAndDispatchWhenReady(InFunction, TStatId(), nullptr, ENamedThreads::GameThread);
}

FGraphEventRef RunLambdaOnAnyThread(TFunction< void()> InFunction)
{
    return FFunctionGraphTask::CreateAndDispatchWhenReady(InFunction, TStatId(), nullptr, ENamedThreads::AnyThread);
}

//Uses proper threading, for any task that may run longer than about 2 seconds.
void RunLongLambdaOnAnyThread(TFunction< void()> InFunction)
{
    FLambdaRunnable::RunLambdaOnBackGroundThread(InFunction);
}

//Example use case

//Run your code
RunLongLambdaOnAnyThread([] {
    //Your off-thread code here, if you access data here, make sure you make an immutable copy (e.g. const)

    //Assume you got some results you want to pass back
    FString someResults = FString(TEXT("some data results"));

    //copy data
    const FString safeResultString = someResults

    //pass results back to game thread
    RunLambdaOnGameThread([pathString] {
        //Do something with your results
    });
});
camenduru commented 3 years ago
//SENDER

UDELEGATE()
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMyMessageSenderActorEventDispatcher_MulticastDelegate);

UPROPERTY(BlueprintAssignable)
FMyMessageSenderActorEventDispatcher_MulticastDelegate MyMessageSenderActorEventDispatcher;

MyMessageSenderActorEventDispatcher.Broadcast();

//RECEIVER

UDELEGATE()
DECLARE_DYNAMIC_DELEGATE(FMyMessageSenderActorEventDispatcher_SinglecastDelegate);

UPROPERTY()
FMyMessageSenderActorEventDispatcher_SinglecastDelegate OutputDelegate;

UFUNCTION()
virtual void MyMessageSenderActorEventDispatcher_Event();

void AMyMessageReceiverActor::BeginPlay()
{
    Super::BeginPlay();

    AMyMessageSenderActor* GetActorOfClass_ReturnValue = CastChecked<AMyMessageSenderActor>(UGameplayStatics::GetActorOfClass(this, AMyMessageSenderActor::StaticClass()), ECastCheckedType::NullAllowed);
    OutputDelegate.BindUFunction(this, "MyMessageSenderActorEventDispatcher_Event");
    if (::IsValid(GetActorOfClass_ReturnValue))
    {
        GetActorOfClass_ReturnValue->MyMessageSenderActorEventDispatcher.AddUnique(OutputDelegate);
        GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, GetActorOfClass_ReturnValue->GetName());
    }
}

void AMyMessageReceiverActor::MyMessageSenderActorEventDispatcher_Event()
{
    //GLog->Log("C++ Message Receiver: I got the Message");
    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("C++ Message Receiver: I got the Message"));
}
camenduru commented 3 years ago
    //Declaring an Array of Actors
    TArray<AActor*> ActorsArray;

    //Declaring a delegate with one int32 parameter
    DECLARE_DELEGATE_OneParam(MyUsefulDelegate, int32);

    //The following functions populates the ActorsArray with all the Actors which reside inside our current level
    UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass(), ActorsArray);

    //Declaring a MyUsefulDelegate
    MyUsefulDelegate Del;

    //Binding a Lambda to it
    //In this lambda we pass the ActorsArray by value since we won't make any changes
    //or want any changes reflected outside the lambda expression
    //If we don't pass the ActorsArray in the capturelist then we won't be able to have access to it!
    Del.BindLambda([ActorsArray](int32 ActorIndex)
        {
            //Print the name of the Actor which has an index equal to the one we provided (ActorIndex)
            //Make sure we provided a valid index for our array
            if (ActorsArray.IsValidIndex(ActorIndex))
                GLog->Log("Actor with given index:" + ActorsArray[ActorIndex]->GetName());
            else
                GLog->Log("You've entered an invalid index. That's unfortunate :(");
        });

    //Show me the 16th Actor of the Array - Don't forget that TArray is zero-based!
    Del.ExecuteIfBound(15);
camenduru commented 3 years ago
    FTimerHandle TimerHandle;
    FTimerDelegate TimerDelegate;

    //Binding our Lambda expression
    TimerDelegate.BindLambda([&]()
        {
            //Typing the logic of our function here just like any other function...
            GLog->Log("Destroying Actor now...");
            Destroy();
        });//Don't forget the ";" in the end of your parenthesis!

    GetWorld()->GetTimerManager().SetTimer(TimerHandle, TimerDelegate, 5.f, false);