委托有点类似函数指针,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 propertiesATestActor2();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;public:  // Called every framevirtual 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(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 propertiesATestActor();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;public:   // Called every framevirtual 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(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(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(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();
}

UE4的委托(Delegate)使用相关推荐

  1. (转)C#中的委托(Delegate)和事件(Event)

    转自:http://blog.chinaunix.net/uid-576762-id-2733751.html 把C#中的委托(Delegate)和事件(Event)放到现在讲是有目的的:给下次写的设 ...

  2. C#的委托(delegate、Action、Func、predicate)

    委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递.事件是一种特殊的委托. 1.委托的声明 delegate我们常用到的一种声明 delegate至少0个参数,至多32个参 ...

  3. UE4 委托(代理)简单理解

    资料来源于: UE4官方文档 UE4 C++ -- 代理 UE4中的代理(Delegate)使用总结 (UE4 4.20)UE4的委托(Delegate)使用 什么是委托Delegates (网上大部 ...

  4. C# Delegate(委托)与多线程

    C# Delegate(委托)与多线程 很多时候写windows程序都需要结合多线程,在.net中用如下得代码来创建并启动一个新的线程.     public void ThreadProc();   ...

  5. Java 利用反射实现C#的委托

    一, 观察者模式的缺点 在之前的博文的介绍过观察者模式了. 观察者模式可以让多个观察者同时观察1个被观察者. 也就说被观察者可以1次过执行所有观察者的update()方法. 再通俗d来讲, 就是多个观 ...

  6. .NET Framework源码研究系列之---Delegate

    前言 曾几何时能看到微软产品的源码简直是天方夜谭,不过现在这却成了现实,微软终于对外开放了它的产品的源代码.抛去开源运动与微软之间的世代情仇,抛去微软这一做法的初衷,这总归是件好事,能够让我们拨开云雾 ...

  7. 一文说通Dotnet的委托

    简单的概念,也需要经常看看.   一.前言 先简单说说Delegate的由来.最早在C/C++中,有一个概念叫函数指针.其实就是一个内存指针,指向一个函数.调用函数时,只要调用函数指针就可以了,至于函 ...

  8. Java与.net的区别delegate和event

    There is no delegate concept in Java The right-side C# program may be mimiced with reflection techno ...

  9. c#中的委托、事件、Func、Predicate、Observer设计模式以及其他

    原文地址:[学习笔记]c#中的委托.事件.Func.Predicate.Observer设计模式以及其他 参考资料: 1. 简单谈谈事件与委托 2. C#中的委托和事件(上) 3. C#中的委托和事件 ...

最新文章

  1. 什么是最佳适应算法?
  2. redis简单学习3-redis常用命令总结
  3. 字节一面 —— List 和 Map、Set 的区别
  4. oracle 02085,OracleDBLink创建和维护以及ORA-02085解决办法
  5. 用eclipice抓取JS代码
  6. Unity2018新功能抢鲜 | ShaderGraph实战之全息效果
  7. Ubuntu 16 apt-get软件包管理错误问题解决记录
  8. 网络雇佣军 Void Balaur,有组织有纪律,且从不休长假
  9. 有没有更好的写v =(v == 0?1:0)的方法; [关闭]
  10. Spark 机器学习拾遗
  11. 研究生马上要毕业了,可是完全写不出论文,该退学吗?
  12. LOJ2321「清华集训 2017」无限之环
  13. 自动售货机支付服务器开发,自动售货机是如何实现移动支付的?
  14. 用游戏编辑器制作MOD脱颖而出
  15. 用excel和python做数据分析的优缺点
  16. 超大附件上传、下载特别慢,怎么破?
  17. 易基因 | 表观技术:染色质结构构象与DNA互作:ChIP-seq、ATAC-seq
  18. 职高计算机应用基础学的什么,职高计算机应用基础教法初探
  19. 深度神经网络的成功应用,深度神经网络技术赋能
  20. Qt添加.qrc文件和设置exe图标和控件图标

热门文章

  1. 爱旅行web多模块项目搭建
  2. Netty的深入浅出--10.接上一篇netty与protobuf整合的问题
  3. 老猿学5G扫盲贴:中国移动5G融合计费漫游计费架构和路由方案
  4. JS学习笔记——高级编程中compose函数的介绍和基本实现
  5. 第14节 实例-事件响应之组合键
  6. LTE-A 载波聚合(Carrier Aggregation)介绍
  7. c语言病毒源代码 小型,来来来,教你一个用C语言写个小病毒
  8. 如何直接获取绝大部分网站的顶部title栏图标、logo(非js获取)
  9. 运行无间:阿里巴巴运维保障体系的一种最佳实践
  10. QT 常用布局管理器