【UE4 C++ 基礎知識】<8> Delegate 委托



概念

定義

  • UE4中的delegate(委托)常用於解耦不同對象之間的關聯:委托的觸發者不與監聽者有直接關聯,兩者通過委托對象間接地建立聯系。
    監聽者通過將響應函數綁定到委托上,使得委托觸發時立即收到通知,並進行相關邏輯處理。
  • 委托,又稱代理,本質是一個特殊類的對象,它內部可以儲存(一個或多個)函數指針、調用參數和返回值。

藍圖示例

image

聲明委托

  • 委托簽名聲明可存在於全局范圍內、命名空間內、甚至類聲明內。此類聲明可能不在於函數體內
  • 可以是返回一個值的函數。
  • 最多4個"載荷"變量。
  • 最多8個函數參數。
//定義一個無參普通單播委托
DECLARE_DELEGATE( DelegateName )

//定義一個無參普通單播委托, 帶返回參數
DECLARE_DELEGATE_RetVal( ReturnValueType, DelegateName )

//定義一個無參動態單播委托, 帶返回參數
DECLARE_DYNAMIC_DELEGATE_RetVal

//定義一個無參普通多播委托
DECLARE_MULTICAST_DELEGATE( DelegateName )

//定義一個無參事件(特殊的多播委托)
DECLARE_EVENT( OwningType, EventName )

//定義一個無參動態單播委托
DECLARE_DYNAMIC_DELEGATE( DelegateName)

//定義一個無參動態多播委托
DECLARE_DYNAMIC_MULTICAST_DELEGATE( DelegateName )

單播委托

  • 綁定單個可調用對象
  • 支持返回值.
  • 支持參數
  • 不支持反射以及序列化

聲明宏

//不支持返回,支持參數
DECLARE_DELEGATE_*
DECLARE_DELEGATE(DelegateName)
DECLARE_DELEGATE_OneParam(DelegateName, Param1Type)
DECLARE_DELEGATE_<Num>Params(DelegateName, Param1Type, Param2Type, ...)

//支持返回RetValType,支持參數
DECLARE_DELEGATE_RetVal_*
DECLARE_DELEGATE_RetVal(RetValType, DelegateName) 
DECLARE_DELEGATE_RetVal_OneParam(RetValType, DelegateName, Param1Type) 
DECLARE_DELEGATE_RetVal_<Num>Params(RetValType, DelegateName, Param1Type, Param2Type, ...)

綁定委托

  • Bind 綁定到現有委托對象。

  • BindStatic 綁定原始C++指針全局函數委托。

  • BindRaw 綁定原始C++指針委托。由於原始指針不使用任何類型的引用,因此在刪除目標對象后調用Execute 或 ExecuteIfBound 會不安全。

  • BindLambda

  • BindSP 綁定基於指針的共享成員函數委托。共享指針委托會保留對對象的弱引用。可使用 ExecuteIfBound() 進行調用。

    TSharedRef<FLogWriter> LogWriter(new FLogWriter());
    WriteToLogDelegate.BindSP(LogWriter, &FLogWriter::WriteToLog);
    
  • BindUObject 綁定 UObject 的成員函數委托。UObject 委托會保留對你的對象 UObject 的弱引用。可使用 ExecuteIfBound() 進行調用。

  • BindUFunction綁定由UFUNCTION標記的函數

  • UnBind 取消綁定此委托。

執行函數

  • Execute 不檢查其綁定情況即執行一個委托

  • ExecuteIfBound 檢查一個委托是否已綁定,如是,則調用Execute

  • IsBound 檢查一個委托是否已綁定,經常出現在包含 Execute 調用的代碼前

用法示例

不帶參數Delegate

//InventoryGameMode.h類外聲明
DECLARE_DELEGATE(FStandardDelegateSignature)

//InventoryGameMode.h類成員聲明變量
FStandardDelegateSignature MyStandardDelegate;

//DelegateListener.cpp綁定委托
MyInventoryGM->MyStandardDelegate.BindUObject(this, &ADelegateListener::EnableLight);
//DelegateListener.cpp解綁委托
MyInventoryGM->MyStandardDelegate.Unbind();

//MyTriggerVolume.cpp調用委托,間接調用函數
MyInventoryGM->MyStandardDelegate.ExecuteIfBound();

帶參數Delegate

//InventoryGameMode.h類外聲明
DECLARE_DELEGATE_OneParam(FParamDelegateSignature,FLinearColor)

//InventoryGameMode.h類成員聲明變量
FParamDelegateSignature MyParamDelegate;

//ParamDelegateListener.cpp 綁定委托
MyInventoryGM->MyParamDelegate.BindUObject(this, &AParamDelegateListener::SetLightColor);
//ParamDelegateListener.cpp 解綁委托
MyInventoryGM->MyParamDelegate.Unbind();

//MyTriggerVolume.cpp調用委托,間接調用函數
auto Color = FLinearColor(1, 0, 0, 1);
MyInventoryGM->MyParamDelegate.ExecuteIfBound(Color);

傳遞有效負載數據

image


