事件处理(Handling Events)和委托(Delegate)代码示例(二)【UE4】【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 的定义(增加 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)

接着在 GameMode 类中声明一个 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 到场景中的话,当我们进入触发区域,只有最后一个拖进场景的灯会亮,这是因为委托只绑定了最后一个实例函数。

(未完待续)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShaderJoy

您的打赏是我继续写博客的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值