十六、委托
UE4 中Delegate(委托)使用頻率非常高,常用的委托有單播、多播、動(dòng)態(tài)多播、事件等等.
本篇將講解這些委托的使用方法,注意事項(xiàng)等知識(shí).?
一.什么是Delegate(委托)?
Delegate委托,又稱代理,本質(zhì)是一個(gè)特殊類的對(duì)象,它內(nèi)部可以儲(chǔ)存(一個(gè)或多個(gè))函數(shù)指針、調(diào)用參數(shù)和返回值;委托的觸發(fā)者不與監(jiān)聽者有直接關(guān)聯(lián),兩者通過委托對(duì)象間接地建立聯(lián)系;監(jiān)聽者通過將響應(yīng)函數(shù)綁定到委托上,使得委托觸發(fā)時(shí)立即收到通知,并進(jìn)行相關(guān)邏輯處理。委托的作用如同函數(shù)指針,但它更安全(支持編譯期類型安全檢查),并且更易于使用。
聲明委托——顧客點(diǎn)外賣指定某個(gè)餐館(委托),綁定委托——餐館(委托)指名(綁定到)某個(gè)員工(對(duì)象)去送餐(對(duì)象的成員函數(shù)),執(zhí)行委托——員工送餐(調(diào)用對(duì)象的成員函數(shù))
委托的聲明綁定可以在回調(diào)函數(shù)的類身上;委托的聲明綁定也可以在執(zhí)行回調(diào)函數(shù)的類身上;
二.Delegate的意義
1.將實(shí)時(shí)監(jiān)測(cè)邏輯轉(zhuǎn)換為觸發(fā)式邏輯
一般我們不會(huì)一直在每幀調(diào)用的方法里面寫一些觸發(fā)判斷,畢竟這太蠢了。
描述一個(gè)簡(jiǎn)單容易理解的小例子:
如果要實(shí)現(xiàn)物體從A移動(dòng)到B點(diǎn),在到達(dá)B點(diǎn)時(shí)打印一句"到達(dá)目標(biāo)點(diǎn)!".
如果使用檢測(cè)式邏輯:或許你會(huì)在使用Timer或者Tick每隔一段時(shí)間判斷一下是否到達(dá)B點(diǎn),這樣會(huì)導(dǎo)致判斷邏輯會(huì)在到達(dá)B點(diǎn)之前一直在重復(fù)執(zhí)行,如果判斷邏輯較為復(fù)雜,則會(huì)導(dǎo)致程序會(huì)高頻率執(zhí)行一段運(yùn)算復(fù)雜邏輯,導(dǎo)致性能降低.并且如果使用的Timer間隔比較長(zhǎng)(Tick及時(shí)事件間隔短但是還是會(huì)有延遲),可能會(huì)導(dǎo)致物體到達(dá)時(shí),Timer倒計(jì)時(shí)還沒到,導(dǎo)致判定失準(zhǔn)!
如果使用觸發(fā)式邏輯:在B點(diǎn)放置一個(gè)BoxTrigger,當(dāng)物體到達(dá)B點(diǎn)時(shí)觸發(fā)Overlap回調(diào)時(shí)執(zhí)行打印,這么做避免了高頻率執(zhí)行判斷邏輯,降低了性能的損耗,并且精確度比檢測(cè)式要更高!(box trigger也是委托的一種形式)
2.降低耦合性
可以看到,如果我們想要在B中調(diào)用A類中的Call函數(shù),我們必須在B中創(chuàng)建一個(gè)A類型的指針a,然后通過指針a去調(diào)用Call函數(shù)。這樣就會(huì)導(dǎo)致B類跟A類存在了耦合關(guān)系.?
如果C++看不出關(guān)系我們就用藍(lán)圖來解釋耦合性



B想要執(zhí)行A里面的函數(shù),那么首先要得到A類,再去執(zhí)行A里面的函數(shù)PrintA

