淘先锋技术网

首页 1 2 3 4 5 6 7

委托有点类似函数指针,UE4定义了一大堆的委托方便我们的使用

在DelegateCombinations.h文件中的定义了各种委托

#define DECLARE_DELEGATE( DelegateName ) FUNC_DECLARE_DELEGATE( DelegateName, void )
#define DECLARE_MULTICAST_DELEGATE( DelegateName ) FUNC_DECLARE_MULTICAST_DELEGATE( DelegateName, void )
#define DECLARE_EVENT( OwningType, EventName ) FUNC_DECLARE_EVENT( OwningType, EventName, void )
#define DECLARE_DYNAMIC_DELEGATE( DelegateName ) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_DELEGATE) FUNC_DECLARE_DYNAMIC_DELEGATE( FWeakObjectPtr, DelegateName, DelegateName##_DelegateWrapper, , FUNC_CONCAT( *this ), void )
#define DECLARE_DYNAMIC_MULTICAST_DELEGATE( DelegateName ) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_DELEGATE) FUNC_DECLARE_DYNAMIC_MULTICAST_DELEGATE( FWeakObjectPtr, DelegateName, DelegateName##_DelegateWrapper, , FUNC_CONCAT( *this ), void )
#define DECLARE_DELEGATE_RetVal( ReturnValueType, DelegateName ) FUNC_DECLARE_DELEGATE( DelegateName, ReturnValueType )
#define DECLARE_DYNAMIC_DELEGATE_RetVal( ReturnValueType, DelegateName ) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_DELEGATE) FUNC_DECLARE_DYNAMIC_DELEGATE_RETVAL( FWeakObjectPtr, DelegateName, DelegateName##_DelegateWrapper, ReturnValueType, , FUNC_CONCAT( *this ), ReturnValueType )

我在项目中经常使用的有两类委托,单播委托和多播委托

单播委托(DECLARE_DELEGATE)

单播委托,指的是只能绑定一个函数指针的委托,实现一对一的通知。

单播委托的定义是没有“MULTICAST”修饰的委托,如下面这些

DECLARE_DELEGATE
DECLARE_DELEGATE_OneParam
DECLARE_DELEGATE_TwoParams
DECLARE_DELEGATE_ThreeParams

UE4在DelegateCombinations.h文件已经预先帮你声明了从无参数到长达9个参数的委托

怎么使用?

BindUObject

(1)声明委托和定义委托变量

//单播无参数的委托
DECLARE_DELEGATE(FSingleDelagateWithNoParam);


FSingleDelagateWithNoParam SingleDelagateWithNoParam;

//单播一个参数的委托
DECLARE_DELEGATE_OneParam(FSingleDelagateWithOneParam, FString);


FSingleDelagateWithOneParam SingleDelagateWithOneParam;

(2)绑定函数指针 BindUObject。 这里我经常用BindUObject,绑定的函数指针为UObject或者继承UObject的对象的函数指针

如我定义一个Actor:

.h :

UCLASS()
class MYPROJECT_API ATestActor2 : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ATestActor2();
 
protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
 
public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;
 
private:
	void Func1();
	void Func2(FString param);
	
};

.cpp :

void ATestActor2::Func1()
{
	UE_LOG(LogTemp, Error, TEXT("TestActor2 Func1"));
}
 
void ATestActor2::Func2(FString param)
{
	UE_LOG(LogTemp, Error, TEXT("TestActor2 Func2 param = %s"), *param);
}

绑定委托:


void ATestActor2::BeginPlay()
{
	Super::BeginPlay();
	APlayerController* playerController = GetWorld()->GetFirstPlayerController();
	if (nullptr == playerController)
		return;
	ACharacter* character = playerController->GetCharacter();
	if (nullptr == character)
		return;
	AMyProjectCharacter* myProjectCharacter = Cast<AMyProjectCharacter>(character);
	if (nullptr == myProjectCharacter)
		return;
	myProjectCharacter->SingleDelagateWithNoParam.BindUObject(this, &ThisClass::Func1);
	myProjectCharacter->SingleDelagateWithOneParam.BindUObject(this, &ThisClass::Func2);
}

这里得注意的是,单播委托如果已经绑定过函数指针,在没有UnBind的情况下如果再次绑定函数指针,会报错。所以经常使用

(3)执行委托,触发相应的对象的函数

SingleDelagateWithNoParam.ExecuteIfBound();
SingleDelagateWithOneParam.ExecuteIfBound(FString("PerformSingleDelagateWithOneParam"));

这里呢,得注意的一点单播委托执行委托函数有:ExecuteExecuteIfBound

我一般用ExecuteIfBound而不是Execute,因为ExecuteIfBound更安全,指的是在委托绑定有效函数指针的前提下才能执行,而Execute如果在委托绑定无效的函数指针的情况下就执行会报错。

运行结果:
在这里插入图片描述
(4)移除委托Unbind,移除已经绑定的函数指针

(5)IsBound,判断委托是否已经绑定了函数指针

BindStatic

用于绑定于类的静态函数

UCLASS(config=Game)
class AMyProject6Character : public ACharacter
{
 
    static void Test(float a);
 
	DECLARE_DELEGATE_OneParam(FAA, float)
	FAA faa;
 
}
 
faa.BindStatic(&AMyProject6Character::Test);

BindRaw

BindRaw是用于绑定不继承UObject的类或者结构体的对象的方法

CreateUObject

创建委托变量:

DECLARE_DELEGATE_OneParam(FTestDelegate, float);
 
void AMyProject3Character::PrintTestInfo(float Value)
{
	UE_LOG(LogTemp, Error, TEXT("Value = %f"), Value);
}
 
FTestDelegate MyDel = FTestDelegate::CreateUObject(this, &AMyProject3Character::PrintTestInfo);

其他CreateRaw,CreateStatic同理。

多播委托(DECLARE_MULTICAST_DELEGATE)

多播委托,指的是能绑定多个函数指针的委托,实现一对多的通知。

多播委托的定义是有“MULTICAST”修饰的委托,如下面这些:

DECLARE_MULTICAST_DELEGATE
DECLARE_MULTICAST_DELEGATE_OneParam
DECLARE_MULTICAST_DELEGATE_TwoParams
DECLARE_MULTICAST_DELEGATE_ThreeParams

怎么使用?

AddUObject

(1)声明委托和定义委托变量


//多播无参数委托
DECLARE_MULTICAST_DELEGATE(FMuitiDelagateWithNoParam);
 
FMuitiDelagateWithNoParam MuitiDelagateWithNoParam;
//多播一个参数的委托
DECLARE_MULTICAST_DELEGATE_OneParam(FMuitiDelagateWithOneParam, FString);
 
FMuitiDelagateWithOneParam MuitiDelagateWithOneParam;

(2)绑定函数指针 AddUObject。 这里我经常用BindUObject,这个函数绑定的函数指针为UObject或者继承UObject的类对象顶点函数指针.这里可以添加多个函数指针。

.h

UCLASS()
class MYPROJECT_API ATestActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ATestActor();
 
protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
 
public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;
 
private:
	void Func1();
	void Func2(FString param);

.cpp


void ATestActor::Func1()
{
	UE_LOG(LogTemp, Error, TEXT("TestActor Func1"));
}
 
void ATestActor::Func2(FString param)
{
	UE_LOG(LogTemp, Error, TEXT("TestActor Func2 param = %s"), *param);
}

ATestActor::BeginPlay():

void ATestActor::BeginPlay()
{
	Super::BeginPlay();
	APlayerController* playerController = GetWorld()->GetFirstPlayerController();
	if (nullptr == playerController)
		return;
	ACharacter* character = playerController->GetCharacter();
	if (nullptr == character)
		return;
	AMyProjectCharacter* myProjectCharacter = Cast<AMyProjectCharacter>(character);
	if (nullptr == myProjectCharacter)
		return;
 
	myProjectCharacter->MuitiDelagateWithNoParam.AddUObject(this, &ThisClass::Func1);
	myProjectCharacter->MuitiDelagateWithOneParam.AddUObject(this, &ThisClass::Func2);
}

ATestActor2::BeginPlay():

void ATestActor2::BeginPlay()
{
	Super::BeginPlay();
	APlayerController* playerController = GetWorld()->GetFirstPlayerController();
	if (nullptr == playerController)
		return;
	ACharacter* character = playerController->GetCharacter();
	if (nullptr == character)
		return;
	AMyProjectCharacter* myProjectCharacter = Cast<AMyProjectCharacter>(character);
	if (nullptr == myProjectCharacter)
		return;
	myProjectCharacter->MuitiDelagateWithNoParam.AddUObject(this, &ThisClass::Func1);
	myProjectCharacter->MuitiDelagateWithOneParam.AddUObject(this, &ThisClass::Func2);
}

同时绑定了ATestActor和ATestActor2的函数指针

(3)执行委托,触发函数

	MuitiDelagateWithNoParam.Broadcast();
	MuitiDelagateWithOneParam.Broadcast(FString("PerformMultiDelagateWithOneParam"));

在这里插入图片描述
(4)移除函数指针Remove和RemoveAll

Remove的参数为委托AdddUObject返回的句柄FDelegateHandle

AddStatic

用法跟上面的BindStatic对应

动态委托(DECLARE_DYNAMIC_DELEGATE)

动态委托在UE4内置的各种类经常可见,如Actor的鼠标点击(OnBeginCursorOver),开始进入Actor碰撞范围(OnActorBeginOverlap)等等

在这里插入图片描述
目前我知道关于动态委托主要是可用于蓝图的委托绑定,如下面所示:

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FTestDynamicDelagate, float, Value);
 
UPROPERTY(BlueprintAssignable, Category = "Test")
	FTestDynamicDelagate OnTestDynamicDelagate;

可以看出得加上 UPROPERTY(BlueprintAssignable, Category = “Test”),这里如果不是动态委托的话会报错

在这里插入图片描述
一般来说我们在C++代码使用动态委托绑定一个对象的方法的时候比较喜欢用__Internal_BindDynamic和__Internal_AddDynamic,当然UE4为了调用方便封装了对应的宏:

#define BindDynamic( UserObject, FuncName ) __Internal_BindDynamic( UserObject, FuncName, STATIC_FUNCTION_FNAME( TEXT( #FuncName ) ) )
 
 
#define AddDynamic( UserObject, FuncName ) __Internal_AddDynamic( UserObject, FuncName, STATIC_FUNCTION_FNAME( TEXT( #FuncName ) ) )

这里比较注意的是用AddDynamic绑定的方法得被“UFUNCTION”标记,否则绑定无效

UFUNCTION()
	void OnTestBegin(AActor* OverlappedActor, AActor* OtherActor);
 
 
void AMyActor::OnTestBegin(AActor* OverlappedActor, AActor* OtherActor)
{
	if (OverlappedActor)
	{
		UE_LOG(LogTemp, Warning, TEXT("OverlappedActor is %s"), *OverlappedActor->GetName());
	}
 
	if (OtherActor)
	{
		UE_LOG(LogTemp, Warning, TEXT("OtherActor is %s"), *OtherActor->GetName());
	}
}
 
 
void AMyActor::BeginPlay()
{
	Super::BeginPlay();
	OnActorBeginOverlap.AddDynamic(this, &AMyActor::OnTestBegin);
}

可返回值委托(DECLARE_DELEGATE_RetVal)

可返回值委托和之前“DECLARE_DELEGATE” “DECLARE_MULTICAST_DELEGATE”差不多,主要的区别是DECLARE_DELEGATE_RetVal这些委托在执行的时候可以返回一个值,如下所示:

DECLARE_DELEGATE_RetVal(float, FTestRetValDelegate);
 
FTestRetValDelegate TestRetValDelegate;
 
float Test();
 
float AMyActor::Test()
{
	return 1.0f;
}
 
float Value = TestRetValDelegate.Execute();

委托的额外传参数

有时候,我们在委托绑定函数指针的时候,就随便传入一个变量值,UE4的委托也是可以办到的。如下面所示:

//单播无参数的委托
DECLARE_MULTICAST_DELEGATE(FMuitiDelagateWithNoParam);
 
FMuitiDelagateWithNoParam MuitiDelagateWithNoParam;
void ATestActor::Func2(FString param)
{
	UE_LOG(LogTemp, Error, TEXT("TestActor Func2 param = %s"), *param);
}
 
void ATestActor::BeginPlay()
{
	Super::BeginPlay();
	APlayerController* playerController = GetWorld()->GetFirstPlayerController();
	if (nullptr == playerController)
		return;
	ACharacter* character = playerController->GetCharacter();
	if (nullptr == character)
		return;
	AMyProjectCharacter* myProjectCharacter = Cast<AMyProjectCharacter>(character);
	if (nullptr == myProjectCharacter)
		return;
 
	myProjectCharacter->MuitiDelagateWithNoParam.AddUObject
	(this, &ThisClass::Func2, FString("MuitiDelagateWithNoParam"));
 
}

委托的使用理念

就项目经验而言,我感觉委托就是软件经典模式中的“观察者模式(Observer)”的具体运用,可以很好的松耦合。

比如:玩家死亡,导致UMGWidget的text数值改变,导致敌人获取经验,导致。。。。。。

按正常实现:

class UMGText
{
    void BeginPlay();
    void Change();
}
 
class Fighter
{
  void BeginPlay();
  void GetExprience();
}
 
 
Class Person
{
 
void BeginPlay();
void Dead();
}
 
void Person::Dead()
{
 
    umgText->Change();
    fighter->GetExperience();
}
 

毫无疑问,随着角色死亡影响的事情越来越多,以后我们的Person对象里会保存或者需要获取无数诸如umgText或者fighter的乱七八糟的对象,耦合度很高,

但是有了委托,你可以在Person中声明委托,然后UMGText类和Figther类中的开始函数进行获取Person的委托,然后AddUObject“Change和GetExperience函数”。然后玩家死亡的时候调用委托就行了,实现了很棒的松耦合。

class UMGText
{
    void BeginPlay();
    void Change();
}
 
void UMGText::BeginPlay()
{
    Person person = GetPerson();
    person->PeronDead.AddUObject(this, &ThisClass::Change);
}
 
class Fighter
{
  void BeginPlay();
  void GetExprience();
}
 
void Fighter::BeginPlay()
{
    Person person = GetPerson();
    person->PeronDead.AddUObject(this, &ThisClass::GetExprience);
}
 
 
class Person
{
 
DECLARE_MULTICAST_DELEGATE(FPeronDead);
FPeronDead PeronDead;
 
void Dead();
}
 
void Person::Dead()
{
    PeronDead.Broadcast(); 
}