UE5--浅析委托原理(Delegate)
委托概述
委托是一种用于事件处理的机制。通过使用委托,可以将一个或多个函数绑定到一个事件上,在事件触发时自动调用这些函数。代理也叫做委托,比如:跳,跑,开枪,伤害等响应,就是注册一个委托回调,其作用就是提供一种消息机制,都知道消息的传递需要发送方和接收方,而代理的过程也可分为这两大部分,我们可以换个名字分别叫做:触发点和执行点,这就是代理的主要部分,记住这个两个点就能记住代理原理。这里面也有很多的类型擦除,类型还原,还有函数签名转换。
委托种类
委托的使用
三个步骤:
1.声明委托代理
2.绑定函数到代理上
3.代理的调用
单播
DECLARE_DELEGATE( FSimpleDelegate ); // 无参、无返回值
DECLARE_DELEGATE_OneParam(FPakEncryptionKeyDelegate, uint8[32]); // 1个参数、无返回值
DECLARE_DELEGATE_TwoParams(FPakSigningKeysDelegate, TArray<uint8>&, TArray<uint8>&); // 2个参数、无返回值
DECLARE_DELEGATE_RetVal_ThreeParams(bool, FOnMountPak, const FString&, int32, IPlatformFile::FDirectoryVisitor*); // 3个参数、bool返回
声明定义单播可以有参数和无参数
单播代理委托,指的是只能绑定一个函数指针的委托,实现一对一的通知。
单播调用方式为 ExecuteIfBound()
单播删除方式为 Unbind()
单播绑定方法为 Bind…(如 BindStatic,BindRaw,BindUObject等)
多播
//多播委托
DECLARE_MULTICAST_DELEGATE(DelegateName);
DECLARE_MULTICAST_DELEGATE_ONEPARAM(DelegateName, Param1Type);
DECLARE_MULTICAST_DELEGATE_XXXPARAMS(DelegateName, Param1Type,...);
多播代理委托,指的是能绑定多个函数指针的委托,实现一对多的通知。
多播调用方式为 BroadCast()
多播的删除方式为 Remove(),RemoveAll()
多播绑定方式为 Add…(如 AddUObject…);
注意
1.只要动态多播才可以被蓝图绑定,通过宏定义BlueprintAsignable
2.看是否有返回值,通过关键字“RetVal”,只有单播委托和动态单播委托,才有返回值。
3.多播委托和单播委托都支持多参数传入,最多9个参数
4.委托的开头必须用F,否则无法编译
5.动态委托可以序列化,通过名称查找函数。
动态委托案例
下面是我用来学习动态委托的一个小案例,让自己更清楚的看见委托的使用与过程
新建两个C++对象
然后拖入场景,一下是两个Actor的代码
MyActorA
.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActorA.generated.h"
/*
* 让A进行对B的调用,我们就需要在A中声明委托类型
* 我们这里测试动态多播参数
* 必须使用F开头的
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDongTaiDuoBo,bool,OkorNotOk);
UCLASS()
class HOTREPLACE_API AMyActorA : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyActorA();
UPROPERTY(BlueprintAssignable)
FDongTaiDuoBo DongTaiDuoBo;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
//用来执行触发委托
UFUNCTION(BlueprintCallable)
void execute(bool ifSuccess);
};
.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyActorA.h"
// Sets default values
AMyActorA::AMyActorA()
{
// 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;
}
// Called when the game starts or when spawned
void AMyActorA::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMyActorA::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMyActorA::execute(bool ifSuccess)
{
//这里用来触发委托
DongTaiDuoBo.Broadcast(ifSuccess);
}
MyActorB
.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActorB.generated.h"
UCLASS()
class HOTREPLACE_API AMyActorB : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyActorB();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
/*
* 这里来调用A里面的委托
* 现在写一个被委托的函数
*/
UFUNCTION(BlueprintCallable)
void Bexecute(bool ifSuccess);
};
.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyActorB.h"
#include "MyActorA.h"
#include "Kismet\GameplayStatics.h"
// Sets default values
AMyActorB::AMyActorB()
{
// 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;
}
// Called when the game starts or when spawned
void AMyActorB::BeginPlay()
{
Super::BeginPlay();
/*
* 在场景中寻找A
*/
TArray<AActor*>Actors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(),AMyActorA::StaticClass(),Actors);
//在场景中遍历到我我们需要的ActorB,然后把函数绑定到ActorB上面
for (auto Actor : Actors)
{
AMyActorA* A=Cast<AMyActorA>(Actor);
if (A!=nullptr)
{
//把找到的AActor转换成A需要的类
A->DongTaiDuoBo.AddDynamic(this,&AMyActorB::Bexecute);
}
}
}
// Called every frame
void AMyActorB::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMyActorB::Bexecute(bool ifSuccess)
{
if (ifSuccess)
{
UE_LOG(LogTemp,Warning,TEXT("Success"));
}
else
{
UE_LOG(LogTemp, Warning, TEXT("NotSuccessnnnnnn"));
}
}
在关卡蓝图中,进行连连看
MyActorA
测试结果
之后你就会发现我们的动态委托测试成功了。