3.Delegate偽代碼演示AB之間不存在耦合性
不一定要看懂這串偽代碼,但是要看清B類中沒有A類指針,依然執(zhí)行A類中的函數(shù)
三.使用委托的大致流程
①使用DECLARE_*宏聲明一個(gè)自定義delegate類型FDelegateXXX,通過聲明的FDelegateXXX類型創(chuàng)建一個(gè)委托對(duì)象
②?綁定需要執(zhí)行的函數(shù)指針到代理對(duì)象上(this或者類的指針)
③?執(zhí)行代理對(duì)象中的函數(shù)指針會(huì)立即執(zhí)行
④ 不需要某個(gè)函數(shù)指針時(shí),可將其從代理對(duì)象中解綁?
四.Delegate的講解
1.單播委托(不支持藍(lán)圖)
單播委托只能綁定一個(gè)回調(diào)函數(shù),新綁定的會(huì)覆蓋舊的,因此被稱為單播
三種單播:無(wú)參數(shù)無(wú)返回值,有參數(shù)無(wú)返回值,有參數(shù)有返回值?
(1)定義委托創(chuàng)建委托對(duì)象,定義委托以F開頭
(2)綁定回調(diào)函數(shù)的幾種方式
①BindUObject

BindUObject 參數(shù)講解:
?InUserObject:綁定的回調(diào)函數(shù)屬于那個(gè)類就傳入這個(gè)類的指針變量;委托跟回調(diào)函數(shù)在同一個(gè)類就用this
?InFunc:被綁定的回調(diào)函數(shù),注意,InFunc是InUserObject的成員函數(shù)
?注意理解!! InUserObject是InFunc的擁有者,傳入這兩個(gè)參數(shù)的目的是為了確認(rèn)委托要綁定函數(shù)(InFunc)屬于那個(gè)類(InUserObject)
CallbackTarget類(自己定義的類)作為回調(diào)函數(shù)的擁有者,用于提供被綁定的回調(diào)函數(shù)(說人話就是委托觸發(fā)后,執(zhí)行那個(gè)類里面的那個(gè)函數(shù))
其實(shí)這個(gè)CallBackTarget這個(gè)類里存在著我們需要觸發(fā)后執(zhí)行的函數(shù)的函數(shù),這個(gè)類可以是任意的類;
②BindUFunction
BindUFunction 參數(shù)講解:
?InUserObject:綁定的回調(diào)函數(shù)屬于那個(gè)類就傳入這個(gè)類的指針變量;在同一個(gè)類就用this
?InFunctionName:回調(diào)函數(shù)的函數(shù)名.
注意!! ue4之所以僅根據(jù)函數(shù)名就能正確綁定回調(diào)函數(shù),是通過ue4的反射機(jī)制,這就要求被綁定的函數(shù)必須要被UFUNCTION()宏包住,否則無(wú)法正確的找到對(duì)應(yīng)的函數(shù)導(dǎo)致綁定失敗.
注意理解!! InUserObject是InFunc的擁有者,傳入這兩個(gè)參數(shù)的目的是為了確認(rèn)委托要綁定函數(shù)(InFunc)屬于那個(gè)類(InUserObject)
③BindStatic
BindStatic可以綁定靜態(tài)成員函數(shù),也可以綁定全局靜態(tài)函數(shù)(靜態(tài)非成員函數(shù))?
④BindLambda?
Lambda表達(dá)式,不清楚的請(qǐng)?zhí)D(zhuǎn)到下面鏈接進(jìn)行了解

?(3)觸發(fā)(執(zhí)行)回調(diào)函數(shù)的寫法
重點(diǎn)解釋一下:無(wú)反回值回調(diào)是ExecuteIfBound()而有返回值的要用Execute()


?(4)委托解綁寫法

單播委托的小案例
委托/回調(diào)函數(shù)/執(zhí)行回調(diào)函數(shù)都在同一個(gè)類中的案例(傳this):BindObject
委托聲明綁定在回調(diào)函數(shù)的類身上

在UE中新建一個(gè)繼承與Actor的C++類:CPP_TestBindUObject_FunClass
.h文件
.CPP文件
委托與執(zhí)行回調(diào)函數(shù)一個(gè)類,回調(diào)函數(shù)一個(gè)類中的案例(傳類的指針):BindObject

