最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

[UE5]使用C++批量管理StreamLevel(上)

2023-09-24 22:53 作者:Epslin  | 我要投稿

說說前情

在智慧園區(qū)開發(fā)過程中,經(jīng)常有要切換StreamLevel(以下和“流送關(guān)卡”叫法混用)的需求,比如聚焦到某個(gè)建筑時(shí)加載建筑內(nèi)部結(jié)構(gòu),使用流關(guān)卡,能避免在UE打包文件剛運(yùn)行時(shí)就加載99%的時(shí)間都用不上的內(nèi)容,實(shí)現(xiàn)按需加載。同時(shí),還有一些功能需要我們基于某些包含多個(gè)場景的“預(yù)設(shè)”額外再加載卸載,通過對流送關(guān)卡做一個(gè)管理系統(tǒng)將“預(yù)設(shè)”與當(dāng)前已加載的關(guān)卡進(jìn)行對比,可以避免重復(fù)的加載卸載,實(shí)現(xiàn)精準(zhǔn)控制。

用藍(lán)圖管理會有什么問題?

那么該怎么搞這么一個(gè)管理系統(tǒng)呢?使用過藍(lán)圖的朋友都知道,藍(lán)圖內(nèi)默認(rèn)的StreamLevel管理函數(shù)比較孱弱,甚至不能原生加載多個(gè)關(guān)卡。如果在For循環(huán)中連續(xù)調(diào)用LoadStreamLevel,會發(fā)現(xiàn)只響應(yīng)了其中的一個(gè),所以就需要一些奇妙的寫法來實(shí)現(xiàn)多個(gè)關(guān)卡的加載。

藍(lán)圖中“手動(dòng)Loop”遞歸實(shí)現(xiàn)多個(gè)關(guān)卡加載

但這樣會有什么問題呢,對于藍(lán)圖的異步邏輯,如果有鄰近的兩次調(diào)用,比如用戶在兩個(gè)都需要切換的模塊間切換,就會發(fā)現(xiàn)事情再次壞了起來。

如果A還沒有完成卸載,B就要加載,就會概率觸發(fā)加載卸載不完全的問題。那么又需要寫個(gè)檢測函數(shù),等A完成后再告訴B你可以進(jìn)行加卸載了,這對業(yè)務(wù)模塊的編寫搞出了很多心理負(fù)擔(dān),又或許你想除了這樣更加“奇妙”的寫法。

問題更大的奇妙寫法

另外的問題就是,在加載完成后需要告知加些在申請的模塊,常見的寫法就是用委托/事件分發(fā)(EventDispatcher),此時(shí)有一個(gè)更加危險(xiǎn)的事情是藍(lán)圖中可以調(diào)用UnbindAllEventsFrom,萬一同事一不小心調(diào)用了這個(gè),那其他同事的功能很有可能被誤傷。心理負(fù)擔(dān)++。

所以該怎么解決這些問題?

  • 對于奇妙的Loop寫法,也可以調(diào)用C++函數(shù)使用簡單的For循環(huán)完成。

  • 對于通知問題,可以定義一個(gè)接口類(Interface),需要加卸載時(shí)先把要告知誰傳進(jìn)來,如果這個(gè)告知目標(biāo)實(shí)現(xiàn)了這個(gè)接口,在處理完對應(yīng)的請求時(shí)就通過接口通知它。

  • 對于兩個(gè)十分接近的加卸載請求,可以使用隊(duì)列,在A的請求沒做完之前,把B的請求先壓入隊(duì)列,依次處理。UEC++中提供了TQueue<>數(shù)據(jù)類型但不能暴露給藍(lán)圖。

考慮到不想在藍(lán)圖中實(shí)現(xiàn)隊(duì)列(懶)和藍(lán)圖和C++不同模塊的兼容性,使用C++編寫關(guān)卡管理內(nèi)容和接口有更好的通用性。Subsystem作為可以儲存變量又不需要Cast的全局對象,在使用起來更加簡單,也可以幫其他同事減輕記憶負(fù)擔(dān)。

