[演講筆記][UnrealEngine] UE5中的渲染技術 2022台北遊戲開發者論壇

posted in: UnrealEngine, 開發日誌 | 0

花了一些時間把EpicChina在2022台北遊戲開發者論壇的演講看完並做了一些筆記。 題目是關於UE5中的渲染技術。 只是看完影片後覺得資訊量太多,腦袋呈現完全爆炸狀態……然後影片結尾聽到講者說這只是很粗淺的介紹後我就崩潰了。 雖然以下筆記很多內容我都還有點消化不良而且可能也有理解錯誤的地方,但不管怎麼樣先分享出來給需要的人。 想更進一步知道細節的推薦大家去看演講,不能只有我崩潰。 https://www.twitch.tv/videos/1533064361 1:54:37秒 開始 1. 在nanite中大小三角形會分別用軟硬體做,大的用硬體做,小的為了避開overshading的問題,他自己用compute shader寫了rasterization。為什麼呢?在三角型在算pixel的顏色時,由於頂點不會剛好在pixel的中間,因此他必須相鄰的4個pixel拉進來,用一個Quad為單位來考慮。當你的三角型很小而且緊密時,某些pixel會一直反覆被拉進去做計算,這情況稱之為Overshading或者是Quad OverDraw。而Nanite為了解決這個問題,因此大的三角型雖然還是靠硬體做,但小的三角型則是自己用軟體做,在Compute Shader中寫rasterization。小的三角型有一些條件需要達成才會被歸類為小三角型。 2. 整套技術的重點在Cluster生成、Culling跟LOD的選擇,為了高效的達成Culling跟LOD的選擇,引擎從4.22開始就把整個render底層換成retained mode,就是說讓GPU去維護整個場景render object的一些狀態。 這邊是GDC2019演講。 3. LOD的生成機制,其實就是先做graph partition演算法做cluster分組,然後不斷的遞迴減半面數,最終生成一個DAG(Directed Acyclic Graph)。Graph partition的分群的給定條件是共享的邊盡可能的少,面積盡可能的均勻。這出來的結果是每下一級LOD都會是最小的變化,也就是說外觀的變化最小。而衡量外觀變化的標準就是使用QEM(Quadric error metric)。 4. QEM這個衡量標準可以保證我們減面後的error會越來越大,從DAG的Root到Leaf Node,記錄在節點中的error值可以幫助我們做LOD的選擇。 5. 每個Cluster的BoundingBox都會再各別對每級的LOD個別生成BVH(BoudingVolumeHierarchy)後,再掛到一個大的BVH root下面,因為這樣gpu的入口會比較一致。每4個Cluster會組成一個cluster group,最終這個nanite模型會是一個cluster的BVH結構。 6. Culling會利用分群後的資訊進行,經過以下步驟: — a. Instance Culling:用上一個frame的HZB做跟BoundingBox來做剔除,得到一個粗略的可能還可見對象的instance。— b. … Continued

[開發日誌][UnrealEngine] 關於在手機上使用MallocBinned的一些研究記錄

posted in: 開發日誌 | 0

在Unreal中MallocBinned總共有3個版本:MallocBinned、MallocBinne2、MallocBinned3。這類的memory pool機制,主要的設計原則就是希望盡量的將大小相同的物件放在一塊連續的記憶體上,然後根據物件的大小,一個蘿蔔一個坑,在分配的時候再從對應的Pool中拿出空間出來使用。通常我們只會對小物件做這件事,過大的記憶體物件需求通常會直接呼OS的allocator。 例如FMallocBinned所定義的小物件是以下幾個size:16, 32, 48, 64…到32768,也就是說至多到32KB。 看了一下android的memory pool機制,預設arm64是用MallocBinned2跑,而要不要換到MallocBinned3取決於各別專案會不會用爆目標最低機型的記憶體,會的話,由於MallocBinned3有Virtual Memory,因此可以突破記憶體上限。 另外IOS預設是使用MallocBinned,因為只有他有實作Apple系統內建的的nano allocator。系統的nano allocator預設會保留512MB的pool給small object用,看起來有點像是MallocBinned2在做的事;相對於MallocBinned的Pool,它是在該size被使用到的時候再去跟OS要一大塊記憶體回來。 關於nano allocator,詳細可以看Engine\Source\Runtime\Core\Private\Apple\ApplePlatformMemory.cpp中NanoMallocInit 的註解。以下是Copy出來的註解: 可以想見能在OS層級做掉的機制,當然會比FMallocBinned2引擎自己實作的還要高效,因此也不難理解iOS會選擇使用FMallocBinned來搭配。 IOS要換到MallocBinned3是不可能的,因為根本就沒有實作這條路,我有試著硬開出來,但會直接crash,微稍深入研究了一下Apple官方文檔,裡面有提到,雖然Mac相關VirtualMemory的功能都是正常的,但是我們只能page-in但不能page-out到硬碟上,不排除未來官方未來會在iOS上支援swap memory,可以觀察每年WWDC看看有沒有提到相關的議題,但現況就是iOS不支援MallocBinned3。另外有發現蘋果在發表iPadOS16的時候,有提到支援Virtual memory swap這個feature,這表示現況我們是能在iPad上使用MacllocBinned3的(待測)。 Apple官方文檔 iPadOS16 Feature介紹 只是為什麼iOS不支援MallocBinned3而Android支援? 我想是為了延長手機的生命週期吧。 由於MallocBinned3是依賴於VirtualMemory的技術,他會需要大量進行PageOut/PageIn的動作,但由於手機系統大多是使用FlashMemory,其在設計上有寫入次數的限制,寫入次數越多,其跟記憶體之間的資料交換會越不穩定。 因此iOS只支援PageIn而不支援PageOut的根本原因在此,為了維持系統的穩定度,減少硬碟的寫入次數,因此Apple禁止了PageOut這條路;由於不支援PageOut,因此我們就沒辦法使用MallocBinned3。 這個限制其實在Android系統上也是存在,只是該限制只存在於boot disk上,其他另外的secondary storage,例如SD card則沒有這種限制,所以Android還是可以正常的使用MallocBinned3。

[UnrealEngine][開發日誌] CommonUI plugin 筆記與 Plugin ( HorizonFramework ) 的設計思路

posted in: UnrealEngine, 開發日誌 | 0

關於Plugin的官方介紹: https://docs.unrealengine.com/5.0/en-US/common-ui-plugin-for-advanced-user-interfaces-in-unreal-engine/ 本篇文章主要是記錄目前我在CommonUI plugin中所看到的一些東西,可能分析的不是很完整,但就是一個簡單的筆記。 版本:Unreal 5.0.3 目前plugin裡面主要有以下幾個功能: 各別平台的Input以及 Icon Mapping:例如xbox跟switch的A跟B是相反的,他有機制處理輸入跟icon的對應。 Styling: 例如CommonText可以做一個Style BP給大家用,之後調整遊戲文字就只需要調該style BP就行了 從GameViewportClient上做Input Rereroute,根據目前是ECommonInputMode::Menu、Game或All決定裡面的行為。在功能面上看起來跟InputMode在機制上類似,Menu對應UIOnly、Game對應GameOnly而All對應GameAndUI,不確定之後官方會想怎麼發展。 UI Manager的機制。 我自己看是覺得整套機制還不是很成熟,在使用上你會覺得缺少很多細節的實作。不過也對,他現在是放在experimental plugin下面,要使用在production上要有心理準備自己需要去補完很多缺少的實作。 關於UI Manager,他實現的機制比較特別,我自己的Plugin是用Actor,但他是拿你第一個AddToViewport的主Widget來當管理器,這時候你會需要在這個Widget上再刻一套需要的功能介面好讓其他系統呼叫。 整套機制主要分為2個元素構成: Widget Container容器:分為UCommonActivatableWidgetStack跟UCommonActivatableWidgetQueue,分別可以進行push跟pop操作,這二個容器的差別在於我們的設計是先進先出或後進先出。我們可以有多個Stack分別對應不同的需求,在Lyra中他是把Game跟Popup Modal拆成二個stack ActivatableWidget:他是一個UserWidget,我們需要繼承下來進行設計的各種UI頁面,用來push進Widget Container的元素。。 他Push跟Pop時內建transition動畫,但他動畫效果是寫死在code中,而不是去呼叫UMG上的動畫,所以不是很符合實際產品製作的需求。內建的Transition動畫效果有:FadeOnly、Horizontal、Vertical、Zoom (ECommonSwitcherTransition)。 由於我以前有解決過UI Manager相關的問題,因此就再來多來聊聊我Plugin的設計思路吧。 以下是我Plugin的連結: Horizon Framework 這個Plugin當初設計的目的是想要做一套通用的遊戲框架(GameFramework),並做為HorizonDialogue擴建的基礎。只是做到後來發現通用框架難以在一個Plugin裡面實現出來,所以目前就變成了專注在UIManager相關的功能上面。 我的UI管理器做叫HorizonSceneManager,而用來進行Push跟Pop的元素叫做HorizonScene,Scene這個名稱是借鏡於cocos2d,他們都繼承自Actor,因此都會在World上生成一份實體。HorizonScene是用來封裝UserWidget,因此我們在設計完UserWidget之後,需要另外創建一份BP繼承自HorizonScene,並把UserWidget的Class指派進Scene的參數中。如下圖: SceneManager裡面有一份SceneStack跟SceneEventList,裡面有設計一系統的API讓我們可以針對SceneStack進行操作,幾個重要的API為:ChangeScene、PushScene、PopScene、RemoveScene。這幾個Function會建立對應的SceneEvent放進SceneEventList中,SceneManager會在每個Tick的時候逐一拿出來執行,並在適當的時機點通知SceneManager,並把Scene加入進SceneStack中、呼叫對應的delegate通知使用者實作需要的遊戲邏輯。 每個Scene主要會經過以下幾個生命週期: Enter:視是不是處於VR模式決定初始化Widget的方式,並呼叫StartTransIn。 … Continued