多播委托

  • 可以綁定多個回調函數,當其觸發時,所有綁定的回調函數都會執行, 實質是維持了一個單播委托的數組

  • 沒有返回值.

  • 支持參數

  • 不支持反射以及序列化

聲明宏

DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature)
DECLARE_MULTICAST_DELEGATE_OneParam(FMulticastDelegateSignature, FString)

綁定多播委托

  • Add 將函數委托添加到該多播委托的調用列表中。

  • AddStatic 添加原始C++指針全局函數委托。

  • AddRaw 添加原始C++指針委托。原始指針不使用任何類型的引用,因此如果從委托下面刪除了對象,則調用此函數可能不安全。調用Execute()時請小心!

  • AddSP 添加基於共享指針的(快速、非線程安全)成員函數委托。共享指針委托保留對對象的弱引用。

  • AddUObject 添加基於UObject的成員函數委托。UObject委托保留對對象的弱引用。

  • Remove從該多播委托的調用列表中刪除函數(性能為O(N))。請注意,委托的順序可能不會被保留!

  • RemoveAll 從該多播委托的調用列表中刪除綁定到指定UserObject的所有函數。請注意,委托的順序可能不會被保留!

多播執行

  • Broadcast 將該委托廣播給所有綁定的對象,但可能已過期的對象除外。

用法示例

//InventoryGameMode.h類外聲明
DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature)

//InventoryGameMode.h類成員聲明變量
FMulticastDelegateSignature MyMulticastDelegate;

//MulticastDelegateListener.h聲明
FDelegateHandle MyDelegateHandle;
//MulticastDelegateListener.cpp 綁定委托
MyDelegateHandle = MyInventoryGM->MyMulticastDelegate.AddUObject(this, &AMulticastDelegateListener::ToggleLight);
//MulticastDelegateListener.cpp 解綁委托
MyInventoryGM->MyMulticastDelegate.Remove(MyDelegateHandle);

//MyTriggerVolume.cpp調用委托,間接調用函數
MyInventoryGM->MyMulticastDelegate.Broadcast();

//綁定lambda
FDelegateHandle Handle;
Handle=MDOneParam.AddLambda(
  [](FString str)
  {
    UE_LOG(LogTemp,Warning,TEXT("Lambda Call,Param Value:%s"),*str);
  }
)

動態委托

  • 支持反射以及序列化,但其執行速度比常規委托慢。可
  • 支持返回值
  • 不支持參數
  • 動態多播委托可以暴露給藍圖,在藍圖中動態綁定相關的函數,而普通的委托和動態多播委托則不行

聲明宏

  • 參數構成:(委托名,參數類型1,參數名1,參數類型2,參數名2)

動態多播代理的名稱開頭須為F,否則會編譯或調用報錯

DECLARE_DYNAMIC_DELEGATE_*
DECLARE_DYNAMIC_DELEGATE(FOnGameWindowCloseButtonClickedDelegate);  // 無參、無返回值
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnAssetLoaded, class UObject*, Loaded); // 1個參數、無返回值

DECLARE_DYNAMIC_DELEGATE_RetVal_*
DECLARE_DYNAMIC_DELEGATE_RetVal(EMouseCursor::Type, FGetMouseCursor); // 無參、EMouseCursor::Type返回值
DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(UWidget*, FGenerateWidgetForObject, UObject*, Item); // 1個參數、UWidget*返回值
DECLARE_DYNAMIC_DELEGATE_RetVal_TwoParams(FEventReply, FOnPointerEvent, FGeometry, MyGeometry, const FPointerEvent&, MouseEvent);  // 2個參數、FEventReply返回值

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMyDelegate)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyDelegate, FString, InPrar)

DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FNotifyPawnChange, float, PawnHpPercent,float,PawnPhysicalShieldPercent,float, PawnMageShieldPercent);

動態委托綁定

  • BindDynamic( UserObject, FuncName )

  • AddDynamic( UserObject, FuncName )

  • RemoveDynamic( UserObject, FuncName ) 解綁單個

  • Clear全部解綁

動態多播也支持使用Remove和Removeall,用法與多播一樣

執行動態委托

  • Execute 不檢查其綁定情況即執行一個動態委托

  • ExecuteIfBound 檢查一個動態委托是否已綁定,如是,則調用Execute

  • IsBound 檢查一個動態委托是否已綁定,經常出現在包含 Execute 調用的代碼前

  • Broadcast 將該動態多播委托廣播給所有綁定的對象,但可能已過期的對象除外。

普通委托可以作為函數參數使用

//動態委托
DECLARE_DYNAMIC_DELEGATE(FWDE_Dy_Sl_Zero);

//委托變量作為參數
UFUNCTION(BlueprintCallable, Category = "FrameWork")
void RegFunDel(FWDE_Dy_Sl_Zero TargetFun);

動態多播委托

  • AddDynamic綁定的方法得被UFUNCTION標記,否則綁定無效

  • 動態代理對象類型可以使用UPROPERTY標記,並設置為BlueprintAssignable,從而暴露給藍圖使用,其他代理均無法使用(不加編譯可過,調用出錯)

DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FNotifyPawnChange, float, PawnHpPercent,float,PawnPhysicalShieldPercent,float, PawnMageShieldPercent);

