[UnrealEngine] Plugin製作相關注意事項與心得分享

posted in: 未分類 | 0

前言 寫出一套好用好維護的程式碼相信會是所有程式設計師無意識中追求的目標,而把自己的功能包裝成Plugin是一個非常直接達成這個目的的手段,因為設計的過程會強迫把自己置更高、更抽象的位置往下俯瞰整個遊戲系統並定位出自己的功能應該在哪個位置。 只是,並不是任何的程式碼都適合放進Plugin中。因此接下來我的目的主要是想根據自己過去的經驗,分享一些自己在寫各種Plugin時的大原則。雖然這些想法不一定適用於所有的情境,應該也會有許多我沒考慮到的狀況,但基本上也就只是目前在我有限的經驗中現階段的想法。如果有人有不一樣的觀點也歡迎一起討論,或許可以碰撞出一些東西也說不定。 順帶一提,這邊的Plugin討論的主要是CodePlugin,其他類型的Plugin,例如Asset Only或BP Only,並不在接下來的討論範圍內,除非我有另外指明。 目前我在Unreal Marketplace已經有上架六項產品,內容包括Quest、Interact、Dialogue以及Tween,有興趣的可以參考: https://www.unrealengine.com/marketplace/en-US/profile/horizon-studio 另外我也會不定期的在我的FB頁面上分享一些開發日誌,通常是跟遊戲技術相關的話題,有興趣的歡迎追蹤:https://www.facebook.com/dorgonresearches 本文開始 Unreal中的程式碼我們可以非常粗爆的大致分層如下:EngineModule->EnginePlugin->GamePlugin->GameModule->GameFeaturePlugin。其中EngineModule、EnginePlugin我們頂多修修bug不可能做太大的修改;GameModule則是看專案團隊的風格會有不同的構成方式,我們等一下再回來聊這部份;而GamePlugin跟GameFeaturePlugin則是我們接下來要討論的重點。 EnginePlugin 、GamePlugin相對直觀,大家應該不會有什麼疑惑,但GameFeaturePlugin這個概念或許就有人沒聽過了。這個功能是5.0所引入的最大變革之一,對程式相關職能的人而言,特別是寫Gameplay相關的,我覺得其重要性甚至更勝於Nanite跟Lumen,因為相關機制會從根本性上的改變了我們程式碼的架構方式以及思考模式。 就設計理念看來,他是屬於DLC概念的實現,跟Patch不同,Patch需要蓋掉原本的內容,而DLC必須要做到「熱插拔」的機制。 就傳統UE4 project,我們要達到DLC的這項要求非常麻煩,因為我們只能對「Content-Only」plugin包出dlc包,而哪些dlc需要開啟或關閉則需要你在BaseGame中做出一些可以管理的設計。在GameFeature Plugin 機制出現後,我們總算不需要做這些勞力活了。注:這邊的BaseGame指的是 ${PROJECT_ROOT}/Source 下面所有的Runtime GameModule以及${PROJECT_ROOT}/Content下面所有會打包進去的Asset。 有些人會困惑於我們建出的 GameFeature Plugin 沒辦法跨專案共用,但這個理解並沒有錯,基本上GameFeature Plugin需要依賴於BaseGame而存在,因此我們也可以理解成他是專屬於project的Plugin。若你的功能想要跨專案共用,你要寫的是一般的GamePlugin而不是GameFeature。 在理解了GamePlugin跟GameFeaturePlugin的不同點之後,讓我們先來思考一下,Plugin到底是什麼?就字面上來看的話,形象一點解釋,他就像是一個插頭,把這個插頭插進我們的專案之後就能夠提供各種我們遊戲專案會需要的功能。雖然Plugin不像DLC需要做到「熱插拔」,但大家期望的是功能的插入跟移除是相對簡單的。因此每個GamePlugin在設計上要能做到獨立運作,也就是說在設計上只能依賴於Engine或EnginePlugin,依賴於其他同層級的GamePlugin會明顯造成遊戲專案的使用障礙。移除簡不簡單這件事取決定Plugin的功能與細部設計這邊先不展開討論,但對於功能的插入而言,應該沒有人會希望開啟某個Plugin前還需要去閱讀文件看清楚有哪些相依Plugin需要下載吧?若我們設計的Plugin有依賴於其他同層級的GamePlugin的情形發生,可能要思考看看哪些功能要下放到GameModule給專案自己實作,哪些功能需要整合進同一個GamePlugin中。 由上面的討論我們知道,Plugin在本質上就是對於能獨立運作功能的封裝,而這些功能來自於各種需求各異的不同專案,因此Plugin在設計上必須要放在一個更抽象的位置以滿足不同專案的需求。因此我不建議在Plugin中實現任何的框架機制,因為那個是Engine該做的事;若只是薄薄一層的實作,主要目的只是更改某些Engine的預設設定的話,那個則是專案GameModule該做的事,我們不該假設我們的設定會比Engine的預設更通用;若是覺得Engine的某些Function實作有bug或不夠完美,所以想繼承下來做override來做workaround,那個也是專案該做的事,我們可以寫一些文檔分享給其他團隊說明為什麼我們想做這些修改,而不是寫成某個Plugin要求大家先繼承某個class再做某些事,因為那不一定適用於所有的專案,再次強調,我們不該假設我們的實作會比Engine的實作更具有通用性,若你覺得你的目的是修掉Engine Bug,你該做的是上Github送PullRequest給Engine。 那麼到底什麼東西適合寫成一個Plugin,最好的判斷方法是:先假設把這個Plugin拿到UnrealMarketplace上賣時,我們是否能夠明確的指出這個Plugin想解決的是什麼問題?受眾是誰、有多少人可能會需要這個功能……等等類似的問題,若無法回答這些問題,代表相關功能的想法還不夠成熟,建議先拉回GameModule中等待各種需求的磨練。 那麼在把功能寫在GameModule時我們該注意什麼呢?就我所知,根據專案團隊的風格會有不同的構成方式,有人喜歡使用一個大module放入所有的功能、也有人喜歡依功能的性質導入一些模組化的設計,但不可否認的是,由於這邊的程式碼非常接近Gameplay、更迭會非常快速,有可能一個遊戲企畫的改變導致某個系統在瞬間變成一個沒用的東西。基於這種變動頻繁的特性,因此有些人會覺得不需要在GameModule的層級上浪費時間進行遊戲邏輯的模組化設計,反正就是想辦法用最快的方法將功能做出來,不要因為模組化而導入的各種限制而拖慢了組裝系統的時間。不過我自己倒是有不同的觀點,在GameModule這個層級做模組化反而是我們用來對抗變化的手段,良好的模組化設計應當要考慮到當你想把某個功能移除時需要耗費多少的力氣,理想狀況是直接把該Module移除就行,不過這部份就屬於不同系統的實作細節了,就不再往下討論。除此之外,我認為模組化的練習可以幫助我們慢慢的把相對不成熟的想法粹練出來。我們不需要一開始就用Plugin的高度在思考問題,在個人經驗不足或需求還不明確的情況下很容易陷入寸入難行的地步;相對的在GameModule層級進行設計的話,我們可以先以更貼近專案的需求的方式來思考怎麼進行抽象化的設計。 結論 以上寫了這麼多,大致下可以簡單用以下幾點描述:     1. 每個GamePlugin在設計上要能做到獨立運作,也就是說在設計上只能依賴於Engine或EnginePlugin。     2. 不建議在Plugin中實現任何的框架機制,因為那個是Engine該做的事,不該假設我們的設定會比Engine的預設更通用。 … Continued

