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

举报
ShaderJoy 发表于 2021/12/29 23:18:32 2021/12/29
【摘要】 3. 创建带参数的委托 我们可以通过修改委托的签名来使其接受参数 比如我们需要接受一个参数的话,可以在 GameMode 中这样声明: DECLARE_DELEGATE_OneParam(FParamDelegateSignature, FLinearColor) 注意:这个宏与之前稍有不同,后缀多出了一个 _One...

3. 创建带参数的委托

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

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

DECLARE_DELEGATE_OneParam(FParamDelegateSignature, FLinearColor)
 

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

接着再添加一个 FParamDelegateSignature 成员 

FParamDelegateSignature MyParameterDelegate;	
 

这和之前一样,创建一个委托实例作为 GameMode 成员
然后创建一个 Actor 子类,取名为 ParamDelegateListener,

在头文件中添加以下声明


  
  1. UFUNCTION()
  2. void SetLightColor(FLinearColor LightColor);
  3. UPROPERTY()
  4. UPointLightComponent* PointLight;

ParamDelegateListener.cpp 


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


回到 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 参数)


  
  1. UFUNCTION()
  2. void SetLightColor(FLinearColor LightColor, bool EnableLight);


  
  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 中添加多播的委托声明

需要明确声明为多播

DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature)
 

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

FMulticastDelegateSignature MyMulticastDelegate;	
 


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

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


  
  1. FDelegateHandle MyDelegateHandle;
  2. UPROPERTY()
  3. UPointLightComponent* PointLight;
  4. UFUNCTION()
  5. void ToggleLight();
  6. virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

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

 源文件的代码如下:


  
  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. // Called when the game starts or when spawned
  10. void AMulticastDelegateListener::BeginPlay()
  11. {
  12. Super::BeginPlay();
  13. UWorld* TheWorld = GetWorld();
  14. if (TheWorld != nullptr)
  15. {
  16. AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));
  17. AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);
  18. if (MyGameMode != nullptr)
  19. {
  20. // Adds a UObject-based member function delegate. UObject delegates keep a weak reference to your object.
  21. // 注册一个对象方法
  22. MyDelegateHandle = MyGameMode->MyMulticastDelegate.AddUObject(this, &AMulticastDelegateListener::ToggleLight);
  23. }
  24. }
  25. }
  26. // Called every frame
  27. void AMulticastDelegateListener::Tick( float DeltaTime )
  28. {
  29. Super::Tick( DeltaTime );
  30. }
  31. void AMulticastDelegateListener::ToggleLight()
  32. {
  33. PointLight->ToggleVisibility();
  34. }
  35. void AMulticastDelegateListener::EndPlay(const EEndPlayReason::Type EndPlayReason)
  36. {
  37. Super::EndPlay(EndPlayReason);
  38. UWorld* TheWorld = GetWorld();
  39. if (TheWorld != nullptr)
  40. {
  41. AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));
  42. AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);
  43. if (MyGameMode != nullptr)
  44. {
  45. // 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!
  46. MyGameMode->MyMulticastDelegate.Remove(MyDelegateHandle);
  47. }
  48. }
  49. }

MyTriggerVolume.cpp 的实现为:


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


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

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

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

(未完待续)

文章来源: panda1234lee.blog.csdn.net,作者:panda1234lee,版权归原作者所有,如需转载,请联系作者。

原文链接:panda1234lee.blog.csdn.net/article/details/64123532

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。