事件处理(Handling Events)和委托(Delegate)代码示例(二)【UE4】【C++】
发布日期:2021-07-01 05:53:53 浏览次数:2 分类:技术文章

本文共 5431 字,大约阅读时间需要 18 分钟。

 

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 valuesAParamDelegateListener::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
("PointLight"); RootComponent = PointLight;}// Called when the game starts or when spawnedvoid AParamDelegateListener::BeginPlay(){ Super::BeginPlay(); UWorld* TheWorld = GetWorld(); if (TheWorld != nullptr) { AGameMode* GameMode = Cast
(UGameplayStatics::GetGameMode(TheWorld)); AUE4TestGameMode * MyGameMode = Cast
(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 framevoid 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 valuesAMulticastDelegateListener::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
("PointLight"); RootComponent = PointLight;}// Called when the game starts or when spawnedvoid AMulticastDelegateListener::BeginPlay(){ Super::BeginPlay(); UWorld* TheWorld = GetWorld(); if (TheWorld != nullptr) { AGameMode* GameMode = Cast
(UGameplayStatics::GetGameMode(TheWorld)); AUE4TestGameMode * MyGameMode = Cast
(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 framevoid 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
(UGameplayStatics::GetGameMode(TheWorld)); AUE4TestGameMode * MyGameMode = Cast
(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 到场景中,当我们进入触发区域,只有最后一个拖进场景的灯会亮,这是因为委托只绑定了最后一个实例函数。

 

(未完待续)

 

转载地址:https://panda1234lee.blog.csdn.net/article/details/64123532 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:事件处理(Handling Events)和委托(Delegate)代码示例(三)【UE4】【C++】
下一篇:事件处理(Handling Events)和委托(Delegate)代码示例(一)【UE4】【C++】

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月30日 19时24分34秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章