事件处理(Handling Events)和委托(Delegate)代码示例(二)【UE4】【C++】


博客来源于: https://blog.csdn.net/panda1234lee/article/details/64123532

3. 创建带参数的委托

我们可以通过修改委托的签名来使其接受参数

比如我们需要接受一个参数的话,可以在 GameMode 中这样声明:

[cpp]  view plain  copy
  1. DECLARE_DELEGATE_OneParam(FParamDelegateSignature, FLinearColor)  

注意:这个宏与之前稍有不同,后缀多出了一个 _OneParam ,而且我们还需要指定接受参数的类型——本例为 FLinearColor


接着再添加一个 FParamDelegateSignature 成员 
[cpp]  view plain  copy
  1. FParamDelegateSignature MyParameterDelegate;      

这和之前一样,创建一个委托实例作为 GameMode 成员


然后创建一个 Actor 类,取名为 ParamDelegateListener,

在头文件中添加以下声明

[cpp]  view plain  copy
  1. UFUNCTION()  
  2. void SetLightColor(FLinearColor LightColor);  
  3.   
  4. UPROPERTY()  
  5. UPointLightComponent* PointLight;  

ParamDelegateListener.cpp 

[cpp]  view plain  copy
  1. #include "Test.h"  
  2. #include "UE4TestGameMode.h"  
  3. #include "ParamDelegateListener.h"  
  4.   
  5.   
  6. // Sets default values  
  7. AParamDelegateListener::AParamDelegateListener()  
  8. {  
  9.     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
  10.     PrimaryActorTick.bCanEverTick = true;  
  11.     PointLight = CreateDefaultSubobject<UPointLightComponent>("PointLight");  
  12.     RootComponent = PointLight;  
  13.   
  14. }  
  15.   
  16. // Called when the game starts or when spawned  
  17. void AParamDelegateListener::BeginPlay()  
  18. {  
  19.     Super::BeginPlay();  
  20.     UWorld* TheWorld = GetWorld();  
  21.     if (TheWorld != nullptr)  
  22.     {  
  23.         AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));  
  24.         AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);  
  25.         if (MyGameMode != nullptr)  
  26.         {  
  27.             // Binds a UObject-based member function delegate. UObject delegates keep a weak reference to your object. You can use ExecuteIfBound() to call them.(注意绑定的还是 UFUNCTION)  
  28.             MyGameMode->MyParameterDelegate.BindUObject(this, &AParamDelegateListener::SetLightColor);  
  29.         }  
  30.     }  
  31.   
  32. }  
  33.   
  34. // Called every frame  
  35. void AParamDelegateListener::Tick( float DeltaTime )  
  36. {  
  37.     Super::Tick( DeltaTime );  
  38.   
  39. }  
  40. // 1个参数  
  41. void AParamDelegateListener::SetLightColor(FLinearColor LightColor)  
  42. {  
  43.     PointLight->SetLightColor(LightColor);  
  44. }  

回到 MyTriggerVollume.cpp,在 NotifyActorBeginOverlap 函数中添加以下代码:

[cpp]  view plain  copy
  1. 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 负载数据

[cpp]  view plain  copy
  1. MyGameMode->MyParameterDelegate.BindUObject(this, &AParamDelegateListener::SetLightColor, false);  

并修改 SetLightColor 的定义

[cpp]  view plain  copy
  1. UFUNCTION()  
  2. void SetLightColor(FLinearColor LightColor, bool EnableLight);  

[cpp]  view plain  copy
  1. // 2个参数  
  2. void AParamDelegateListener::SetLightColor(FLinearColor LightColor, bool EnableLight)  
  3. {  
  4.     PointLight->SetLightColor(LightColor);  
  5.     PointLight->SetVisibility(EnableLight);  
  6. }  

注意:负载数据并不局限于带参数的委托,其他的委托形式也可以使用


5. 多播委托(Multicast Delegate)

之前说的委托,都是只绑定了一个函数指针,而多播委托绑定的是一个函数指针集合,每个函数指针都有对应的一个委托句柄,当广播(Broadcast)委托的时候,他们将会被激活。


首先在 GameMode 中添加多播的委托声明

需要明确声明为多播

[cpp]  view plain  copy
  1. DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature)  

接着在类中声明一个 FMulticastDelegateSignature 成员

[cpp]  view plain  copy
  1. FMulticastDelegateSignature MyMulticastDelegate;      

其次,创建一个新 Actor 类,命名为 MulticastDelegateListener

在其头文件中添加以下声明:

[cpp]  view plain  copy
  1. FDelegateHandle MyDelegateHandle;  
  2.   
  3. UPROPERTY()  
  4. UPointLightComponent* PointLight;  
  5.   
  6. UFUNCTION()  
  7. void ToggleLight();  
  8.   
  9. virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;  

大部分和之前的 Listener 类很相似,但是多一个 委托句柄实例,将用它来存储委托实例的引用,我们的添加(AddUObject)和移除(Remove)都需要它作为参数


 源文件的代码如下:

[cpp]  view plain  copy
  1. // Sets default values  
  2. AMulticastDelegateListener::AMulticastDelegateListener()  
  3. {  
  4.     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
  5.     PrimaryActorTick.bCanEverTick = true;  
  6.     PointLight = CreateDefaultSubobject<UPointLightComponent>("PointLight");  
  7.     RootComponent = PointLight;  
  8.   
  9. }  
  10.   
  11. // Called when the game starts or when spawned  
  12. void AMulticastDelegateListener::BeginPlay()  
  13. {  
  14.     Super::BeginPlay();  
  15.     UWorld* TheWorld = GetWorld();  
  16.     if (TheWorld != nullptr)  
  17.     {  
  18.         AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));  
  19.         AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);  
  20.         if (MyGameMode != nullptr)  
  21.         {  
  22.             // Adds a UObject-based member function delegate. UObject delegates keep a weak reference to your object.  
  23.             // 注册一个对象方法  
  24.             MyDelegateHandle = MyGameMode->MyMulticastDelegate.AddUObject(this, &AMulticastDelegateListener::ToggleLight);  
  25.         }  
  26.     }  
  27.   
  28. }  
  29.   
  30. // Called every frame  
  31. void AMulticastDelegateListener::Tick( float DeltaTime )  
  32. {  
  33.     Super::Tick( DeltaTime );  
  34.   
  35. }  
  36.   
  37. void AMulticastDelegateListener::ToggleLight()  
  38. {  
  39.     PointLight->ToggleVisibility();  
  40. }  
  41.   
  42. void AMulticastDelegateListener::EndPlay(const EEndPlayReason::Type EndPlayReason)  
  43. {  
  44.     Super::EndPlay(EndPlayReason);  
  45.     UWorld* TheWorld = GetWorld();  
  46.     if (TheWorld != nullptr)  
  47.     {  
  48.         AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));  
  49.         AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);  
  50.         if (MyGameMode != nullptr)  
  51.         {  
  52.             // 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!  
  53.             MyGameMode->MyMulticastDelegate.Remove(MyDelegateHandle);  
  54.         }  
  55.     }  
  56. }  

MyTriggerVolume.cpp 的实现为:

[cpp]  view plain  copy
  1. // Broadcasts this delegate to all bound objects, except to those that may have expired.  
  2. MyGameMode->MyMulticastDelegate.Broadcast();  

广播函数很像我们之前的 ExecuteIfBound函数,但有一点不同,它不需要检查是否有函数绑定在委托上。

最后的效果是,如果我们往场景中拖放了四五个MulticastDelegateListener,当我们进入触发区域,它们的灯会同时打开或关闭,因为每个实例函数都被添加到委托集合当中;

如果拖放了四五个DelegateListener 到场景中,当我们进入触发区域,只有最后一个拖进场景的灯会亮,这是因为委托只绑定了最后一个实例函数。


(未完待续)

猜你喜欢

转载自blog.csdn.net/qq_29523119/article/details/80816134
今日推荐