本篇参考自https://blog.csdn.net/panda1234lee/article/details/64123532
3.创建带参数的委托
我们可以通过修改委托的签名来使其接受参数。
比如我们需要接受一个参数的话,可以在GameMode中这样声明。
DECLARE_DELEGATE_OneParam(FParamDelegateSignature, FLinearColor)
注意:这个宏与之前稍有不同,后缀多出了一个 _OneParam ,而且我们还需要指定接受参数的类型——本例为 FLinearColor。
接着再添加一个FParamDelegateSignature成员。
FParamDelegateSignature myParamterDelegate;
这和之前一样,创建一个委托实例作为GameMode成员。
然后创建一个名为ParamTiggerListener的Actor类。
在头文件中添加声明
// 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);
}
回到MyTrigger.cpp,在 NotifyActorBeginOverlap 函数中添加以下代码:
myGameMode->myParamterDelegate.ExecuteIfBound(FLinearColor(1, 0, 0, 1));
与之前不同的是,我们需要多指定一个参数,参数类型与我们之前的委托声明一致。
显然,MyTrigger根本无需知道ParamDelegateListener的存在,却通过GameMode就可以调用ParamDelegateListener中的函数了,很大的程度上降低了类间的耦合度。
4.通过委托绑定传递负载数据(Payload Data)
稍加修改,我们就可以在委托调用时传递额外创建时的参数(additional creation-time parameter),即我们在MyTrriger中调用的方式不变,任然是ExecuteIfBound(FLinearColor(1, 0, 0, 1)),但可以额外添加一些负载数据,在ParamDelegateListener中的BindUObject上添加。
首先修改
AParamDelegateListener::BeginPlay 中的 BindUObject,为其添加一个 bool 负载数据。
myGameMode->myParamterDelegate.BindUObject(this, &AParamTiggerListener::SetLightColor, false);
并修改SetLightColor的定义
void SetLightColor(FLinearColor LightColor, bool EnableLight);
void AParamTiggerListener::SetLightColor(FLinearColor LightColor, bool EnableLight)
{
PointLight->SetLightColor(LightColor);
PointLight->SetVisibility(EnableLight);
}
注意:负载数据并不局限于带参数的委托,其他的委托形式也可以使用。
5.多播委托(Multicast Delegate)
之前说的委托,都只绑定了一个函数指针,而多播委托绑定的一个函数指针集合,每个函数指针都有对应的一个委托句柄,当广播(Broadcast)委托的时候,他们将会被激活。
首先在GameMode中添加多播的委托声明
需要明确声明为多播
DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature)
接着在类中声明一个FMulticastDelegateSignature的成员。
FMulticastDelegateSignature MyMulticastDelegate;
其次,创建一个名为 MulticastDelegateListener的Actor 类。
在其头文件中添加声明
//委托句柄
FDelegateHandle MyDelegateHandle;
UPROPERTY()
UPointLightComponent* PointLight;
UFUNCTION()
void ToggleLight();
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
大部分和之前的Listener类相似,但是多了一个委托句柄实例,将用它来存储委托示例的引用,我们的添加(AddUObject)和移除(Remove)都需要它作为参数。
源文件的代码如下:
// 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.
// 注册一个对象方法
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);
}
}
}
MyTrigger.cpp的实现为:
myGameMode->MyMulticastDelegate.Broadcast();
广播函数很像我们之前的ExecuteIfBound函数,但有一点不同的是,它不需要检查是否有函数绑定在委托上。
最后的效果是,如果我们往场景中拖放四五个MulticastDelegateListener,当我们进入触发区域时,他们的灯会同时打开或关闭,因为每个实例函数都被添加到了委托集合当中。
如果拖放四五个TriggerListener到场景中,当我们进入触发区域,只有最后一个拖进场景的灯会亮,这是因为委托只绑定了最后一个实例函数。