UE4 委托
委托是一种可在C++
对象上调用成员函数,并可以动态绑定到任意对象的成员函数,之后在该对象上调用函数。可以降低类之间的耦合度,在UE4
中共支持三种类型的委托。
- 单播委托
- 多播委托
- 动态委托
1. 声明委托
- 单播
声明方式 | 描述 |
---|---|
DECLARE_DELEGATE(DelegateName) | 绑定void Function() 类型的函数 |
DECLARE_DELEGATE_OneParam(DelegateName,Param1Type) | 绑定void Function(Param1) 类型的函数 |
DECLARE_DELEGATE_TowParams(DelegateName,Param1Type,Param2Type) | 绑定void Function(Param1,Param2) 类型的函数 |
DECLARE_DELEGATE_RetVal_TowParams(RetValType,DelegateName,Param1Type,Param2Type) | 绑定RetType Function(Param1,Param2) 类型的函数 |
//实例代码
DECLARE_DELEGATE(FDelegateName1NoParam);
DECLARE_DELEGATE_TwoParams(FDelegateName2,float,const FString&);
DECLARE_DELEGATE_RetVal_TwoParams(int32,FDelegateName3,int,const FString&);
-
多播
多播相较于单播,宏的变化为:
DECLARE_MULTICAST_DELEGATE....
同时需要注意多播是没有返回值的。
声明方式 | 描述 |
---|---|
DECLARE_MULTICAST_DELEGATE(DelegateName) | 绑定void Function() 类型的函数 |
DECLARE_MULTICAST_DELEGATE_TwoParams | 绑定void Function(Param1,Param2) 类型的函数 |
// 实例代码
// 多播代理
// 多播代理上绑定的函数的执行顺序是不固定的
// 多播代理无返回值
DECLARE_MULTICAST_DELEGATE(FMultiDelegateNoParam);
DECLARE_MULTICAST_DELEGATE_TwoParams(FMultiDelegate2,int32,const FString&)
- 动态单/多播
动态单/多播委托是一个可以进行序列化利用反射暴露给蓝图的代理,需要注意的是,如果需要传入参数,在代理声明的时候,类型后面需要定义变量名(因为需要暴露给蓝图,变量是需要显示调用的)。
声明方式 | 描述 |
---|---|
DECLARE_DYNAMIC_DELEGATE(DelegateName) | 绑定void Function() 类型的函数 |
DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(RetVal,DelegateName,ParamType1,Param1Name) | 绑定RetValFunction(Param1) 类型的函数 |
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(DelegateName,ParamType1,Param1Name,ParamType2,Param2Name) | 绑定void Function(Param1,Param2) 类型的函数 |
// 动态代理 可以暴露给blueprint使用
// 动态单播
DECLARE_DYNAMIC_DELEGATE(FDynamicDelegate1);
// 如果有参数 类型后需要定义变量名 因为可以暴露在BluePrints中 所以需要名字
DECLARE_DYNAMIC_DELEGATE_RetVal_TwoParams(int32,FDynamicDelegate2,int32,intVal,const FString&,str);
// 动态多播 注意同样没有返回值
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDynamicDelegate3,int32,val,const FString&,str);
2. 绑定委托
- 单播
API | 描述 |
---|---|
BindLambda(FunctorType&& Intype) | 绑定一个Lambda 表达式 |
BindRaw(UserClass* user,MembFunctor* Infunctor) | 绑定一个原生C++ 成员方法 |
BindSp(TSharedPtr<Userclass,ESPMode::fast>&UserPtrRef,MembFunctor* Infunctor) | 绑定一个共享指针 |
BindSatic(StaticFunctionPtr* Infunctor) | 绑定一个静态函数 |
BindSp(TSharedPtr<Userclass,ESPMode::ThreadSafe>&UserPtrRef,MembFunctor* Infunctor) | 绑定一个线程安全的共享指针 |
BindUFunction(UserClass*user,const FName& InFunctionName) | 绑定一个被UFUNCTION() 宏修饰的函数 |
BindUObject(UserClass*user,MembFunctor* Infunctor) | 绑定一个继承自UObejct类的成员方法 |
示例代码:
void ADelegateActor::SingleDelegateTest()
{
DelegateInstance1.BindUObject(this,&ADelegateActor::DelegateFunction);
DelegateInstance1.BindLambda([this](int32 val,const FString& str)->int32
{
return 0;
});
// 绑定原生C++类
FTestObject objectInstance;
DelegateInstance1.BindRaw(&objectInstance,&FTestObject::delegateFunc);
// 绑定共享指针,一般创建纯C++ 用TSharedPtr用于管理线程
TSharedPtr<FTestObject> sharedPtr1 = MakeShareable(new FTestObject);
DelegateInstance1.BindSP(sharedPtr1.ToSharedRef(),&FTestObject::delegateFunc);
// 绑定静态函数
DelegateInstance1.BindStatic(&FTestObject::delegateFunc2);
// 绑定一个安全线程的共享指针
TSharedPtr<FTestObject,ESPMode::ThreadSafe> sharedPtr2 = MakeShareable(new FTestObject);
DelegateInstance1.BindThreadSafeSP(sharedPtr2.ToSharedRef(),&FTestObject::delegateFunc);
// 绑定一个方式,通过Name查找,用到了反射。同时这个函数需要加上UFUNCTION()宏
// UFUNCTION()
// int32 delegateFunc3(int32 val,const FString& str){return 0;}
DelegateInstance1.BindUFunction(this,FName("delegateFunc3"));
}
- 多播
多播绑定函数和单播不同的地方为,它可以绑定多个函数对象,用的函数是Add+BindTargetName
,需要注意的是多播没有返回值。
示例代码:
MultiDelegateInstance1.AddUObject(this,&ADelegateActor::MultiDelegateFunc);
MultiDelegateInstance1.AddLambda([](int val,const FString& str){});
.........
- 动态单播/多播
示例代码:
UFUNCTION()
void MultiDelegateFunc(int32 val,const FString& str){}
UFUNCTION()
int32 delegateFunc3(int32 val,const FString& str){return 0;}
// 动态单播
DynamicSigledelegateRef.BindDynamic(this,&ADelegateActor::delegateFunc3);
// 动态多播
// 注意动态多播绑定的方法一定要有UFUNCTION宏,因为需要用到反射的功能
dynamultiDelegateRef.AddDynamic(this,&ADelegateActor::MultiDelegateFunc);
3. 调用委托
- 单播
通过Execute
和ExecuteIfBound
进行调用,区别是ExecuteIfBound
调用时会判断有没有进行了绑定再进行调用,但是返回值是bool
。所以用ExecuteIfBound
调用绑定的函数对象必须没有返回值。
示例代码:
if(DelegateInstance1.IsBound())
{
DelegateInstance1.Execute(20,TEXT("Unreal"));
}
//DelegateInstance1.ExecuteIfBound(); 返回值为bool
//解绑
DelegateInstance1.Unbind();
-
多播
多播通过
BoardCast()
可以通知调用所有绑定的函数对象。
示例代码:
MultiDelegateInstance1.Broadcast(10,"Unreal");
-
动态单播/多播
- 在
C++
中进行调用
// 动态单播 DynamicSigledelegateRef.BindDynamic(this,&ADelegateActor::delegateFunc3); DynamicSigledelegateRef.Execute(10,"Unreal"); // 动态多播 // 注意动态多播绑定的方法一定要有UFUNCTION宏,因为需要用到反射的功能 dynamultiDelegateRef.AddDynamic(this,&ADelegateActor::MultiDelegateFunc); dynamultiDelegateRef.Broadcast(10,"Unreal");
-
在蓝图中进行调用
单播在蓝图中调用需要利用
UFUNCTION()
宏进行修饰,并将自己作为形参传入函数。
UFUNCTION(BlueprintCallable) void DynamicSingleDelegateTest(FDynamicDelegate2 delegateRef);
在蓝图中调用:
- 在
多播在蓝图中调用可以直接使用UPROPERTY()
宏中的BlueprintAssignable
的属性修饰符修饰,注意:该修饰符只能用于动态多播委托
UPROPERTY(BlueprintAssignable)
FDynamicDelegate3 dynamicMultiDelegate;
在蓝图中调用:
4. C++实现UE4委托
借用侯捷老先生的话,知道一个东西,却不明白它的原理,不高明!
,所以为了能更好的理解UE4委托,于是模仿UE4创建代理绑定以及调用的方式,自己用C++写了一套很简单的委托。
- 实现单播无返回值无参数委托
class FDelegateDefault{
public:
FDelegateDefault():functor(nullptr){};
template<class ObjectType>
void BindRaw(ObjectType* InObejct,void(ObjectType::*InFunc)(void)){
functor = std::bind(InFunc,InObejct);
}
void BindLambda(function<void(void)> Infunctor){
functor = std::move(Infunctor);
}
void Execute(){
functor();
}
bool ExecuteIfBound(){
if(IsBound()){
Execute();
return true;
}
return false;
}
bool IsBound(){
return functor?true:false;
}
private:
function<void(void)>functor;
};
class testObject{
public:
void testFunc(){
cout<<"test default delegate"<<endl;
}
};
//测试代码
#define DECLARE_DELEGATE(FDelegateName) FDelegateDefault FDelegateName;
int main(){
testObject obj;
DECLARE_DELEGATE(Delegate);
Delegate.BindRaw(&obj,&testObject::testFunc);
Delegate.BindLambda([](){
cout<<"default bind Lambda Test"<<endl;
});
Delegate.ExecuteIfBound();
return 0;
}
- 实现单播多参有返回值的委托
template<class retType,class ...paramTypes>
class FDelegateBase{
public:
virtual retType Execute(paramTypes...params)=0;
};
template<class retType,class objectType,class ...paramTypes>
class FDelegateObject:public FDelegateBase<retType,paramTypes...>{
public:
FDelegateObject():Functor(nullptr),Object(nullptr){};
FDelegateObject(objectType* InObejct,retType(objectType::*InFunctor)(paramTypes...)):Object(InObejct),Functor(InFunctor){};
virtual retType Execute(paramTypes...params)override{
if(Functor&&Object){
return (Object->*Functor)(params...);
}
}
private:
objectType* Object;
retType(objectType::*Functor)(paramTypes...);
};
template<class retType,class ...paramTypes>
class FDelegateLambda:public FDelegateBase<retType,paramTypes...>{
public:
FDelegateLambda():functor(nullptr){};
FDelegateLambda(function<retType(paramTypes...)> InFunctor){
functor = std::move(InFunctor);
}
virtual retType Execute(paramTypes...params)override{
if(functor){
return functor(params...);
}
}
private:
function<retType(paramTypes...)>functor;
};
template<class retType,class ...paramTypes>
class FDelegateParams{
public:
FDelegateParams():delegatePtr(nullptr){}
~FDelegateParams(){
if(delegatePtr){
delete delegatePtr;
delegatePtr = nullptr;
}
}
template<class objectType>
void BindRaw(objectType* InObejct,retType(objectType::*InFunc)(paramTypes...)){
prepareforBind();
delegatePtr = new FDelegateObject<retType,objectType,paramTypes...>(InObejct,InFunc);
}
void BindLambda(function<retType(paramTypes...)> InFunc){
prepareforBind();
delegatePtr = new FDelegateLambda<retType,paramTypes...>(InFunc);
}
retType Execute(paramTypes...params){
if(delegatePtr){
return delegatePtr->Execute(params...);
}
}
bool ExecuteIfBound(paramTypes...params){
if(delegatePtr){
Execute(params...);
}
}
bool IsBound(){
return delegatePtr?true:false;
}
protected:
void prepareforBind(){
if(delegatePtr){
delete delegatePtr;
delegatePtr = nullptr;
}
}
private:
FDelegateBase<retType,paramTypes...>* delegatePtr;
};
class testObject{
public:
const char* testFunc2(int val,string str){
cout<<"val:"<<val<<endl;
cout<<"str:"<<str<<endl;
const char* tempstr = str.c_str();
return tempstr;
}
};
//测试代码
#define DECLARE_DELEGATE_RetVal_Params(FDelegateName,retType,...) FDelegateParams<retType,__VA_ARGS__>FDelegateName;
int main(){
testObject obj;
DECLARE_DELEGATE_RetVal_Params(Delegate,const char*,int,string);
Delegate.BindRaw(&obj,&testObject::testFunc2);
Delegate.Execute(10,"Unreal");
Delegate.BindLambda([](int val,string str)->const char*{
cout<<"Bind Lambda"<<endl;
cout<<"Val:"<<val<<endl;
cout<<"str:"<<str<<endl;
return "Unreal";
});
Delegate.ExecuteIfBound(10,"Unity");
return 0;
}
多播的实现,大同小异。有兴趣的可以看我之前C++委托中实现多播委托的方式。这里不予讨论。C++委托_WLSTLA-优快云博客