[開發日誌][UnrealEngine] 如何將Mixamo的動畫Retarget給Unreal Character

posted in: 未分類, 開發日誌 | 0

這篇文章主要是要記錄自己怎麼把Mixamo中的動畫導出來給Unreal中的Mannequin用,以及除何修正中途遇到的一些坑。 使用版本:5.1 使用的是Third Person Template 第一步當然是先從Mixamo下載看中想使用的動畫。 由於Mixamo的骨架跟Unreal不相容,主要原因Mixamo是少了一個root,這會造成root motion沒辦法正常運作。我們可以下載 mixamo_convert這個Blender Addons來幫我們加上一個root bone。 如下圖,右邊加上root的是我們希望的狀態: 這邊要注意的是,Blender Addons的安裝要使用zip才認的到。 https://github.com/enziop/mixamo_converter 下載完zip之後,從preference選Add-ons安裝,然後把功能打開 接著從右上方打開選單 可以看到Mixamo Rootbaker 可以選擇把bone轉成UnrealEngine公版的bone Name,但我這邊不勾,之後自己再使用UE內建的IK Rig以及IK Retargeter來做map,詳細步驟後面會記錄。另外他的OutputPath目錄不會自己建,所以要自己先開: 之後我們就可以先匯入轉換過後的Mixamo的模型(X Bot.fbx) 接著匯入其他的動畫,記得Import Mesh勾掉,Skeleton選擇剛剛匯入的。 然後就可以開始建立IK Rig,這個檔案的目的是建出組Bone Chain,讓我們可以可以在IK Retargeter中指定Source IK Rig跟Target IK Rig做map。 Source就是剛剛匯進來的Mixamo,我們必須要手動建一個出來(右鍵Animation -> IK Rig 下可以找到),Target則是Unreal內建的Mannequin,路徑是/Game/Characters/Mannequins/Rigs/IK_Mannequin。 … Continued

[開發日誌][UnrealEngine] 如何自動排序原始碼中的include

posted in: UnrealEngine, 未分類, 開發日誌 | 0

之前在寫code的時候,發現某個cpp檔中的include了一大堆其他module中的檔案,而且其中一大堆重覆include,越看越覺得亂,然後心中的潔癖突然發作想要好好的整頓一番。第一個想法是,如果我們的include能夠自動依字母排序,這樣就能夠很清楚的看出有哪些include重覆了,若我們能好好的依不同的module進行分類,還能夠清楚的知道我們用到了各別module中的哪些功能。 查詢了一下,其實VisualStudio中還真的有相關的功能,他的快速鍵是Shift+Alt+L, Shift+Alt+S: 由於我有買Visual Assist X,因此另外也試了一下它的版本,並把快速鍵設成了F9。最後發現這個版本比內建的順手多了: https://docs.wholetomato.com/default.asp?W175 另外也發現了Visual Studio Code也有SortLine的功能: 做完快速鍵的設置後,我們就可以使用該功能來幫我們做排序,我自己是喜歡依據module的不同來區分,如下圖: 除了include之外,我也喜歡對build.cs中的DependencyModuleNames做排序: 嗯,看起來清爽多了。

[UnrealEngine] 如何使用DataFragment設計進行資料欄位擴充

posted in: UnrealEngine, 未分類, 開發日誌 | 0

本文章內容基於UnrealEngine5.1版本 最近在看5.0新導入的MassFramework,看到裡面出現了FInstanceStruct這個詞引起了我的興趣。基本上這東西可以讓我們動態選擇struct,而在Unreal中是把這種組裝用的property稱為Fragment。這個概念我第一次看到是在Unreal官方釋出的Lyra範例專案中看到,只是當時實驗了一下,由於他是使用UObject搭上reflection macro來實現,我沒辦法把一個UObject直接放進DataTable的Row中做編輯。這帶來的結果,就是當我在製作Plugin的時候,若需要用到DataTable時,必須要預先預想好所有使用者可能會想要有的欄位:哪些要直接放到TableRow中、哪些要指向外部的asset讓使用者自己在自己定義的BP或DataAsset中擴充。然而,可以想像的到,多了一個uasset就代表編輯的時候要另外開檔案,這對企畫人員非常的不友善,而且檔案一多,在維護上也會有不小壓力。這次看到了FInstanceStruct,馬上直覺想到這東西或許可以解決這個困擾我很久的問題。稍微實驗了一下,馬上驗證了上面提到的問題可以靠FInstanceStruct的機制來解決,因此決定花一點時間整理二種方法的優缺點。 接著,讓我們來看看這二種方法個別怎麼實現吧。 使用InstanceStruct製作Fragment的例子 InstanceStruct是定義在StructUtils這個Plugin中,記得要先去開起來。雖然在5.1還是Experimental,不過它是MassEntity的相依功能,而這個Plugin已經進Beta,因此看起來是不太需要擔心,目前我用起來感覺還蠻穩定的。 在把Plugin開起來之後,首先我們需要要定義一個BaseFragment: 接著就可以在TableRow中宣告這個property,而這個property需要的meta如下: BaseStruct用來限定Editor中可以顯示哪些Struct出來,而ExcludeBaseStruct可以把這個BaseFragment從選項中排除。 然後就可以根據需求繼承這個BaseFragment並加入任何需要的資料。 實際在DataTable中使用起來的體驗如下圖,我們可以自由選到所有繼承自BaseFragment的struct,裡面對應可編輯的欄位也會動態反射顯示出來: 接著可以用以下方法拿出我們所定義的Fragment: 不過這方法的缺點是,相關的擴充功能只能在C++中進行,因為我們無法用BP去剛剛宣告出來的BaseFragment。 使用UObject製作Fragment的例子 基本概念跟上面的例子差不多,只是我們需要換成使用UObject宣告BaseFragment,另外要記得UCLASS裡要加上DefaultToInstanced跟EditInlineNew。 宣告使用,可以在BP或DataAsset中宣告,這邊宣告在DataAsset中: 接著就可以繼承下來做資料的擴充 當我們把他宣告在DataAsset中時,就可以根據狀況選擇需要的Fragment,另外除了C++外,也可以純使用BP下來做欄位的擴充,裡面的可編輯欄位也能動態的反射顯示出來。 使用法方就是把DataAsset讀進來後,直接對Fragment做Cast就行: 結論 二種製作Fragment的方法各有優缺點,就資料擴充性而言由於InstanceStruct只能在C++中進行擴充,而UObject則可以同時在C++/BP中做,這讓UObject版本帶來某方面的優勢,然而,這方法的缺點是需要遊戲設計者另外製做出BP或DataAsset才能夠做資料的編輯,也就是說會多一個uasset,這在某些需要定義大量道具、武器裝備數據的應用是無法接受的;相反的InstanceStruct則可以很自然的作為TableRow的擴充嵌入DataTable。關於這點,我們可以看下圖,CustomDataFragment使用的是InstanceStruct方法,而CustomDataAsset則是使用UObject的方法,這個DataAsset中包含了Fragment的定義。 因此哪種方法比較好,可能要看遊戲的設計而決定。例如UE5導入的MassFramework,裡面的FMassFragment是基於InstanceStruct的設計;而在Unreal官方釋出的Lyra範例專案中,他的裝備系統是基於UObject Fragment方法的設計。另外若是多個TableRow需要「共用」參數的話,或許UObject Fragment也是個不錯的解決方案。我自己是在資料表中同時提供二種方式,讓使用者自行決定要使用哪種方式做擴充。

[UnrealEngine][讀書心得] 關於UnrealEngine中的ISPC與SIMD的一些雜談

posted in: 未分類 | 207

SIMD(Single Instruction Multiple Data)指的是用一個指令同時處理多個資料這個概念,只是我們常常被誤導說,在CPU上可以使用SIMD指令來加速──這種說法其實並不精確。對,我就是被誤導的那個。在UE4.23時官方整合了ISPC,以更好的幫助CPU做平行化加速,我之前一直把這東西跟SIMD畫上等號,沒去仔細思考這東西的存在到底想解決什麼問題。 根據指令數跟跟資料的處理方式,這個概念其實是根據Flynn’s Taxonomy來進行分類,而SIMD只是其中一種。 SIMD就現行的計算機架構上,分為以下三種: 不過由於Vector Architecture在商業競爭上輸給了Intel因此逐漸式微,以Scalar Processor為主的設計主宰了過去將近30~40年的時間。輸掉的原因有很多,但主要並不是技術問題,而是那個時代大部份的人不需要那麼強的計算力,客戶太少,再加上摩爾定律的推波助瀾下,人們的注意力自然就只集中在由Intel主推擁有最多客戶的架構上。然而近來由於AI時代的來臨,Vector Architecture架構又重新回到了人們眼界中了,例如google tensor裡面的TPU就包含了Vector processor,以及最近很火紅的RISC-V,由於設想目標是embedded devices,不像個人電腦需要考慮兼容性,因此可以大膽的拋棄舊包袱,轉而使用相關的Vector Operation指令。 另一方面,關於Multimedia SIMD,主要是由於Intel在網路起飛的時代發現我們基於Scalar Processor的架構還是需要Vector Architecture那種一個指令同時處理多個資料的加速能力,因此他的解決方式非常粗暴,就是加入新的register跟指令集的方式來支援這個概念。不過當我們開始疊加register跟指令集之後就開始失控了,每我們加一組新的register,我們就要追加一堆新的指令集來支援相關Vector操作,從XMM(128 bit)、YMM(256 bit)到ZMM(512 bit)。這些指令集的擴充非常快速,從1996年開始,幾乎每2~3年就有新的特性加入,然而為了兼容性,新的指令集在加入x86系統後就非常難被移除,因此現代指令集暴增的這麼快速有很大的原因可以歸究於Multimedia SIMD的發展。 接下來讓我們來稍微看一下這些擴充指令集,感受一下它發展的混亂吧:從1996年的MMX,它總共有57條擴充指令使用,到SSE又追加了70條,接著SSE2加入了144條指令,而SSE3只是在原有架構下的擴充,所以只追加了13條指令。接下來則是SSE4.1跟4.2,又分別加入了47條跟7條。前面的架構使用到的是XMM register,接下來為了增加平行化的數量,Intel推出了AVX系列,使用的是YMM register,然後又更進一步的推出了AVX-512,使用ZMM register。可惜的是我目前沒找到AVX的指令集數量,不過我猜應該有上百個。問了一下AI,他是跟我說要看所使用的具體版本。 對於最新的AVX512,Linus Torvalds在2020年的時曾經破口大罵說:我希望AVX-512可以死的痛苦一點(I hope AVX512 dies a painful death)。希望intel不要靠增加魔法指令來讓跑分好看,要他們回到正軌。 基本上這玩意耗能大,大部份的人享受不到好處,又不是什麼科研計算,也不是server,一般人在使用電腦的時候又常常需要切換process執行,這代表作業系統會不斷的進行Context Switch,要知道,每次Context Switch是需要把當前計算用到的register保存下來,在這一讀一寫之間很容易讓使用者感受到延遲,而且還多佔了空間。 另外在Intel 12th … Continued

[演講筆記][UnrealEngine] UE5 Deferred Technology Pipeline for Mobile Games

posted in: 未分類 | 0

花了一些時間把EpicGameChina關於Mobile Deferred Render的演講看完並做了一些筆記,分享給大家,也推薦大家去看影片,我是覺得收獲蠻多的。 影片在此:UE5 Deferred Technology Pipeline for Mobile Games 1. Mobile Deferred Render直到5.1才算比較成熟 2. Deferred的優勢在於可以減少shader permutation,因為在base pass不用算lighting,因此相關的permutation就可以從model material直接decouple出來,省下大量的shader數量。 3. mobile deferred vs mobile forward在關掉static lighting的比較:         a. Mobile Deferred只需要建出LMP_NO_LIGHTMAP一種caseb. Mobile Forward則需要建出LMP_NO_LIGHTMAP跟LMP_MOBILE_DIRECTIONAL_LIGHT_CSM搭上bEnableLocalLights開關,總共有四種case。         c. … Continued

[UE4] FastBuild 4.26的一些實驗數據

posted in: 未分類 | 0

關於怎麼將FastBuild設置起來,可以參考這篇文章: [UE4] 如何使用FASTBuild來做分散式編譯 – 地平線上的多貢地平線上的多貢 (horizon-studio.net) 由於4.26內建了FastBuild的功能,因此找了一點時間做了一下實驗 手邊有二台機器,其實驗環境規格如下:Local: AMD 5950xRemote:MacMini 2018版 6 Core i7 with windows 10 (VMWare Fusion) 在沒用FastBuild,建置手邊一個遊戲專案,共422個cpp所需耗時如下: 1>Total time in Parallel executor: 97.03 seconds1>Total execution time: 106.11 seconds 初始使用FastBuild建置時間如下: 1>— Summary —————————————————–1> /—– Cache —–\1>Build: Seen Built … Continued

[UE4]如何使用Profile-Guided Optimization (PGO)

posted in: 未分類 | 0

只要有寫過 C++ 程式的人,大都可以理解不同的構建配置(BuildConfiguration)編譯器會幫助我們進行不同程度的優化── Debug 與 Development主要是為了除錯,Test用來偵測效能表現,最後Shipping則做出最好的優化配置。──但是除了那些各式各樣的編譯參數調整之外,我們其實還可以利用事先產生「profile」的方式讓編譯器利用運行時期的資訊來做出各種更激進的優化手段,例如:縮減整個code size、 降低 if-then-else branch 的預測失誤來減少 cpu指令的執行數量, 或者是重新組織整個 code layout 以增進 cpu 指令的 cache效率。詳細可以參考Intel的線上文檔。 UE4當然也有提供這項優化功能,但目前(4.25)也只支援 PS4、 XboxOne 與Switch 這三個平台。   就我自身的經驗而言,在啟用 PGO 之後整體的 FPS 大約提升了3左右, 其中屬於CPU 部份的 Game 跟 Draw 這二條 Thread 各別降低了1.xx ms的運行成本。這效能的提升雖然不算多,但卻是在一些常規的優化手段都採取之後能夠再額外得到的效能突破。除了缺點是開發者需要多花一些時間在建置流程的設計上與編譯的等待時間之外,但帶來的卻是能夠讓我們將省下的效能預算投入在更好的遊戲表現上,何樂而不為呢?   首先讓我們先來看一下整體運行流程: 整個流程的第一步,我們必須要產生PGI build,這邊我們可以選擇使用Development 或者是 Test 組態都行, 重點是我們必須要籍助RunUAT指令(UnrealAutomation Tool),並傳入以下參數來產生正確的版本: -UbtArgs=’-LTCG-PGOProfile’  接著在C++進行linking之前出現以下訊息的話就代表指令成功: EnablingProfile Guided Instrumentation (PGI). Linking will take a while.   以下是完整的PGI Build指令: 接著我們必須利用Gauntlet(4.21推出的自動化測試框架)將剛才Local PGIBuild跑起來,但在這之前,我們必須要先設計專案所使用到的PGONode並放進/Path/To/YourProject/Build/Script/YourProject.Automation/YourProject.PGONode.cs,如下: 接著我們必須先編譯這個C#專案,並把dll的輸出路徑設定到Engine/Binaries/DotNET/AutomationScripts/下面,這樣Gauntlet才有辦法找到 YourProjectPGONode並運行起版本。C#專案編譯指令如下:   然後我們就可以運行 Gauntlet 將版本跑起來,其中參數-test=”YourProjectPGONode” 會去找到我們剛剛撰寫的 C# Script起來運行: 這邊我們可以選擇手動跑過整個遊戲流程或者是撰寫自動化 Script ,等到收集完足夠的 profile data 之後, 再手動輸入UE4的quit consolecommand讓引擎跑完正常結束遊戲的流程, Gauntlet 便會自動的將profile.profdata 放在 /Path/To/MyPorject/Build/${YourPlatform}/PGO/ 下面。   到此為止我們已經收集到建構PGO版本所需要的全部資訊,上面介紹的建置PGI版本與收集profile.profdata不需要每次做版本都執行,我們只需要定期的人工檢視C++程式碼有足夠的變動再跑這個流程即可。   接下來,我們就可以利用profile.profdata做出優化過後的PGO版本,只要在RunUAT 做 build 時,代入以下參數:-UbtArgs=’-LTCG -PGOOptimize’,他就會自動的找到 /Path/To/MyPorject/Build/${YourPlatform}/PGO/ 下的profile.profdata 進行建置,接著在C++進行linking之前出現以下訊息的話就代表指令成功: EnablingProfile Guided Optimization (PGO). Linking will take a while.   以下是完整的PGO Build指令:

[UE4] 怎麼設定tortoisegit來diff uasset

posted in: 未分類 | 0

基本上做法很簡單,就是利用UE4 Editor傳入-diff參數後,藉由tortoise git的external diff viewer來達成目的。這樣的話,我們就不需要開editor就可以呼叫ue4的diff window了。實際指令如下: ${UE4_ENGINE_ROOT}\Engine\Binaries\Win64\UE4Editor.exe -diff %base %mine 可參考下面程式碼的實作: Engine\Source\Editor\UnrealEd\Private\EditorCommandLineUtils.cpp Usage: \n\ -diff [options] left right \n\ -diff [options] remote local base result \n\ 要做 merge的話,可以使用這個指令: ${UE4_ENGINE_ROOT}\ \Engine\Binaries\Win64\UE4Editor.exe -diff %theirs %mine %base %merged

[UE4] 使用FileOrder加速遊戲的載入時間

posted in: 未分類 | 0

剛剛看到官方文件中有Ordering your .pak file的這個技巧:https://docs.unrealengine.com/en-US/Engine/Basics/Projects/Packaging 看起來可以節省不少Loading asset的時間 剛剛試了一下,這裡就做個簡單的記錄。 基本上就是在package完之後,在跑game的時候新增以下參數:-fileopenlog,然後就會產生GameOpenOrder.log這個檔案,如下圖: 產生的這個GameOpenOrder.log,把他複制到專案下的Build\WindowsNoEditor\FileOpenOrder,再打包一次就行了 記得GameOpenOrder.log要上到版控中,然後在做shipping build之前產最後一次的GameOpenOrder.log之後再打包。