3. 建立帶參數的委托
我們可以通過修改委托的簽名來使其接受參數
比如我們需要接受一個參數的話,可以在 GameMode 中這樣聲明:
DECLARE_DELEGATE_OneParam(FParamDelegateSignature, FLinearColor)
注意:這個宏與之前稍有不同,字尾多出了一個 _OneParam ,而且我們還需要指定接受參數的類型——本例為 FLinearColor
接着再添加一個 FParamDelegateSignature 成員
FParamDelegateSignature MyParameterDelegate;
這和之前一樣,建立一個委托執行個體作為 GameMode 成員
然後建立一個 Actor 子類,取名為 ParamDelegateListener,
在頭檔案中添加以下聲明
UFUNCTION()
void SetLightColor(FLinearColor LightColor);
UPROPERTY()
UPointLightComponent* PointLight;
ParamDelegateListener.cpp
#include "Test.h"
#include "UE4TestGameMode.h"
#include "ParamDelegateListener.h"
// Sets default values
AParamDelegateListener::AParamDelegateListener()
{
// 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 AParamDelegateListener::BeginPlay()
{
Super::BeginPlay();
UWorld* TheWorld = GetWorld();
if (TheWorld != nullptr)
{
AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));
AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);
if (MyGameMode != nullptr)
{
// Binds a UObject-based member function delegate. UObject delegates keep a weak reference to your object. You can use ExecuteIfBound() to call them.(注意綁定的還是 UFUNCTION)
MyGameMode->MyParameterDelegate.BindUObject(this, &AParamDelegateListener::SetLightColor);
}
}
}
// Called every frame
void AParamDelegateListener::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
}
// 1個參數
void AParamDelegateListener::SetLightColor(FLinearColor LightColor)
{
PointLight->SetLightColor(LightColor);
}
回到 MyTriggerVollume.cpp,在 NotifyActorBeginOverlap 函數中添加以下代碼:
MyGameMode->MyParameterDelegate.ExecuteIfBound(FLinearColor(1, 0, 0, 1)); // 帶一個參數
與之前不同的是,我們需要多指定一個參數,參數類型和我們之前的委托聲明一緻。
顯然,MyTriggerVolume 壓根就無需知道 ParamDelegateListener 的存在,卻通過 GameMode 就可以調用 ParamDelegateListener 的函數了,很大程度上降低了類間的耦合度。
解綁委托方式與之前相同,不再贅述。
4.通過委托綁定傳遞負載資料(Payload Data)
稍加修改,我們就可以在委托被調用時傳遞額外建立時的參數(additional creation-time parameter),即我們在 MyTriggerVolume 中的調用方式不變,仍然是 ExecuteIfBound(FLinearColor(1, 0, 0, 1)),但可以額外添加一些負載資料,在 ParamDelegateListener 中的 BindUObject 上添加。
首先修改 AParamDelegateListener::BeginPlay 中的 BindUObject,為其添加一個 bool 負載資料
MyGameMode->MyParameterDelegate.BindUObject(this, &AParamDelegateListener::SetLightColor, false);
并修改 SetLightColor 的定義(增加 bool 參數)
UFUNCTION()
void SetLightColor(FLinearColor LightColor, bool EnableLight);
// 2個參數
void AParamDelegateListener::SetLightColor(FLinearColor LightColor, bool EnableLight)
{
PointLight->SetLightColor(LightColor);
PointLight->SetVisibility(EnableLight);
}
注意:負載資料并不局限于帶參數的委托,其他的委托形式也可以使用
5. 多點傳播委托(Multicast Delegate)
之前說的委托,都是隻綁定了一個函數指針,而多點傳播委托綁定的是一個函數指針集合,每個函數指針都有對應的一個委托句柄,當廣播(Broadcast)委托的時候,他們将會被激活。
首先在 GameMode 中添加多點傳播的委托聲明
需要明确聲明為多點傳播
DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature)
其次,建立一個新 Actor 類,命名為 MulticastDelegateListener
在其頭檔案中添加以下聲明:
FDelegateHandle MyDelegateHandle;
UPROPERTY()
UPointLightComponent* PointLight;
UFUNCTION()
void ToggleLight();
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
大部分和之前的 Listener 類很相似,但是多一個 委托句柄執行個體,将用它來存儲委托執行個體的引用,我們的添加(AddUObject)和移除(Remove)都需要它作為參數
源檔案的代碼如下:
// 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)
{
AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));
AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(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)
{
AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));
AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(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);
}
}
}
MyTriggerVolume.cpp 的實作為:
// Broadcasts this delegate to all bound objects, except to those that may have expired.
MyGameMode->MyMulticastDelegate.Broadcast();
廣播函數很像我們之前的 ExecuteIfBound 函數,但有一點不同,它不需要檢查是否有函數綁定在委托上。
最後的效果是,如果我們往場景中拖放了四五個 MulticastDelegateListener ,當我們進入觸發區域,它們的燈會同時打開或關閉,因為每個執行個體函數都被添加到委托集合當中;
如果拖放了四五個 DelegateListener 到場景中,當我們進入觸發區域,隻有最後一個拖進場景的燈會亮,這是因為委托隻綁定了最後一個執行個體函數。
(未完待續)