[UE4]事件處理(Handling Events)和委托(Delegate)代碼示例(二)【C++】


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 的定義

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)  

接着在類中聲明一個 FMulticastDelegateSignature 成員

FMulticastDelegateSignature MyMulticastDelegate;      

其次,創建一個新 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 到場景中,當我們進入觸發區域,只有最后一個拖進場景的燈會亮,這是因為委托只綁定了最后一個實例函數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM