官方文档介绍
- 委托本身是一种特殊对象
- 委托可绑定任意对象的函数
- 委托是一种观察者模式,当委托执行时,通知所有绑定到这个委托的对象
定义委托类的宏可以在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 )
从上往下一次是:
- 单播委托:只能绑定一个函数
- 多播委托:可绑定多个函数
- 事件:特殊的多播委托,只能在指定类中触发
- 动态单播委托:只能绑定一个函数,可暴露给蓝图使用
- 动态多播委托:能绑定多个函数,可暴露给蓝图使用
- 单播委托_带返回值:以上只能绑定void函数,这个可绑定带返回值的函数,并可使用返回值
- 动态单播委托_带返回值:同上,可暴露给蓝图使用
可以看到关键词的意义:
单播:只能绑定一个函数
多播:可绑定多个函数
动态:可给蓝图使用
带返回值:可绑定带返回值的函数,并可获取返回值(所以也只支持单播)
事件:类限定的多播
翻看Actor.h会发现其使用的是稀疏动态多播代理,如下。
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_OneParam(FActorDestroyedSignature, AActor, OnDestroyed, AActor*, DestroyedActor ); DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_TwoParams(FActorEndPlaySignature, AActor, OnEndPlay, AActor*, Actor , EEndPlayReason::Type, EndPlayReason);
源码中的解释:
稀疏的委托可以用于不经常绑定的委托,以便对象仅使用1个字节的存储空间,而不用承担委托调用列表的全部开销。 从委托中调用,添加,删除等的成本高于直接使用委托的成本,因此应在节省内存的好处与期望绑定委托的频率之间进行权衡。
委托的使用委托的使用可分为4个步骤:
- 声明委托(委托签名):使用宏声明和定义一个委托类,表明委托可接受的函数签名
- 创建委托:在需要执行委托的类中声明一个委托成员变量
- 执行委托:执行委托,会触发绑定的函数
- 绑定委托:将委托执行时触发的对象成员函数绑定到这个委托上
以上是按照开发时写代码的顺序,实际运行时是绑定在前、执行在后。
示例:
// 1.声明和定义一个委托类
DECLARE_DELEGATE(FMyDelegate);
class FMyObject
{
public:
void MyFunction();
// 2.创建一个委托成员变量
FMyDelegate MyDeleage;
};
void FMyObject::MyFunction()
{
// 3.执行委托
MyDelegate.ExecuteIfBound();
}
// 4. 在其它的地方,需要将函数绑定到委托上
class FObserver
{
public:
void Begin();
void Test();
FMyObject MyObject;
};
void FObserver::Begin()
{
// 4.找个地方绑定委托
// 当MyObject的MyDeleage执行的时候,就会触发Test函数
MyObject.MyDeleage.BindRaw(this,&FObserver::Test);
}
上面的例子只是简单的示例,实际使用时可参考UE4引擎中的例子,比如Actor中的碰撞事件、伤害事件、点击事件等。
各种委托使用的快速参考- 通常单播委托执行使用ExecuteIfBound(),多播委托执行使用Broadcast()。
- 通常什么类声明,什么类就负责执行委托。
- 通常单播委托绑定使用Bind**(),多播委托绑定使用Add**()。
- 适应性最广的是动态多播委托。如果只是给C++用则使用多播委托。
- 用于绑定的函数应该与委托签名一致。
- 定义委托类
DECLARE_DELEGATE(FMyDelegate);//不带参数 DECLARE_DELEGATE_OneParam(FMyDelegate1, int32);//带一个int32类型的参数 DECLARE_DELEGATE_TwoParams(FMyDelegate2, int32, float);//带一个int32和float类型的参数
- 创建委托变量
// 假设放在MyObject中 FMyDelegate MyDelegate; FMyDelegate1 MyDelegate1; FMyDelegate2 MyDelegate2;
- 执行
MyDelegate.ExecuteIfBound(); MyDelegate1.ExecuteIfBound(1); MyDelegate2.ExecuteIfBound(1, 0.5f);
- 绑定和解绑
//假设为了触发FMyObject2中的MyFunction函数。
MyObject->MyDelegate.BindRaw(this,&FMyObject2::MyFunction);//this为非UObject对象,最好不用,使用BindSP代替
MyObject->MyDelegate.BindUObject(this,&FMyObject2::MyFunction);//this为UObject对象
MyObject->MyDelegate.BindUFunction(this,FName("MyFunction"));//最好不用,使用BindUObject代替
MyObject->MyDelegate.BindSP(MyObject2Ptr,&FMyObject2::MyFunction);//MyObject2Ptr为共享指针
MyObject->MyDelegate.BindStatic(&FMyObject2::MyFunction);//MyFunction为static函数
MyObject->MyDelegate.BindLambda([](){});//绑定一个Lambda函数
MyObject->MyDelegate.UnBind();//解除绑定
多播委托
- 定义委托类
DECLARE_MULTICAST_DELEGATE(FMyDelegate);//不带参数 DECLARE_MULTICAST_DELEGATE_OneParam(FMyDelegate1, int32);//带一个int32类型的参数 DECLARE_MULTICAST_DELEGATE_TwoParams(FMyDelegate2, int32, float);//带一个int32和float类型的参数
- 创建委托变量
// 假设放在MyObject中 FMyDelegate MyDelegate; FMyDelegate1 MyDelegate1; FMyDelegate2 MyDelegate2;
- 执行
MyDelegate.Broadcast(); MyDelegate1.Broadcast(1); MyDelegate2.Broadcast(1, 0.5f);
- 绑定和解绑
//假设为了触发FMyObject2中的MyFunction函数。
MyObject->MyDelegate.AddRaw(this,&FMyObject2::MyFunction);//this为非UObject对象,最好不用,使用BindSP代替
MyObject->MyDelegate.AddUObject(this,&FMyObject2::MyFunction);//this为UObject对象
MyObject->MyDelegate.AddUFunction(this,FName("MyFunction"));//最好不用,使用BindUObject代替
MyObject->MyDelegate.AddSP(MyObject2Ptr,&FMyObject2::MyFunction);//MyObject2Ptr为共享指针
MyObject->MyDelegate.AddStatic(&FMyObject2::MyFunction);//MyFunction为static函数
MyObject->MyDelegate.AddLambda([](){});//绑定一个Lambda函数
MyObject->MyDelegate.Remove(this,&FMyObject2::MyFunction);//解除绑定
MyObject->MyDelegate.RemoveAll();//解除绑定
动态单播委托
“动态”意味着可以给蓝图使用,限制是但是只能绑定UFUNCTION宏标记的函数。
- 定义委托类
DECLARE_DYNAMIC_DELEGATE(FMyDelegate);//不带参数 DECLARE_DYNAMIC_DELEGATE_OneParam(FMyDelegate1, int32);//带一个int32类型的参数 DECLARE_DYNAMIC_DELEGATE_TwoParams(FMyDelegate2, int32, float);//带一个int32和float类型的参数
- 创建委托变量
// 假设放在MyObject中 FMyDelegate MyDelegate; FMyDelegate1 MyDelegate1; FMyDelegate2 MyDelegate2;
- 执行
MyDelegate.ExecuteIfBound(); MyDelegate1.ExecuteIfBound(1); MyDelegate2.ExecuteIfBound(1, 0.5f);
- 绑定和解绑
//假设为了触发FMyObject2中的MyFunction函数。 MyObject->MyDelegate.BindDynamic(this,&FMyObject2::MyFunction);//this为UObject对象 MyObject->MyDelegate.UnBind();//解除绑定动态多播委托
- 定义委托类
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMyDelegate);//不带参数 DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyDelegate1, int32);//带一个int32类型的参数 DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FMyDelegate2, int32, float);//带一个int32和float类型的参数
- 创建委托变量
// 假设放在MyObject中 FMyDelegate MyDelegate; FMyDelegate1 MyDelegate1; FMyDelegate2 MyDelegate2;
- 执行
MyDelegate.Broadcast(); MyDelegate1.Broadcast(1); MyDelegate2.Broadcast(1, 0.5f);
- 绑定和解绑
//假设为了触发FMyObject2中的MyFunction函数。 MyObject->MyDelegate.AddDynamic(this, &FMyObject2::MyFunction);//最好不用,使用BindUObject代替 MyObject->MyDelegate.Remove(this, &FMyObject2::MyFunction);//解除绑定 MyObject->MyDelegate.RemoveAll();//解除绑定蓝图中委托的创建
- 蓝图中的事件通常表示一个在EventGraph中的一个函数(其实就是一个void函数),比如BeginPlay,Tick等。
- 委托在蓝图中叫EventDispatchers,只能绑定void函数。
对于动态(单播或多播)委托,只需要用UPROPERTY(BlueprintAssignable)标记,即可暴露给蓝图使用。
// 假设放在MyObject中 UPROPERTY(BlueprintAssignable) FMyDelegate MyDelegate;蓝图中创建,使用
蓝图中是创建一个Event Dispatchers,相当于一个动态多播委托。