UPROPERTY(BlueprintAssignable)
	FNotifyPawnChange NotifyPawnChange;

NotifyPawnChange.Broadcast(PawnHpPercent, PawnPhysicalShieldPercent, PawnMageShieldPercent);

事件

事件與 組播委托 十分相似。雖然任意類均可綁定事件,但只有聲明事件的類可以調用事件 的 Broadcast、IsBound 和 Clear 函數。這意味着事件對象可在公共接口中公開,而無需讓外部類訪問這些敏感度函數。事件使用情況有:在純抽象類中包含回調、限制外部類調用 Broadcast、IsBound 和 Clear 函數。

聲明宏

DECLARE_EVENT( OwningType, EventName ) 創建一個事件。
DECLARE_EVENT_OneParam( OwningType, EventName, Param1Type ) 創建帶一個參數的事件。
DECLARE_EVENT_TwoParams( OwningType, EventName, Param1Type, Param2Type ) 創建帶兩個參數的事件。 
DECLARE_EVENT_<Num>Params( OwningType, EventName, Param1Type, Param2Type, ...) 創建帶 N 個參數的事件。

綁定事件

與多播委托方式相同

事件執行

事件允許附帶多個函數委托,然后調用事件的 Broadcast() 函數將它們一次性全部執行。

  • Broadcast() 將此事件廣播到所有綁定對象,已失效的對象除外。

用法示例

普通用法

//MyTriggerVolume.h 類外聲明
DECLARE_EVENT(AMyTriggerVolume,FPlayerEntered)
//MyTriggerVolume.h 類內聲明
FPlayerEntered OnPlayerEntered;

//MyTriggerVolume.cpp 調用委托
OnPlayerEntered.Broadcast();

//TriggerVolEventListener.cpp 綁定委托
TriggerEventSource->OnPlayerEntered.AddUObject(this, &ATriggerVolEventListener::OnTriggerEvent);

通常用法

將Event定義於類內,通常將Event對象設為私有,類外通過公開的訪問接口進行綁定,觸發,解綁,這種方式起到了保護隱私的作用

  • 定義
class ADelegateActor:public AActor
{
	GENERATED_BODY()
 public:
 	/*
    *OwingType:擁有此Event的類,本例中使用本類:ADelegateActor
    *EventName:事件名稱
    *ParamType:參數列表
    */
	DECLARE_EVENT_OneParam(ADelegateActor, MyDelegateEvent, FString);

    //公開對Event對象的訪問接口
   MyDelegateEvent& OnEventTrigger(){return DelegateEvent;}

 private:
 	//將Event設為私有,防止類外直接訪問到,起到安全作用
  MyDelegateEvent DelegateEvent;
}
  • 綁定回調
class CallbackTarget
{
 public:

  void FunctionForAddUObject(FString str);

  UFUNCTION()
  void FunctionForAddUFunction(FString str);
  //靜態成員函數
  static StaticCallback(FString str)
  {
    UE_LOG(LogTemp,Warning,TEXT("StaticCallback Call,Param Value:%s"),*str);
  }
}
//全局靜態函數(靜態非成員函數)
void StaticFunc(FString str)
{
  UE_LOG(LogTemp,Warning,TEXT("StaticFunc Call,Param Value:%s"),*str);
}
CallbackTarget* Target=new CallbackTarget();
/*多播綁定回調函數以Add開頭*/

//delegatehandle
FDelegateHandle Handle;

//AddUObject 綁定多播
Handle=OnEventTrigger().AddUObject(Target,&CallbackTarget::FunctionForAddUObject);

//AddUFunction 綁定多播
Handle=OnEventTrigger().AddUFunction(Target,FName(TEXT("FunctionForAddUFunction")));

//AddStatic 綁定全局靜態函數
Handle=OnEventTrigger().AddStatic(StaticFunc);

//AddStatic 綁定靜態成員函數
Handle=OnEventTrigger().AddStatic(&CallbackTarget::StaticCallback);

//AddLambda 綁定Lambda表達式
Handle=OnEventTrigger().AddLambda(
  [](FString str)
  {
    UE_LOG(LogTemp,Warning,TEXT("Lambda Call,Param Value:%s"),*str);
  }
)
  • 觸發和解綁
OnEventTrigger().Broadcast("DELEGATE EVENT Call");
OnEventTrigger().Clear();

繼承的抽象事件

基礎類實現:

/** Register/Unregister a callback for when assets are added to the registry */
DECLARE_EVENT_OneParam( IAssetRegistry, FAssetAddedEvent, const FAssetData&);
virtual FAseetAddedEvent& OnAssetAdded() = 0;

派生類實現:

DECLARE_DERIVED_EVENT( FAssetRegistry, IAssetRegistry::FAssetAddedEvent, FAssetAddedEvent);
virtual FassetAddedEvent& OnAssetAdded() override { return AssetAddedEvent; }

在派生類中聲明一個派生事件時,不要在 DECLARE_DERIVED_EVENT 宏中重復函數簽名。此外,DECLARE_DERIVED_EVENT 宏的最后一個參數是事件的新命名,通常與基礎類型相同。


參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM