国产精品va,亚洲免费视频网,日本久久久久久久久久,91啪在线,呦女专区,日韩欧美在线播放,国产精品资源在线

服務(wù)熱線熱線:

13818745178

九游(ninegame)娛樂中國官方網(wǎng)頁-企業(yè)入口

企業(yè)動態(tài)

關(guān)于unreal-engine4:Exploring--UE4Unreal回放系統(tǒng)剖析上

發(fā)布時間:2025-05-10點(diǎn)擊次數(shù):

  回放,是電子游戲中一項(xiàng)常見的性能,用于記錄整個較量過程或者展現(xiàn)游戲中的精彩霎時。通過回放,咱們能夠觀摩高手之間的對決,享受游戲中的精彩霎時,甚至還能夠拿到敵方玩家的較量錄像進(jìn)行剖析和學(xué)習(xí)。

  FIFA / 實(shí)況足球 / NBA2K / 守望先鋒 / 極限競速:地平線 / 跑跑卡丁車

  守望先鋒 / CSGO / Dota / LOL / 魔獸爭霸 / 星際爭霸 / 紅色警戒 / 坦克世界 / 絕地求生 / 王者光榮

  早在 20 世紀(jì) 90 年代,回放零碎就曾經(jīng)誕生并寬泛用于即時戰(zhàn)略、第一人稱射擊以及體育競技等類型的游戲當(dāng)中,而那時存儲器的容量十分無限,遠(yuǎn)遠(yuǎn)無奈與當(dāng)今動輒幾十 T 的硬盤等量齊觀,面對一場數(shù)十分鐘的較量,較量數(shù)據(jù)該如何存儲和播放?回放該如何實(shí)現(xiàn)?這篇文章會通過分析 UE 的回放零碎,來由淺入深地幫忙大家了解其中的原理和細(xì)節(jié)。

  盡管不同游戲里回放零碎具體的實(shí)現(xiàn)形式與利用場景不同,但實(shí)質(zhì)上都是對數(shù)據(jù)的記錄和重現(xiàn),這個過程與網(wǎng)絡(luò)游戲外面的同步技術(shù)十分類似。舉個例子,如果 AB 兩個客戶端進(jìn)行 P2P 的連貫對戰(zhàn),A 客戶端上開始時并沒有對于 B 的任何信息。當(dāng)建設(shè)連貫后,B 開始把本人的相干信息(坐標(biāo),模型,大小)發(fā)給 A,A 在本人的客戶端上利用這個信息從新構(gòu)建了 B,實(shí)現(xiàn)了數(shù)據(jù)的同步。

  思考一下,如果 B 不把這個信息發(fā)給 A,而發(fā)給本人進(jìn)行解決,是不是就相當(dāng)于錄制了本人的機(jī)器上的較量信息再進(jìn)行回放呢?

  沒錯,網(wǎng)絡(luò)游戲中的同步信息正是回放零碎中的錄制信息,因而網(wǎng)絡(luò)同步就是實(shí)現(xiàn)回放零碎的技術(shù)根底!

  在正式介紹回放零碎前,無妨先概括地介紹一下游戲開發(fā)中的網(wǎng)絡(luò)同步技術(shù)。咱們常說網(wǎng)絡(luò)同步能夠簡略分為幀同步、快照同步和狀態(tài)同步

  ,對應(yīng)的英文概念是 LockStep/Deterministic Lockstep。其基本思路是每固定距離(如 0.02 秒)對玩家的行為進(jìn)行一次采樣失去一個“Input 指令”并發(fā)送給其余所有玩家,每個玩家都緩存來自其余所有玩家的“Input 指令”,當(dāng)某個玩家收到所有其余玩家的“Input 指令”后,他的本地游戲狀態(tài)才會推動到下一幀。

  ,能夠翻譯成 Snapshot Synchronization。其思維是服務(wù)器把以后這幀整個游戲世界的狀態(tài)進(jìn)行一個備份,而后把這個備份發(fā)送給所有客戶端,客戶端依照這個備份對本人的世界狀態(tài)進(jìn)行批改和糾正進(jìn)而實(shí)現(xiàn)同步。(快照,對應(yīng)的英文概念是 SnapShot,強(qiáng)調(diào)的是某一時刻的數(shù)據(jù)狀態(tài)或者備份。從游戲世界的角度了解,快照就是整個世界所有的狀態(tài)信息,包含對象的數(shù)量、對象的屬性、地位線信息等。從每個對象的角度了解,快照就是指整個對象的各種屬性,比方生命值、速度這些。所以,不同場景下快照所指的內(nèi)容可能是不同的。)

  ,能夠翻譯成 State(State Based)Synchronization。其思維與快照同步類似,也是服務(wù)器將世界的狀態(tài)同步給客戶端。但不同的是狀態(tài)同步的粒度變得十分?。ㄒ詫ο蠡蛘邔ο蟮膶傩詾閱挝唬?wù)器不須要把一幀外面所有的對象狀態(tài)進(jìn)行保留和同步,只須要把客戶端須要的那些對象以及須要的屬性進(jìn)行保留和發(fā)送即可。

  拓展:快照同步其實(shí)是狀態(tài)同步的前身,那時候整個游戲須要記錄的數(shù)據(jù)量還不是很大,人們也天然的應(yīng)用快照來代表整個世界在某一時刻的狀態(tài),通過定時地同步整個世界的快照就能夠做到完滿的網(wǎng)絡(luò)同步。然而這種間接把整個世界的狀態(tài)進(jìn)行同步的過程是很消耗流量和性能的,思考到對象的數(shù)據(jù)是逐漸產(chǎn)生變動的,咱們能夠只記錄發(fā)生變化的那些數(shù)據(jù),所以就有了基于 delta 的快照同步。更進(jìn)一步的,咱們能夠把整個世界拆分一下,每一幀只針對須要的對象進(jìn)行 delta 的同步,這樣就齊全將各個對象的同步拆分開來,再聯(lián)合一些過濾能夠進(jìn)一步縮小沒必要的數(shù)據(jù)同步,最初造成了狀態(tài)同步的計(jì)劃。更多對于網(wǎng)絡(luò)同步技術(shù)的倒退和細(xì)節(jié)能夠參考我的文章——《細(xì)談網(wǎng)絡(luò)同步在游戲歷史中的倒退變動》。

  在空幻引擎外面,默認(rèn)實(shí)現(xiàn)的是一套絕對欠缺的狀態(tài)同步計(jì)劃,場景外面的每個對象都稱為一個 Actor,每個 Actor 都能夠獨(dú)自設(shè)置是否進(jìn)行同步(Actor 身上還能夠掛 N 個組件,也能夠進(jìn)行同步),Actor 某一時刻的標(biāo)記 Replicated 屬性就是所謂的狀態(tài)信息。服務(wù)器在每幀 Tick 的時候,會去判斷哪些 Actor 應(yīng)該同步給哪些客戶端,哪些屬性須要進(jìn)行同步,而后對立序列化成二進(jìn)制(能夠了解為一個以后世界狀態(tài)的增量快照)發(fā)給對應(yīng)的客戶端,客戶端在收到后還能夠調(diào)用回調(diào)函數(shù)進(jìn)一步解決。這種通信形式咱們稱為屬性同步。

  此外,UE 外面還有另一種通信形式叫 RPC,能夠像調(diào)用本地函數(shù)那樣來調(diào)用遠(yuǎn)端的函數(shù)。RPC 罕用于做一些跨端的事件告訴,盡管并不嚴(yán)格屬于傳統(tǒng)意義上狀態(tài)同步的領(lǐng)域,但也是 UE 網(wǎng)絡(luò)同步外面不可短少的一環(huán)。

  網(wǎng)絡(luò)驅(qū)動治理,封裝了同步 Actor 的基本操作,還包含初始化客戶端與服務(wù)器的連貫,建設(shè)屬性同步記錄表,解決 RPC 函數(shù),創(chuàng)立 Socket,構(gòu)建并治理 Connection 信息,接管數(shù)據(jù)包等等基本操作。

  示意一個網(wǎng)絡(luò)連接。服務(wù)器上,一個客戶端到一個服務(wù)器的一個連貫叫一個 ClientConnection。在客戶端上,一個服務(wù)器到一個客戶端的連貫叫一個 ServerConnection。

  數(shù)據(jù)通道,每一個通道只負(fù)責(zé)替換某一個特定類型特定實(shí)例的數(shù)據(jù)信息。比方一個 ActorChannel 只負(fù)責(zé)解決對應(yīng) Actor 自身相干信息的同步,包含本身的同步以及子組件、屬性的同步、RPC 調(diào)用等。

  聯(lián)合咱們后面提到的網(wǎng)絡(luò)同步技術(shù),如果咱們當(dāng)初想在游戲外面錄制一場較量要怎么做呢?是不是像快照同步一樣把每幀的狀態(tài)數(shù)據(jù)記錄下來,而后播放的時候再去讀取這些數(shù)據(jù)呢?沒錯!利用網(wǎng)絡(luò)同步的思維,把游戲自身當(dāng)成一個服務(wù)器,游戲內(nèi)容當(dāng)成同步數(shù)據(jù)進(jìn)行錄制存儲即可。

  當(dāng)然對于幀同步來說,咱們并不會去記錄不同時刻世界的狀態(tài)信息,而是把關(guān)注點(diǎn)放在了玩家的行為指令上(Input 隊(duì)列)。幀同步會默認(rèn)各個客戶端的初始狀態(tài)完全一致,只有保障同一時刻每個指令的雷同,那么客戶端上整個游戲世界的推動和體現(xiàn)也應(yīng)該是齊全一樣的(須要解決浮點(diǎn)數(shù)精度、隨機(jī)數(shù)一致性問題等)。因?yàn)橹豁氁涗浲婕业男袨閿?shù)據(jù),所以一旦幀同步的框架實(shí)現(xiàn),其回放零碎的實(shí)現(xiàn)是十分不便和輕量化的。

  錄制:就像服務(wù)器網(wǎng)絡(luò)同步一樣,每幀去記錄所有對象(Actor)的狀態(tài)信息,而后通過序列化的形式寫到一個緩存外面。

  在 EpicLancher 外面引擎(我應(yīng)用的是 4.26 版本),創(chuàng)立一個第三人稱的模板工程命名為 MyTestRec;

  與數(shù)據(jù)的存儲流程相同,當(dāng)咱們通過 PlayReplay 開始播放回放時,須要先從對應(yīng)的 NetworkReplayStreamer 外面取出回放數(shù)據(jù),而后解析成 FQueuedDemoPacket 數(shù)組。隨后每幀在 TickDemoPlayback 依據(jù) Packet 外面的工夫戳繼續(xù)一直地進(jìn)行反序列化來復(fù)原場景外面的對象。到這里,咱們曾經(jīng)整頓出了錄制和回放的大抵流程和入口地位。但為了能循序漸進(jìn)地分析回放零碎,我還成心暗藏了很多細(xì)節(jié),比如說 NetworkReplayStreamer 外面是如何存儲回放數(shù)據(jù)的?回放零碎如何做到從指定工夫開始播放的?想弄清這些問題就不得不進(jìn)一步剖析回放相干的數(shù)據(jù)結(jié)構(gòu)與組織思維。

  無論通過哪種形式實(shí)現(xiàn)回放都肯定會波及到快進(jìn)、暫停、跳轉(zhuǎn)等相似的性能。然而,咱們目前應(yīng)用的形式并不能很好地反對跳轉(zhuǎn),次要問題在于空幻引擎默認(rèn)應(yīng)用增量式的狀態(tài)同步,任何一刻的狀態(tài)數(shù)據(jù)都是后面所有狀態(tài)同步數(shù)據(jù)的疊加,必須從最開始播放能力保障不失落掉兩頭的任何一個數(shù)據(jù)包。比方下圖的例子,如果我想從第 20 秒開始播放并且從第 5 個數(shù)據(jù)包開始加載,那么肯定會失落 Actor1 的創(chuàng)立與移動信息。

  數(shù)據(jù)流在錄制的時候兩頭是沒有明確宰割的,也就是所有的序列化數(shù)據(jù)都嚴(yán)密地連貫在一起的,無奈進(jìn)行拆分,只能從頭開始一點(diǎn)點(diǎn)讀取并反序列化解析。兩頭哪怕丟了一個字節(jié)的數(shù)據(jù)都可能造成前面的數(shù)據(jù)解析亂掉。

  存檔點(diǎn),即一個殘缺的世界快照(相似單機(jī)游戲中的存檔),通過這個快照能夠齊全回復(fù)過后的游戲狀態(tài)。每隔一段時間(比方 30s)存儲一個 Checkpoint。

  一段間斷工夫的數(shù)據(jù)流,存儲著從上一個 Checkpoint 到以后的所有序列化錄制數(shù)據(jù)。

  通過這種形式,咱們在任何時刻都能夠找到一個鄰近的全局快照(Checkpoint)并進(jìn)行加載,而后再依據(jù)最終目標(biāo)的工夫疾速地讀取后續(xù)的 Stream 信息來實(shí)現(xiàn)目標(biāo)地位的跳轉(zhuǎn)。拿后面的案例來說,因?yàn)槲耶?dāng)初在 20s 的時候能夠通過 Checkpoint 的加載而失去后面 Actor1 在以后的狀態(tài),所以能夠完滿地實(shí)現(xiàn)跳轉(zhuǎn)性能。在理論錄制的時候,ReplayHelper 的 FQueuedDemoPacket 其實(shí)有兩個,別離用于存儲 Stream 和 Checkpoint。

  每次回放開始前咱們都能夠傳入一個參數(shù)用來指定跳轉(zhuǎn)的工夫點(diǎn),隨后就會開啟一個 FPendingTaskHelper 的工作,依據(jù)指標(biāo)工夫找到后面最靠近的快照,并通過 UDemoNetDriver:: LoadCheckpoint 函數(shù)來反序列化復(fù)原場景對象數(shù)據(jù)(這一步實(shí)現(xiàn) Checkpoint 的加載)。

  如果指標(biāo)工夫比快照的工夫要大,則須要在 ConditionallyReadDemoFrameInto PlaybackPackets 疾速地把這段時間差的數(shù)據(jù)包全副讀出來并進(jìn)行解決,默認(rèn)狀況下在一幀內(nèi)實(shí)現(xiàn),所以玩家并無感知(數(shù)據(jù)流太大的話會造成卡頓,能夠思考分幀)。

  Archive 能夠翻譯成檔案,在空幻外面是用來存儲序列化數(shù)據(jù)的類。其中 FArchive 是數(shù)據(jù)存儲的基類,封裝了一些序列化 / 反序列化等操作的接口。咱們能夠通過繼承 FArchive 來實(shí)現(xiàn)自定義的序列化操作。

  那這兩個 Archive 具體是如何存儲和保護(hù)的呢?為了能有一個直觀的展現(xiàn),倡議大家先去依照 2.3 小結(jié)的形式去操作一下,而后就能夠在你工程下 /Saved/Demo/ 門路下失去一個回放的文件。這個文件次要存儲的就是多個 Stream 和一個 Checkpoint,關(guān)上后大略如下圖(因?yàn)槭切蛄谢闪?2 進(jìn)制,所以是不可讀的)

  而在讀取播放時,數(shù)據(jù)的解決流程會有一些差別。零碎會嘗試一次性從磁盤加載所有信息到一個用于組織回放的數(shù)據(jù)結(jié)構(gòu)中——FLocalFileReplayInfo

  3.3.3 回放架構(gòu)梳理小結(jié)到此,咱們曾經(jīng)對整個零碎有了更深刻的了解,再回頭看整個回放的流程就會清晰很多。

  上個小結(jié)咱們曾經(jīng)從架構(gòu)的角度上梳理了回放錄制的原理和過程,然而還有很多細(xì)節(jié)問題還沒有深究,比方:

  這些問題看似簡略,但實(shí)現(xiàn)起來卻并不容易。比方咱們在播放時須要動靜切換特定的攝像機(jī)視角,那就須要曉得 UE 外面的攝像機(jī)零碎,包含 Camera 的治理、如何設(shè)置 ViewTarget、如何通過網(wǎng)絡(luò) GUID 找到對應(yīng)的指標(biāo)等,這些內(nèi)容都與游戲玩法高度耦合,因而在剖析錄制加載細(xì)節(jié)前倡議先回顧一下 UE 的 Gameplay 框架。

  UE 的 Gameplay 根本是依照面向?qū)ο蟮男问絹碓O(shè)計(jì)的,波及到常見概念(類)如下:

  一個可控的游戲單位,Character 相比 Pawn 多了很多人型角色的性能,比方挪動、下蹲、跳躍等

  所有攝像機(jī)相干的性能都通過 CameraManager 治理,比方攝像機(jī)的地位、攝像機(jī)觸動成果等

  概括來講,一個游戲場景是一個 World,每個場景能夠拆分成很多子關(guān)卡(即 Level),咱們能夠通過配置 Gamemode 參數(shù)來設(shè)置游戲規(guī)則(只存在與于服務(wù)器),在 Gamestate 上記錄以后游戲的較量狀態(tài)和進(jìn)度。對于每個玩家,咱們個別至多會給他一個能夠管制的角色(即 Pawn/Character),同時把這個角色相干的數(shù)據(jù)存儲在 Playerstate 上。最初,針對每個玩家應(yīng)用惟一的一個控制器 Playercontroller 來響應(yīng)玩家的輸出或者執(zhí)行一些本地玩家相干的邏輯(比方設(shè)置咱們的察看對象 VIewTarget,會調(diào)用到 Camermanager 相干接口)。此外,PC 是網(wǎng)絡(luò)同步的要害,咱們須要通過 PC 找到網(wǎng)絡(luò)同步的中心點(diǎn)進(jìn)而剔除不須要同步的對象,服務(wù)器也須要依附 PC 能力判斷不同的 RPC 應(yīng)該發(fā)給哪個客戶端。

  回放零碎 Gameplay 邏輯仍然遵循 UE 的根底框架,但因?yàn)橹徊暗綌?shù)據(jù)的播放還是有不少須要留神的中央。

  在一個 Level 里,有一些對象是默認(rèn)存在的,稱為 StartupActor。這些對象的錄制與回放可能須要非凡解決,比方回放一開始就默認(rèn)創(chuàng)立,盡量避免動靜的結(jié)構(gòu)開銷。

  UE 的網(wǎng)絡(luò)同步自身須要借助 Controller 定位到 ViewTarget(同步核心,便于做范疇剔除),所以回放錄制時會創(chuàng)立一個新的 DemoPlayerController(留神:因?yàn)樵诒镜乜赡芡瑫r存在多個 PC,獲取 PC 時不要拿錯了)。這個 Controller 的主要用途就是輔助同步邏輯,而且會被錄制到回放數(shù)據(jù)外面。

  回放零碎并不限度你的察看視角,然而會默認(rèn)提供一個自在挪動的觀戰(zhàn)對象(SpectatorPawn)。當(dāng)咱們播放時會收到同步數(shù)據(jù)并創(chuàng)立 DemoPC,DemoPC 會從 GameState 上查找 SpectatorClass 配置并生成一個用于觀戰(zhàn)的 Pawn。咱們通常會 Possess 這個對象并挪動來管制攝像機(jī)的視角,當(dāng)然也能夠把觀戰(zhàn)視角鎖定在游戲中的其余對象上。

  回放不倡議錄制 PlayerController(簡稱 PC),游戲中的與玩家角色相干的數(shù)據(jù)也不應(yīng)該放在 PC 上,最好放在 PlayerState 或者 Character 下面。為什么回放不解決 PC?次要起因是每個客戶端只有一個 PC。如果我在客戶端下面錄制回放并且把很多重要數(shù)據(jù)放在 PC 上,那么當(dāng)你回放的時候其余玩家 PC 上的數(shù)據(jù)你就無奈拿到。

  回放不會錄制 Gamemode,因?yàn)?Gamemode 只在服務(wù)器才有,并不做同步。

  RPC 的目標(biāo)是跨端近程調(diào)用,九游娛樂平臺對于非多播的 RPC,他只會在某一個客戶端或者服務(wù)器下面執(zhí)行。也就是說,我在服務(wù)器上錄制就拿不到客戶端的 RPC,我在客戶端上錄制就拿不到服務(wù)器上的 RPC,總會失落掉一些 RPC。

  RPC 是冗余的,可能咱們在回放的時候不想調(diào)用。比方服務(wù)器觸發(fā)了一個 ClientRPC(讓客戶端播放攝像機(jī)觸動)并錄制,那么回放的時候我作為一個觀戰(zhàn)的視角不應(yīng)該調(diào)用這個 RPC(當(dāng)然也能夠自定義的過濾掉)。

  RPC 是一個無狀態(tài)的告訴,一旦錯過了就再也無奈獲取?;胤胖谐3泄し虻奶D(zhuǎn),跳轉(zhuǎn)之后咱們再就無奈拿到后面的 RPC 了。如果咱們適度依賴 RPC 做邏輯解決,就很容易呈現(xiàn)回放體現(xiàn)不對的狀況。

  綜上所述,我并不倡議在反對回放零碎的游戲外面頻繁應(yīng)用 RPC,最好應(yīng)用屬性同步來代替,這樣也能很好的反對斷線重連。

  4)導(dǎo)出所有同步屬性根本信息 FieldExport GroupMap,用于播放時精確且能兼容地接管這些屬性

  調(diào)用 ProcessReplayTasks 解決以后正在執(zhí)行的工作,如果工作沒有實(shí)現(xiàn)就返回(工作有很多種,比方 FGotoTime InSecondsTask 就是用來執(zhí)行工夫跳轉(zhuǎn)的工作)

  拿到 StreamArchive,設(shè)置以后回放的工夫(回放工夫決定了以后回放數(shù)據(jù)加載的進(jìn)度)

  去 PlaybackPackets 查找是否還有待處理的數(shù)據(jù),如果沒有數(shù)據(jù)就暫?;胤?/p>

  ConditionallyReadDemo FrameIntoPlaybackPackets 依據(jù)以后的工夫,讀取 StreamArchive 外面的數(shù)據(jù),緩存到 PlaybackPackets 數(shù)組外面

  ConditionallyProcess PlaybackPackets 一一去解決 PlaybackPackets 外面的數(shù)據(jù),進(jìn)行反序列化的操作(這一步是還原數(shù)據(jù)的要害,回放 Actor 的創(chuàng)立通常是這里觸發(fā)的)

  FinalizeFastForward 解決快進(jìn)等操作,因?yàn)樵蹅兛赡茉谝粠臅r候解決了回放 N 秒的數(shù)據(jù)(也就是快進(jìn)),所以這里須要把被快進(jìn)掉的回調(diào)函數(shù)(OnRep)都執(zhí)行到,同時記錄到底快進(jìn)了多少工夫

  在 2.3.2 大節(jié),咱們提到了 UE 的網(wǎng)絡(luò)同步形式為增量式的狀態(tài)同步,任何一刻的狀態(tài)數(shù)據(jù)都是后面所有狀態(tài)同步數(shù)據(jù)的疊加,所以必須從最開始播放能力保障不失落掉兩頭的任何一個數(shù)據(jù)包。想要實(shí)現(xiàn)快進(jìn)和工夫跳躍必須通過加載最近的 Checkpoint 能力實(shí)現(xiàn)。

  在每次開始回放前,咱們能夠給回放指定一個指標(biāo)工夫,而后回放零碎就會創(chuàng)立一個

  。基本思路是先找到左近的一個 Checkpoint(快照點(diǎn))加載,而后疾速讀取從 Checkpoint 工夫到指標(biāo)工夫的數(shù)據(jù)包進(jìn)行解析。這個過程中有很多細(xì)節(jié)須要了解,比方咱們從 20 秒跳躍到 10 秒的時候,20 秒時刻的 Actor 是不是都要刪除?刪除之后要如何再去創(chuàng)立一個新的和 10 秒時刻截然不同的 Actor?無妨帶著這些問題去了解上面的流程。

  3)刪除掉所有非 StartUp(StartUp:一開始擺在場景里的)的 Actor,StartUp 依據(jù)狀況選擇性刪除(在跳轉(zhuǎn)進(jìn)度的時候,整個場景的 Actor 可能曾經(jīng)齊全不一樣了,所以最好全副刪除,對于擺在場景外面的可毀壞墻,如果沒有產(chǎn)生過變動能夠無需解決,如果被打壞了則須要刪除從新創(chuàng)立)。

  除此之外,咱們還提到了回放的暫停。其實(shí)暫停分為兩種,一種是暫?;胤艛?shù)據(jù)的錄制 / 讀取,通過 UDemoNetDriver:: PauseRecording 能夠?qū)崿F(xiàn)暫?;胤诺匿浿?,通過 PauseChannels 能夠暫?;胤潘?Actor 的體現(xiàn)邏輯(個別是在加載 Checkpoint、快進(jìn)、沒有數(shù)據(jù)讀取時主動調(diào)用),然而不會進(jìn)行 Tick 等邏輯執(zhí)行。另一種暫停是暫停 Tick 更新(也能夠用于非回放世界),通過 AWorldSetting:: SetPauserPlayerState 實(shí)現(xiàn),這種暫停不僅會進(jìn)行回放數(shù)據(jù)包的讀取,還會進(jìn)行 WorldTick 的更新,包含動畫、挪動、粒子等,是嚴(yán)格意義上的暫停。

  3.5.1 回放兼容性的意義回放的錄制和播放往往不是一個機(jī)會,玩家可能了回放后過了幾天才想起來觀看,甚至還會用曾經(jīng)降級到 5.0 的游戲版本去播放 1.0 時的回放數(shù)據(jù)。因而,咱們須要有一個機(jī)制來盡可能地兼容過來一段時間游戲版本的回放數(shù)據(jù)。

  因?yàn)榇a在迭代的時候,函數(shù)流程、數(shù)據(jù)格式、類的成員等都會發(fā)生變化(減少、刪除、批改),游戲邏輯是必須要依賴這些內(nèi)容能力正確執(zhí)行。舉個例子,如果 1.0 版本的代碼中類 ACharacter 上有一個成員變量 FString CurrentSkillName 記錄了游戲角色以后的技能名字,在 2.0 版本的代碼時咱們把這個成員刪掉了。因?yàn)樵?1.0 版本錄制的數(shù)據(jù)外面存儲了 CurrentSkillName,咱們在應(yīng)用 2.0 版本代碼執(zhí)行的時候必須得想方法繞過這個成員,因?yàn)檫@個值在以后版本外面沒有任何意義,強(qiáng)行應(yīng)用的話可能造成回放失常的數(shù)據(jù)被籠罩掉。

  其實(shí)不只是回放,咱們?nèi)粘T趹?yīng)用編輯器等工具時,只有同時波及到對象的序列化(通用點(diǎn)來講是固定格局的數(shù)據(jù)存儲)以及版本迭代就肯定會遇到相似的問題,輕則導(dǎo)致引擎資源有效重則產(chǎn)生解體。

  在 UE 的回放零碎外面,兼容性的問題還要更簡單一些,因?yàn)椴暗搅丝栈镁W(wǎng)絡(luò)同步的實(shí)現(xiàn)原理。

  第一節(jié)咱們談到了空幻有屬性同步和 RPC 兩種同步形式,且二者都是基于 Actor 來實(shí)現(xiàn)的。在每個 Actor 同步的時候,咱們會給每個類創(chuàng)立一個 FClassNetCache 用于惟一標(biāo)識并緩存他的同步屬性,每個同步屬性 /RPC 函數(shù)也會被惟一標(biāo)識并緩存其相干數(shù)據(jù)在 FFieldNetCache 構(gòu)造外面。因?yàn)橥环莅姹镜目蛻舳舜a和服務(wù)器代碼雷同,咱們就能夠保障客戶端與服務(wù)器每個類的 FClassNetCache 以及每個屬性的 FFieldNetCache 都是雷同的。這樣在同步的時候咱們只須要在服務(wù)器上序列化屬性的 Index 就能夠在客戶端反序列化的時候通過 Index 找到對應(yīng)的屬性。

  這種計(jì)劃的實(shí)現(xiàn)前提是客戶端與服務(wù)器的代碼必須是一個版本的。如果客戶端的類成員與服務(wù)器對應(yīng)的類成員不同,那么這個 Index 在客戶端上所代表的成員就與服務(wù)器上的不統(tǒng)一,最終的執(zhí)行后果就是謬誤的。所以對于失常的游戲來說,咱們必須要放棄客戶端與服務(wù)器版本雷同。然而對于回放這種可能跨版本執(zhí)行的狀況就須要有一個新的兼容計(jì)劃。

  盡管這種形式減少了同步的開銷和老本,但對于回放零碎來說是能夠承受的,而且回放的整個錄制過程中是齊全牢靠的,不會因?yàn)閬G包而產(chǎn)生播放時導(dǎo)出數(shù)據(jù)沒收到的狀況。這樣即便我新版本的對象屬性數(shù)量發(fā)生變化(比方程序發(fā)生變化),因?yàn)槲以诨胤艛?shù)據(jù)外面曾經(jīng)存儲了這個對象所有會被序列化的屬性信息,我肯定能找到對應(yīng)的同步屬性,而對于曾經(jīng)被刪掉的屬性,我回放時本地代碼創(chuàng)立的 FClassNetCache 不蘊(yùn)含它,因而也不會被利用進(jìn)來。

  到這里,咱們基本上能夠了解空幻引擎對回放零碎的向后兼容性計(jì)劃。然而即便有了下面的計(jì)劃,咱們其實(shí)也只是兼容了類成員產(chǎn)生扭轉(zhuǎn)的狀況,保障了不會因?yàn)閷傩允涠尸F(xiàn)邏輯的謬誤執(zhí)行。然而對于新增的屬性,因?yàn)樵瓉泶鎯Φ幕胤盼募饷婊静淮嬖谶@個數(shù)據(jù),回放的時候是齊全不會有任何相干的邏輯的。因而,所謂回放零碎的兼容也只是有肯定限度的兼容,想很好地反對版本差別過大的回放文件還是絕對艱難許多的。

  這是侑虎科技第 1367 篇文章,感激作者 Jerish 供稿。歡送轉(zhuǎn)發(fā)分享,未經(jīng)作者受權(quán)請勿轉(zhuǎn)載。如果您有任何獨(dú)到的見解或者發(fā)現(xiàn)也歡送分割咱們,一起探討。(QQ 群:465082844)

上一篇:虛幻4引擎(Unreal Engine4)

返回列表

下一篇:Unreal Engine虛幻游戲引擎擴(kuò)展資料 - 工業(yè)城市