[UnrealEngine][開發日誌] 關於打包最後會出現的Waiting for child processes to complete訊息

posted in: UnrealEngine, 開發日誌 | 0

分享一下之前專案遇到的一個打包速度優化手段。 之前專案在打包的時候,最後一定出現Waiting for child processes to complete(0/1)這個訊息,當專案越變越大之後,很明顯的卡在這一步的時間變長了。追了一下code,發現他是在做把東西打包進pak的動作,每一個pak只會開一條thread處理,而訊息中的(0/1),則表示了我們把所有的內容塞進了同一個pak中(completed/numPak);也就是說若我們沒有啟用Chunk機制的話 (見:https://docs.unrealengine.com/4.27/en-US/SharingAndReleasing/Patching/GeneralPatching/CookingAndChunking/ ),這邊會浪費不少時間在做單核cpu的等待,之前試過,隨便分個8個chunk,就讓整體打包時間加快大約20分鐘左右。 通常會關注到chunk議題,很多時候是因為在做手遊時需要去分第一包跟做patch;但其實分chunk之後帶來的好處還蠻多的,除了打包速度加快之外,我們還可以利用UAssetManager來做Preload相關的機制。 關於打包的細節有興趣可以去看UnrealEngine\Engine\Source\Programs\AutomationTool\Scripts\CopyBuildToStagingDirectory.Automation.cs中的RunUnrealPakInParallel這個function。

[UnrealEngine][開發日誌] Shadre pipeline:Usage Flags vs ShadingModel

posted in: UnrealEngine, 開發日誌 | 1

UsageFlag文檔:https://docs.unrealengine.com/4.27/en-US/RenderingAndGraphics/Materials/MaterialProperties/ ShadingModel文檔: https://docs.unrealengine.com/4.27/en-US/RenderingAndGraphics/Materials/MaterialProperties/LightingModels/ 本文章是基於4.27 之前有空想要比對一下mobile pass跟SM5 pass到底差在哪裡。有發現在windows editor中,只有SM5跟ES3二種,就算是選了蘋果icon也不是真的metal,真正的metal shader code只能在mac環境下編譯出來。 在追code的過程中相關些名詞一直出現,引起了我的好奇,因此決定仔細研究一下。想要追shader相關的程式碼,這幾個ini參數最好設一下會比較好debug: 在material上有usage跟shading model的選項,usage主要的用途是用來決定哪些shader組合是不是要compile permutation;ShadingModel主要是用來決定計算光源貢獻時要走哪條IntegrateBxDF的路。 在Unreal中shader code的generation主要是靠C++ Template的機制來大量產生各種組合(permutation),其中可以看到引擎大量的使用Policy-Based Design。我們知道template的機制是compile time產code,所以我們只要有n個base shader,再加上m種policy的話,我們就可以產生n*m種組合。 每個Policy都會實作ShouldCompilePermutation跟ModifyCompilationEnvironment這二個function;前者搭配Usage使用,替BaseShaderType決定要搭配哪些Policy組合的編譯,例如,NoLightMapPolicy,接著實作會呼叫ModifyCompilationEnvironment,他會替Shader加macro,決定某段shader code要不要留下來。 以bIsUsedWithStaticLighting為例,若該usage有勾選的話,在跑IMPLEMENT_SHADER_TYPE時,其相關連到一組的BasePassVertexShader.usf、BasePassTessellationShaders.usf以及BasePassPixelShader.usf(Vertex Shader, Hull and Domain Shader, Pixel Shader)就會被用搭配到的TLightMapPolicy建立出來。 例如在${PROJECT_ROOT}/Saved/ShaderDebugInfo/PCD3D_SM5/AmbientOcclusion/FLocalVertexFactory可以看到類似於下面中的資料夾。其中產生的permutation shader code,針對BasePassVertexShader.usf,產生NoLightMapPolicy以及NoLightMapPolicyAtmosphericFog這二組;針對BasePassPixelShader.usf,則產生了FNoLightMapPolicy跟FNoLightMapPolicySkylight。 這些Policy主要的功能是用來ModifyCompilationEnvironment,替shader code加入需要的macro define,之後每個permutation就會利用mcpp這個preprocess來幫我們濾掉不需要code,並把所有include到的shader結合成一個大檔之後,產生特製的shader code出來。 另外Usage除了決定permutation要不要產生出來外,有一些還會用來附加一些macro進shader中,例如OutEnvironment.SetDefine(TEXT(“USES_DISTORTION”), Material->IsDistorted()); 有發現Usage雖然有八成左右的功能都只是用來決定編譯組合;ShadingModel主要是用來決定計算光源貢獻的做法。但有看到有少部份會直接去加shader … Continued

[UnrealEngine][開發日誌] Shadre pipeline optimization: 關於VerifyGlobalShaders的實驗結果令我非常震驚

posted in: UnrealEngine, 開發日誌 | 0

之前在debug android的時候,有發現一開始的黑畫面特別久,特別在用Visual Studio attach debuger的時候,基本上第一畫面完全跑不出來。好奇追了一下,發現了整個流程是卡在VerifyGlobalShaders這個function上,仔細看了裡面,發現在package build的時候他完全沒做任何事,就只是把所有的shader的permutation撈出來for loop跑過一輪檢查而已,這個foreach的執行次數在4.27有8萬多個(85610),看起來非常浪費計算量,就算是跳過也不會影響後面的功能,說不定跳過之後能夠省個幾秒鐘的黑畫面時間。試了一下,這個想法是對的,跳過之後確實後面的畫面也能夠正常顯示出來,讓我的debugger能夠順利的往後執行。 但這時候我就好奇了,為什麼引擎要放一個明顯無用的ForLoop在那裡?這段code通常只會在Development build執行吧?為什麼連Shipping build也要跑?把他跳過之後我們能夠省下多少時間? 為了驗證這個更動能夠帶來更好的遊戲體驗,因此我決定計時看看跳過VerifyGlobalShaders 跟原本不跳過的版本差異在哪。使用的版本是Test並啟用了PGO優化。 但是,結果讓我非常的震驚,數據如下。 機型:Galaxy S21 5G SM-G9910   Skip NoSkip 第一次開啟 12.52秒 7.9秒 第二次開啟 1.87秒 1.66秒 機型:Galaxy S21 ultra 5G   Skip NoSkip 第一次開啟 10.28秒 7.98秒 第二次開啟 2.13秒 1.71秒 這個驚人反直覺的結果,讓我開始思考到底發生了什麼事。為什麼跳過無用的Loop,其執行結果反而比沒跳過的慢?這之間是不是有什麼硬體上的預熱效果在裡頭發揮了作用?思考到這裡,我突然想到,所有的CPU都有所謂的L1、L2 … Continued

[UE4][開發日誌]使用nuget來發佈遊戲專案中的UE4Editor dll

posted in: UnrealEngine, 開發日誌 | 0

這幾天花了一此時間研究了nuget的佈署方式,主要的目的,便是要讓大家在還真的購買plugin之前,有個機會先能夠在editor中進行試用。 雖然perforce使用者有[UGS](https://docs.unrealengine.com/en-us/Programming/Deployment/Unreal-Game-Sync/Unreal-Game-Sync-Overview)可以用,但由於跟perforce綁定,對於習慣於用git的自己而言還是要去思考別的替代方案。 另外,nuget好處是,開放給大家的部份可以發到nuget.org上,如果公司內部要私用的話,也是可以自己架一個nuget server或者是直接弄一個local feed在NAS上,就使用彈性而言,我個人覺得比UGS來得高。 目前我正在開發的幾個plugin都也已經發佈到nuget.org開發大家下載了,有興趣的人可以試試看,使用方法跟步驟已經記錄在對應的Github頁面中了。 plugin marketplace: https://www.unrealengine.com/marketplace/profile/horizon-studioGithub專案連結:https://github.com/dorgonman/HorizonUIPluginDemohttps://github.com/dorgonman/HorizonTweenDemohttps://github.com/dorgonman/HorizonFrameworkDemohttps://github.com/dorgonman/HorizonVRDemohttps://github.com/dorgonman/HorizonDialogueDemo 另外由於我這幾個專案都有進Azure Devops的build pipeline了,理論上只要我這邊有任何更新,都會自動發佈到nuget.org上。

[UE4]UE4靈異現象之不管怎麼算都多出了一個人……

posted in: UnrealEngine | 0

(本文中的故事情節純屬虛構,如有雷同……的話?) UE4用久了,遇過的怪事不會少只會多。 只是,這次的事件卻令我感到毛骨悚然。 事情該從哪邊開始講起呢?對了,就從功能面的設計面開始吧。 首先,由於我正在製作一個網路對戰型的遊戲,因此希望在其他玩家以「Spectator(觀眾)」的身份進到遊戲的時候,能夠即時讓對戰中的玩家知道目前場上有多少觀眾正在觀賽。 而要決定Client端要不要以「Spectator」的方式登入的方法非常簡單,基本上就是以下二種方法: 在OpenLevel的時候帶入參數?SpectatorOnly=1。 覆寫AGameModeBase::MustSpectate這個方法,並根據我們遊戲設計的邏輯決定是否回傳true。(例如比賽正在進行中的話,後面就都只能以spectator的方式加入) 這個階段我只是想要確spectator的計數功能是否正常,因此我的想法非常單純: 當Client上的SpectatorPawn成功進到遊戲的時候(觸發PossessedBy)的時候送一個RPC給Server。 由於我已經知道SpectatorPawn本身只存在於Client端,所以他本身是沒有連線功能的。 為了成功將RPC送出,因此必須要藉由PlayerController的連線通道: https://gist.github.com/dorgonman/9b62afabd399b7fbc855b64ee3e08757   然後在PlayerController上的實作如下: https://gist.github.com/dorgonman/f9b41c58339b2ffa68773a13ab2d966e   在Server收到SpectatorReady通知之後,馬上去增加AMyGameModeBase::NumReadySpectator的數量並印出來 在編譯完成按下Play之後,可以在Log上發現這段訊息: LogTemp: NumReadySpectator: 1 嗯,很好!SpectatorPawn的登入通知很順利的傳送給Server了!   正當我滿足於現在的實作,準備進行下一階段功能的設計時,我突然看到了現在MyGameModeBase::MustSpectate中的實作: https://gist.github.com/dorgonman/bdd1380a8acd64615e6ee913ad2c0669 「有一個觀眾?」眉頭一皺,我感覺到那邊似乎不太對勁。 理論上的MustSpectate回傳都是false的話,那麼NumReadySpectator應該都會是0才是, 而且,pPC->Server_SpectatorReady()應該是完全不會被呼叫到才是。 於是,我馬上把視線移向了editor中的WorldOutliner,想看看計數器中的Spectator到底哪來的:   一看了之後我嚇出了一身冷汗──這個世界中並沒有任何「SpectatorPawn」存在。 就我的理解,World Outliner應該會顯示目前世界上所有存活著的Actor才是, 難不成這個世界還存在著一個看不見的Spectator默默的觀察著玩家? 為了查證SpectatorPawn到底是被誰生出來的,於是我便在Constructor下了斷點──果然, 世界就這麼停留在Spectator被生成的那個瞬間,循著callstack往上追蹤,我發現了一個線索: 當Client端從Server那邊收到SpectatorClass之後,便會觸發PlayerController中的ReceivedSpectatorClass()把SpectatorPawn生成出來。 我感覺更加困惑了:每個PlayerController在收到Server的通知之後都會生成SpectatorPawn出來? 這不是我所認識的UE4阿!證據是,World … Continued

[UE4]網路同步之Character謀殺事件

posted in: UnrealEngine | 0

事情是這樣的。 本來我是想要測一下network做replication變數相關的功能,因此建了一個空白的level(全黑色那張),並做了一個Character後加了幾個變數與寫了幾個function來進行實驗。在按下play之後……嗯,一開始變數成功的從server傳過去給client了(開了二個player,一個listen server,一個client)。很好!就在我滿足於事情正如想像中進展的時候,突然間,變數卻完全送不過去給Client了! 到底發生了什麼事?就我的理解上,我用的是COND_None,只要server有任何變動的話,變數應該會送給client阿?難不然變數是unreliable的?不對阿,replication變數肯定都是reliable,頂多就只有因為頻寬的問題而延遲送達而已。經過我的反覆實驗,最後發現了一件驚人的事實: 我listen server上的character居然被砍掉了! 在得知這項資訊後,我決定馬上到AMyCharacter::EndPlay這個function中下斷點──我想想看看到底是那個混蛋砍了我的character。可惜的是,我依然沒有抓到兇手。──AMyCharacter::EndPlay是被呼叫了沒錯,可是EndPlayReason只寫了一行Destroyed。這資訊完全沒有幫助,因此我決定循著call stack往上層追,最後看到了這段程式碼: https://gist.github.com/dorgonman/00c3c173f3a70fc040d88d00915e3323 WTF!?網路收到了斷線通知之後,就馬上把我的角色砍了!?為什麼listen server上所擁有的Character會被斷線?我並沒有加入任何砍Actor的邏輯阿?難道是因為自動觸發GC的緣故?也不對阿,Actor的生命週期應該是跟著world才是,要砍Actor的話只能手動呼叫Destroy(K2_DestroyActor)才行。我完全被眼前的事實驚呆了,這跟我過去對於UE4相關的知識完全不相符。 「這樣不行,一定要找出原因。」──雖然感受到了挫折,但我才不會因為這點小事就被擊敗。為了找出被斷線的原因,我決定利用手邊僅剩唯一的蛛絲馬跡「Bunch.bClose」來找出殺死Character的兇手是誰。事出必有因,既然Bunch.bClose這個flag是true,那必然是有那段邏輯使然。在搜遍了整個引擎之後,總算發現下面這段非常可疑的程式碼: https://gist.github.com/dorgonman/5f021b9eac330493d3f012bfda9a8d99 當Channel->Close();被呼叫之後,就會送出一個CloseBunch出來並把我的Character殺死。而為什麼會進到這段邏輯的根本原因,在於bIsRecentlyRelevant是false,意即,listen server認為這個Character已經跟自己沒有任何瓜葛了,所以就殺了他。 到這裡,似乎就已經破案了,原來人是Server自己殺掉的阿…… 但李組長眉頭一皺,事情似乎並不是這麼樣的單純。為什麼listen server會無緣無殺的去殺掉自己的親生兒子?難不成二兒子就那麼該死嗎(PlayerController2)?大兒子(PlayerController1,listen server)不是還活蹦亂跳的好好的在那邊嗎?為什麼就只有二兒子!?而且是非常固定,每次在世界被建構起來約10秒鐘之後,這件兇殺案就會必然的發生。不管是我模擬了幾十次還是幾百次,都無法逃離二兒子死亡的結局。 這世界上肯定是哪裡有Bug了!──我的心底對著Epic大神這麼叫囂著。 這麼顯而易見的Bug,不可能只有我才會遇到。於是我轉而求向古歌大神的協助……然而,卻還是一無所獲。沒辦法,只好靠著新發現的事實,繼續的往上追蹤。目前我們知道,當Listen Server決定要跟二兒子斷絕關係的時候,bIsRecentlyRelevant就會被設成false,那麼我們只要找到設置bIsRecentlyRelevant這個flag的源頭,就必然有辦法找出這件兇殺案背後發生的原因。 殺人兇手,是身為父親的Listen Server。──這點已經不容質疑。 但事實的背後往往有著更令人訝異的真實。只要努力不懈的話,真理永遠就只會有一個。是的,最後我終於到達了,那扇真理之門的面前: https://gist.github.com/dorgonman/baba67405e381f52f35f7112c7b610a7 看到這段程式碼之後,我馬上理解了整個案情的來龍去脈。為了證明事實真的如同我所想像的,我馬上再次開啟了最後一次世界的模擬: 「阿阿阿阿阿阿阿──────!」我彷彿聽到了慘叫聲。 二兒子Z軸的數字正以著不可思議的速度往下遞減──在重力加速度的加成下,二兒子快速的離大兒子遠去,然後在到達一定的臨界值(NetCullDistanceSquared)之後,就這麼消失在世界的盡頭。 什麼嘛,原來是摔死的。 至此,一切的謎題都已經解開了。 想要避免這件慘絕人寰的兇殺案,大致上有以下幾個解決方案: 將CharacterMovement中的Default Land Movement Mode 設成Flying:這樣你的角色就不會掉下去了。 將Character中的Always Relevant這個選項打勾:但是你的Character還是會無限的往下進行自由落體的動作,只是,這個flag是有其必要性,它在多人連線遊戲下可以減少不少網路頻寬,在調整這個設置前,最好對這個flag有正確的認識。 下面放個Cube:這樣Character就有地方可以站。

[UE4] 如何使用FASTBuild來做分散式編譯

posted in: UnrealEngine | 0

首先這套工具只適用於從Github上抓引擎源碼下來編譯的情境,Launcher版本目前(4.19)並不支援 下面這個repository是我調整過後並整合進UE4的版本,可以直接clone下來使用: https://github.com/dorgonman/UnrealEngine/tree/dorgon/develop-4.19 原作者的版本:https://github.com/liamkf/Unreal_FASTBuild  (用這個版本的話,照著他的說明做整合就行了) 要使用這個工具必須要注意以下幾個點:  在安裝Visual Studio 2017的時候,要記得把vc++2017 version 15.5 v14.12勾起來 設定以下幾個環境變數: FASTBUILD_BROKERAGE_PATH \\192.168.100.2\workspace\fastbuild.brokerage  (讓所有的電腦知道那邊有build worker可以用) FASTBUILD_CACHE_PATH     \\192.168.100.2\workspace\UE_FASTBuild_Cache (用來做cache路徑) 要注意的是上面二個路徑要讓所有參與編譯的電腦都有讀寫的權限(我自己是在NAS上開公用資料夾) 在所有要參與分散式編譯的電腦上執行Extras\ThirdPartyNotUE\FASTBuild\Win64\FBuildWorker.exe 打開之後,在做build的時候就可以發現遠端已經有同伴在幫我們編譯了! 我們可以在visual studio的build log看到以下的訊息: 接著將Extras\ThirdPartyNotUE\FASTBuild\Win64\FBuildWorker.exe建立一個捷徑,貼到${UserName}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup之後, 每次電腦開機的時候就會自動執行了。 這套軟體對於沒經費購買Incredibuild的團隊算是一個不錯的解決方案~