在C++工程中創(chuàng)建WorldSubsystem,開整。

準(zhǔn)備測試場景

這里我們準(zhǔn)備了測試場景,每個(gè)Mesh處于一個(gè)單獨(dú)的StreamLevel,其中奇數(shù)結(jié)尾的關(guān)卡默認(rèn)為加載狀態(tài),偶數(shù)結(jié)尾的關(guān)卡默認(rèn)為不加載狀態(tài)。這樣我們可以直觀了解各個(gè)流關(guān)卡的加載狀態(tài)。

測試場景-編輯器下

測試場景-運(yùn)行狀態(tài)

如何加載/卸載多個(gè)流送關(guān)卡

加載流關(guān)卡的核心函數(shù)是UGameplayStatics::LoadStreamLevel,這個(gè)函數(shù)需要依次傳入WorldContextObject、關(guān)卡名稱、加載后是否可見、是否阻塞加載、用于加載完成回調(diào)的?FLatentActionInfo。

前幾個(gè)參數(shù)其實(shí)都很好解釋,主要在于最后一個(gè)參數(shù)?FLatentActionInfo,也是后文中會提到的“回調(diào)地獄”的源頭。但后邊的事后邊再說,先寫一個(gè)小函數(shù)來測試,創(chuàng)建TryLoadSomeLevels函數(shù)如下:

在調(diào)用LoadStreamLevel的時(shí)候傳入上邊定義的FLatentActionInfo,在加載完成后會調(diào)用第三個(gè)參數(shù)中指定名稱的函數(shù),這個(gè)函數(shù)需要使用UFUNCTION()修飾以加入反射系統(tǒng)。在藍(lán)圖中嘗試調(diào)用TryLoadSomeLevels

藍(lán)圖調(diào)用
調(diào)用前
調(diào)用后

發(fā)現(xiàn)還是只調(diào)用了一次,回調(diào)函數(shù)的Log也只打印了一次,和最早提到的簡單for循環(huán)藍(lán)圖方法沒有區(qū)別。

問題的源頭在于FLatentActionInfo,如果傳入的UUID相同,便只會執(zhí)行一次,為了實(shí)現(xiàn)多次執(zhí)行就需要把UUID在循環(huán)中每次變更,我們可以在循環(huán)里做如下更改:

這邊還有一點(diǎn)需要額外提示,在構(gòu)造FLatentActionInfo的時(shí)候,我選擇了帶參數(shù)的構(gòu)造函數(shù)而不是默認(rèn)構(gòu)造,在嘗試過程中,發(fā)現(xiàn)默認(rèn)構(gòu)造有時(shí)會沒法修改UUID,比如下面的代碼,在后續(xù)想修改其中參數(shù)時(shí),就發(fā)現(xiàn)并沒有修改成功。

默認(rèn)構(gòu)造值修改失敗


再次嘗試調(diào)用,發(fā)現(xiàn)已經(jīng)能加載傳入的全部關(guān)卡了,同時(shí)回調(diào)的函數(shù)也被執(zhí)行了關(guān)卡數(shù)對應(yīng)的此次數(shù)。

循環(huán)中更改UUID后調(diào)用結(jié)果
回調(diào)函數(shù)Log打印

那我們再試試卸載,同樣的方法更改一下調(diào)用函數(shù):

藍(lán)圖調(diào)用
執(zhí)行成功
回調(diào)函數(shù)Log打印

從上圖我們可以發(fā)現(xiàn),C++中的邏輯已經(jīng)能按我們的想法加載、卸載多個(gè)關(guān)卡了。我們的初步目標(biāo)已經(jīng)完成。

告知調(diào)用者加載完成

由于加載卸載都是異步過程,需要在加卸載完成后才能執(zhí)行的行為就依賴加卸載管理類的通知,告知后續(xù)模塊可以加卸載行為已經(jīng)完成,可以進(jìn)行后續(xù)操作。在嘗試過程中使用接口和多播委托的方式都有可以起到目的。這邊我們分別演示一下。

首先需要對完成回調(diào)計(jì)數(shù),當(dāng)全部完成后才告知目標(biāo)。因此需要?jiǎng)?chuàng)建用于計(jì)數(shù)的變量,當(dāng)全部完成后調(diào)用委托。

然后在藍(lán)圖里綁定即可使用:

藍(lán)圖中綁定委托實(shí)現(xiàn)通知
成功打印通知消息

但是委托的問題在于每次使用都需要綁定解綁,不但增加記憶負(fù)擔(dān)而且容易被誤通知、誤解綁。使用接口就更方便一些,下面演示一下使用接口通知。

首先創(chuàng)建接口TrySwitchLevelAction

為了照顧通知其他對象的需求,在調(diào)用前需要傳入需要通知誰,因此在上邊委托的修改基礎(chǔ)上,再次對Load、Unload、完成通知函數(shù)進(jìn)行修改

完成之后到藍(lán)圖中實(shí)現(xiàn)接口:

在調(diào)用者中實(shí)現(xiàn)接口

因?yàn)閭魅氲腘otifyTarget并不限制為自己,可以通過傳入其他對象來實(shí)現(xiàn)跨類告知,這里創(chuàng)建了另一個(gè)藍(lán)圖同樣實(shí)現(xiàn)這個(gè)接口,在收到通知時(shí)通過修改PrimitiveData實(shí)現(xiàn)材質(zhì)顏色的修改:

在其它類中實(shí)現(xiàn)接口

當(dāng)收到通知后,會將自己的PrimitiveData設(shè)置為1來影響材質(zhì)

使用PrimitiveData驅(qū)動(dòng)顏色變化
在調(diào)用者中傳入需要通知的對象

調(diào)用前

未加卸載關(guān)卡、沒有收到通知
加卸載完成收到通知
兩個(gè)對象都受到了接口調(diào)用

接口的缺點(diǎn)在于對于異步任務(wù)需要傳入非const的對象指針,在C++編程使用中會增加被對象被修改的風(fēng)險(xiǎn)。

小結(jié)

在上面的嘗試中,我們已經(jīng)大致搞定了多個(gè)流送關(guān)卡的批量加載卸載和通知自己或其他對象,但是每次都需要在藍(lán)圖中創(chuàng)建兩個(gè)節(jié)點(diǎn)還是太麻煩了,尤其對于多播委托,甚至還需要再搞個(gè)計(jì)數(shù)器,實(shí)在是不夠便捷。

在下一篇中,會講解一些進(jìn)階些的內(nèi)容,主要包括:

  • 嘗試把加載卸載放在一起統(tǒng)一處理,讓調(diào)用過程更加簡潔;

  • 把要加卸載的關(guān)卡和當(dāng)前關(guān)卡比較,避免不必要的加卸載調(diào)用;

  • 使用隊(duì)列引用高頻的關(guān)卡切換請求。



[UE5]使用C++批量管理StreamLevel(上)的評論 (共 條)

分享到微博請遵守國家法律
得荣县| 韩城市| 晋宁县| 武山县| 陇川县| 普陀区| 峨眉山市| 芮城县| 佛学| 佛教| 朝阳县| 广灵县| 孝义市| 贵溪市| 鄢陵县| 开化县| 凉山| 东山县| 华安县| 屏东市| 东台市| 平舆县| 庆元县| 平阴县| 绥江县| 舞阳县| 综艺| 云霄县| 高要市| 洛阳市| 合水县| 特克斯县| 洛浦县| 六枝特区| 江源县| 新绛县| 肥乡县| 张家口市| 仙居县| 汤阴县| 类乌齐县|