This article is from https://blog.csdn.net/panda1234lee/article/details/64123532
3. Create delegation with parameters
We can modify the signature of a delegate to accept parameters.
For example, if we need to accept a parameter, we can declare it in GameMode.
DECLARE_DELEGATE_OneParam(FParamDelegateSignature, FLinearColor)
Note: this macro is slightly different from the previous one. There is an extra ﹐ OneParam suffix, and we also need to specify the type of parameter to be accepted - in this case, FLinearColor.
Then add another FParamDelegateSignature member.
FParamDelegateSignature myParamterDelegate;
As before, create a delegate instance as a GameMode member.
Then create an Actor class called paramtigerlistener.
Add a declaration to the header file
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Components/PointLightComponent.h" #include "ParamTiggerListener.generated.h" UCLASS() class DELEGATETEST_API AParamTiggerListener : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AParamTiggerListener(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; UFUNCTION() void SetLightColor(FLinearColor LightColor); UPROPERTY() UPointLightComponent* PointLight; };
ParamDelegateListener.cpp
// Fill out your copyright notice in the Description page of Project Settings. #include "ParamTiggerListener.h" #include "GameFramework/WorldSettings.h" #include "Kismet/GameplayStatics.h" #include "DelegateTestGameModeBase.h" // Sets default values AParamTiggerListener::AParamTiggerListener() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; PointLight = CreateDefaultSubobject<UPointLightComponent>("PointLight"); this->RootComponent = PointLight; } // Called when the game starts or when spawned void AParamTiggerListener::BeginPlay() { Super::BeginPlay(); UWorld* TheWorld = GetWorld(); if (TheWorld != nullptr) { AGameModeBase* GameMode = Cast<AGameModeBase>(UGameplayStatics::GetGameMode(TheWorld)); ADelegateTestGameModeBase* myGameMode = Cast<ADelegateTestGameModeBase>(GameMode); if (myGameMode != nullptr) { myGameMode->myParamterDelegate.BindUObject(this, &AParamTiggerListener::SetLightColor); } } } // Called every frame void AParamTiggerListener::Tick(float DeltaTime) { Super::Tick(DeltaTime); } void AParamTiggerListener::SetLightColor(FLinearColor LightColor) { PointLight->SetLightColor(LightColor); }
Go back to MyTrigger.cpp and add the following code to the notifyactor beginoverlap function:
myGameMode->myParamterDelegate.ExecuteIfBound(FLinearColor(1, 0, 0, 1));
Unlike before, we need to specify one more parameter, and the parameter type is consistent with our previous delegation declaration.
Obviously, MyTrigger does not need to know the existence of ParamDelegateListener at all, but it can call the functions in ParamDelegateListener through GameMode, which greatly reduces the coupling between classes.
4. Pass load data through delegate binding
With minor modifications, we can pass the extra additional creation-time parameter when we delegate the call, that is, the way we invoke it in MyTrriger is unchanged, and it is ExecuteIfBound(FLinearColor(1, 0, 0, 1)), but it can add some load data and add it to BindUObject in ParamDelegateListener.
First amendment
Aparamdelegatelistener:: BindUObject in beginplay, add a bool load data for it.
myGameMode->myParamterDelegate.BindUObject(this, &AParamTiggerListener::SetLightColor, false);
And modify the definition of SetLightColor
void SetLightColor(FLinearColor LightColor, bool EnableLight);
void AParamTiggerListener::SetLightColor(FLinearColor LightColor, bool EnableLight) { PointLight->SetLightColor(LightColor); PointLight->SetVisibility(EnableLight); }
Note: load data is not limited to delegation with parameters. Other forms of delegation can also be used.
5. Multicast delegate
As mentioned before, delegates are only bound with one function pointer, while multicast delegates are bound with a function pointer set. Each function pointer has a corresponding delegate handle. When broadcasting delegates, they will be activated.
First, add the multicast delegation declaration in GameMode
Need to be explicitly declared multicast
DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature)
Then declare a member of FMulticastDelegateSignature in the class.
FMulticastDelegateSignature MyMulticastDelegate;
Second, create an Actor class called MulticastDelegateListener.
Add a declaration to its header file
//Delegate handle FDelegateHandle MyDelegateHandle; UPROPERTY() UPointLightComponent* PointLight; UFUNCTION() void ToggleLight(); virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
Most of them are similar to the previous Listener class, but there is an additional instance of the delegate handle, which will be used to store the reference of the delegate instance. We need it as a parameter for our add (AddUObject) and remove (Remove).
The code of the source file is as follows:
// Fill out your copyright notice in the Description page of Project Settings. #include "MulticastDelegateListener.h" #include "GameFramework/WorldSettings.h" #include "Kismet/GameplayStatics.h" #include "DelegateTestGameModeBase.h" // Sets default values AMulticastDelegateListener::AMulticastDelegateListener() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; PointLight = CreateDefaultSubobject<UPointLightComponent>("PointLight"); RootComponent = PointLight; } // Called when the game starts or when spawned void AMulticastDelegateListener::BeginPlay() { Super::BeginPlay(); UWorld* TheWorld = GetWorld(); if (TheWorld != nullptr) { AGameModeBase* GameMode = Cast<AGameModeBase>(UGameplayStatics::GetGameMode(TheWorld)); ADelegateTestGameModeBase* myGameMode = Cast<ADelegateTestGameModeBase>(GameMode); if (myGameMode != nullptr) { // Adds a UObject-based member function delegate. UObject delegates keep a weak reference to your object. // Register an object method MyDelegateHandle = myGameMode->MyMulticastDelegate.AddUObject(this, &AMulticastDelegateListener::ToggleLight); } } } // Called every frame void AMulticastDelegateListener::Tick(float DeltaTime) { Super::Tick(DeltaTime); } void AMulticastDelegateListener::ToggleLight() { PointLight->ToggleVisibility(); } void AMulticastDelegateListener::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); UWorld* TheWorld = GetWorld(); if (TheWorld != nullptr) { AGameModeBase* GameMode = Cast<AGameModeBase>(UGameplayStatics::GetGameMode(TheWorld)); ADelegateTestGameModeBase* myGameMode = Cast<ADelegateTestGameModeBase>(GameMode); if (myGameMode != nullptr) { // Removes a function from this multi-cast delegate's invocation list (performance is O(N)). Note that the order of the delegates may not be preserved! myGameMode->MyMulticastDelegate.Remove(MyDelegateHandle); } } }
The implementation of MyTrigger.cpp is as follows:
myGameMode->MyMulticastDelegate.Broadcast();
The broadcast function is similar to our previous ExecuteIfBound function, but it does not need to check whether there is a function bound to a delegate.
The final effect is that if we drag and drop four or five multicastdelegatelisteners into the scene, when we enter the trigger area, their lights will turn on or off at the same time, because each instance function is added to the delegate collection.
If we drag and drop four or five trigger listeners into the scene, when we enter the trigger area, only the light of the last one will be on, because the delegate only binds the last instance function.