回調(diào)函數(shù)的類的代碼
.h文件
.cpp文件
委托及執(zhí)行回調(diào)函數(shù)的類的代碼
.h文件
.Cpp文件
利用委托開關(guān)門的案例:走到觸發(fā)盒子里面開門,離開觸發(fā)盒子關(guān)門
Trigger.h文件
Trigger.Cpp文件
Door.h文件
Door.Cpp文件
重點(diǎn)講一下案例2.3中的創(chuàng)建類的指針:由于綁定余姚類的指針?biāo)栽谶@里講一下
案例2:在場(chǎng)景中沒有此actor用spawn實(shí)例化一個(gè)actor
案例3:在場(chǎng)景中有door的一個(gè)actor了,所以我們用get actor of class
get actor of class最終返回AActor*,需要得到AMyDoor還需要cast一下

延伸一下:當(dāng)場(chǎng)景中有多個(gè)door的actor,只需要里面幾扇門打開
需要傳進(jìn)去一個(gè)AActor*的引用,是空的,填充作用

當(dāng)場(chǎng)景中有多個(gè)door的actor,每扇門都需要打開
利用getallactorofclass填充數(shù)組,在將數(shù)組進(jìn)行遍歷

2.多播委托(不支持藍(lán)圖)
相較于單播委托,多播委托可以綁定多個(gè)回調(diào)函數(shù),當(dāng)其觸發(fā)時(shí),所有綁定的回調(diào)函數(shù)都會(huì)執(zhí)行;多播委托和單播委托定義方式很相似,除了多播委托沒有返回值,這里我們只用 有參數(shù)的多播委托進(jìn)行講解?
(1)定義委托
(2)綁定回調(diào)函數(shù)
(3)執(zhí)行多播委托
(4)解綁多播委托
3.動(dòng)態(tài)多播委托(支持藍(lán)圖)
與藍(lán)圖的事件分發(fā)器是一回事
(1)定義委托
(2)綁定回調(diào)函數(shù)
(3)執(zhí)行多播委托
(4)解綁多播委托
(5)動(dòng)態(tài)多播藍(lán)圖中的用法
創(chuàng)建一個(gè)Test_Dynamic_Multicast_Delegate的c++類
.h文件中聲明委托與創(chuàng)建委托對(duì)象
創(chuàng)建一個(gè)BP_DynMulDelegate的藍(lán)圖類繼承Test_Dynamic_Multicast_DelegateC++類

可以看到我們C++中定義的動(dòng)態(tài)多播對(duì)象在藍(lán)圖中以EventDispatcher形式存在

動(dòng)態(tài)多播委托與藍(lán)圖交互
新建一個(gè)c++類:Test_Dynamic_Multicast_Delegate
.h文件(委托聲明 創(chuàng)建委托對(duì)象 回調(diào)函數(shù)聲明)
.cpp文件 (委托綁定回調(diào)函數(shù) 執(zhí)行委托 回調(diào)函數(shù)的實(shí)現(xiàn))
創(chuàng)建一個(gè)BP_DynMulDelegate的藍(lán)圖類繼承Test_Dynamic_Multicast_DelegateC++類


也可以在C++中不綁定,只在藍(lán)圖中綁定

(6)TriggerBox本質(zhì)也是一個(gè)C++的動(dòng)態(tài)多播委托

C++中的OnComponentBeginOverlap



4.Even(事件)? ?基本用不到,簡(jiǎn)單了解一下
Event聲明于類內(nèi),通常將Event對(duì)象設(shè)為私有,類外通過公開的訪問接口進(jìn)行綁定,觸發(fā),解綁,這種方式起到了保護(hù)隱私的作用
(1)Even的定義

DECLARE_EVEN_xxxParams(OwningType, EventName, Param1Type)
OwingType:擁有此Event的類
EventName:事件名稱
ParamType:參數(shù)列表
(2)綁定回調(diào)函數(shù)寫法
(3)綁定回調(diào)函數(shù)寫法
(4)解綁回調(diào)函數(shù)
以上就是四種常用Delegate的用法和注意事項(xiàng).