UE4委托

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. 调用委托

  • 单播

​ 通过ExecuteExecuteIfBound进行调用,区别是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-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值