久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

關(guān)于幀同步和網(wǎng)游游戲開發(fā)的一些心得

 郭恩 2018-08-25

  從今年下半年開始制作一款實(shí)時(shí)對(duì)戰(zhàn)游戲以來,,我就在著手寫一個(gè)幀同步的游戲框架,其中包含了服務(wù)器框架和客戶端框架,,該框架目前已經(jīng)開源,。
  期間踩過無數(shù)的坑,充分領(lǐng)略到了國內(nèi)中文技術(shù)文檔十分稀少和零散的問題,,所以在這里我想寫下我走過的路,,以便于后來者參考。

  首先,,我希望寫一個(gè)前后端能統(tǒng)一語言的框架,,以至于在前端寫好的游戲邏輯,拿到后端就可以直接使用,。
  其次,,我的目標(biāo)是寫一個(gè)同步框架,在框架層面解決同步問題,,在此之上寫游戲邏輯的時(shí)候不需要再考慮游戲同步的問題,。

  目前看來,這兩個(gè)目標(biāo)都得到了比較好的完成,。
  下面是解決方法,。

  首先要解決的是前后端語言一致的問題

  這里我使用了一個(gè)c#服務(wù)器框架 SupperScoket
  遇到的幾個(gè)坑是:

  1.導(dǎo)出這個(gè)框架到在Mono上運(yùn)行時(shí)報(bào)一個(gè)找不到window API的錯(cuò)誤,解決方法是使用.Net 4.0以上版本的SupperSocket

  2.框架在使用TCP模式時(shí),,有時(shí)會(huì)報(bào)出一個(gè)Send byte Time out 的異常,,解決方法是使用TrySend方法,并在返回false時(shí)關(guān)閉連接,。
  (后來發(fā)現(xiàn)這樣用也會(huì)有一個(gè)問題,,就是服務(wù)器經(jīng)常在游戲過程中斷掉客戶端的鏈接,現(xiàn)在我的解決方案是返回False之后再嘗試3次,,如果均不成功才斷掉客戶端,,經(jīng)測試這樣穩(wěn)定了很多)

  3.框架在解析消息時(shí),,遇到不完整的消息沒能正確解析,這里他的文檔不是很詳細(xì),,其實(shí)要把未解析的數(shù)據(jù)數(shù)量緩存起來,,詳情看這篇博客,源碼在此

  第二個(gè)要解決的是同步框架的問題

  這個(gè)問題比較復(fù)雜,,如何在書寫游戲邏輯的時(shí)候感受不到同步問題的存在,?如果每個(gè)事件都要等服務(wù)器的回包,還要體驗(yàn)流暢,,只能從預(yù)處理和追趕兩個(gè)角度入手,。

  預(yù)處理就是,,這個(gè)事件還沒有發(fā)生,,但是考慮到網(wǎng)絡(luò)延遲的存在,提前先把結(jié)果發(fā)送給每個(gè)客戶端,,然后客戶端到了這個(gè)時(shí)刻再把這個(gè)事件表現(xiàn)出來,,典型的例子就是皇室戰(zhàn)爭。

  如果說沒有辦法做到預(yù)處理呢,,比如說玩家的操作需要立即響應(yīng),,那么其他玩家收到這個(gè)事件的時(shí)候必然已經(jīng)遲了,所以就要做追趕,,比較典型的就是影子跟隨算法,。

  但是這兩種做法必然要在游戲邏輯中做對(duì)應(yīng)的處理,開發(fā)者要時(shí)刻清醒此時(shí)是預(yù)測還是追趕,,增加邏輯的復(fù)雜性,,而且游戲的表現(xiàn)可能也參差不齊,有些地方也許同步的好,,有些地方可能不好,,要調(diào)優(yōu)需要在每個(gè)地方都下功夫,增加開發(fā)時(shí)間,。

  那么應(yīng)該怎么辦呢,,最理想的方法當(dāng)然是全部當(dāng)成本地計(jì)算,這樣就無需考慮是追趕還是預(yù)測的問題了,,那么網(wǎng)絡(luò)游戲怎么全當(dāng)成本地計(jì)算呢,?當(dāng)然就是幀同步了。

  關(guān)于幀同步網(wǎng)上已經(jīng)有很多資料,,在此不再贅述,,但是關(guān)于幀同步有一個(gè)核心的問題,那就是它在網(wǎng)絡(luò)差的時(shí)候表現(xiàn)很差,,這一點(diǎn)我們可以從星際爭霸和魔獸爭霸這些游戲中看出來,,一旦有人卡頓,,所有人都要停下等這個(gè)人的消息,但是我們知道手游《王者榮耀》這款游戲就是幀同步做的,,他是怎么解決這一問題的呢,?在《王者榮耀》負(fù)責(zé)人在unite 2017大會(huì)分享中我們沒有看到這一解決方案,我感覺有可能是樂觀幀同步,,但是在看了暴雪分享的守望先鋒同步機(jī)制之后,,我得到了一個(gè)我自己的解決方案。

  那就是預(yù)測回滾和解,。

  原理很簡單,,游戲開始時(shí),每個(gè)客戶端按照幀同步的方案推進(jìn)著游戲,,但是如果遇到服務(wù)器沒能及時(shí)返回其他玩家操作的時(shí)候,,給對(duì)應(yīng)的玩家預(yù)測一個(gè)操作(復(fù)制該玩家最后一次操作),并繼續(xù)推進(jìn)游戲,如果在其后收到了服務(wù)器玩家關(guān)于這個(gè)人的操作,,則把游戲回滾到預(yù)測開始的那一幀重新計(jì)算一遍,,然后和現(xiàn)在游戲世界的表現(xiàn)和解。

  如果服務(wù)器遲遲沒有收到某個(gè)玩家的消息,,則會(huì)給這個(gè)玩家預(yù)測一個(gè)消息(復(fù)制該玩家的最后一次操作)然后推送給所有玩家,,包括那個(gè)掉線的玩家。其他玩家會(huì)以這個(gè)預(yù)測操作為準(zhǔn)計(jì)算接下來的游戲世界,,而這個(gè)掉線玩家也會(huì)收到這個(gè)預(yù)測操作,,并且替換掉玩家實(shí)際進(jìn)行的操作,重新計(jì)算一遍游戲世界,。保證每個(gè)客戶端的輸入一致,。

  原理說起來簡單,但是其實(shí)有幾個(gè)難點(diǎn),。

  第一個(gè)難點(diǎn)就在于回滾,,如何回滾到預(yù)測開始的那一幀呢,要記錄下每一幀的變化,,然后逐幀退回嗎,?還是把每一幀的數(shù)據(jù)做一個(gè)快照保存下來?

  其實(shí)這個(gè)問題實(shí)現(xiàn)起來不難,,關(guān)鍵是從性能考慮,,如果把每一幀的數(shù)據(jù)都快照下來,內(nèi)存可能會(huì)吃緊,,如果做逐幀退回的方式,,實(shí)現(xiàn)起來相對(duì)復(fù)雜,并且在性能上也可能有問題。

  這里就引入了ECS架構(gòu)幫助我簡化了這一問題,,在ECS架構(gòu)中,,C 也就是component(組件),它是純數(shù)據(jù)的集合,并且 E 也就是 Entity(實(shí)體) 集中存放在一起,,這方便了我對(duì)它們的集中操作,,

  在ECS架構(gòu)的幫助下,我實(shí)現(xiàn)了對(duì)組件進(jìn)行快照式的存儲(chǔ),,對(duì)實(shí)體進(jìn)行了增量式的存儲(chǔ),,實(shí)現(xiàn)了對(duì)數(shù)據(jù)的回滾。

  第二個(gè)難點(diǎn)在于和解,,由于預(yù)測操作和玩家真實(shí)操作的不同,,重計(jì)算出來的世界必然預(yù)測的世界有差異,那么怎樣以盡量不引人注意的形式,,把預(yù)測世界過渡到真實(shí)世界呢,,這一點(diǎn)守望先鋒的分享中提到了一部分,但是沒有完全解答這個(gè)問題,。

  實(shí)際上解決這個(gè)問題的思路是,,先確定哪些是可以和解的,哪些是不可以和解的,,然后分頭處理。怎么分頭處理呢,,就是可以和解的在預(yù)測計(jì)算中就表現(xiàn),,不可以和解的,要等到真正的數(shù)據(jù)來了才進(jìn)行表現(xiàn),。

  那么哪些是可以和解的呢,?就是在玩家不知不覺間就可以過渡到的,比如說物體的位置,,動(dòng)畫,。這里有很多的技術(shù)可以做這種和解,比如說影子跟隨算法,。

  不可以和解的比如說冒出的血條數(shù)字,,你不能說傷害數(shù)字都冒出來了,,然后又塞回去。

  但其實(shí)有一個(gè)難點(diǎn)是,飛彈能不能和解,?

  顯然,飛彈的位置是可以和解的,,但是飛彈的創(chuàng)建與銷毀呢,?這里涉及到一個(gè)游戲表現(xiàn)的問題,如果飛彈的創(chuàng)建要等到服務(wù)器回包才出現(xiàn),那么這個(gè)表現(xiàn)在網(wǎng)絡(luò)差的時(shí)候就太糟糕了,。
  所以一定要可以和解,,不能和解創(chuàng)造條件也要和解。

  下面是解決方案

  其實(shí)一部分解決方法在難點(diǎn)1已經(jīng)提到了,,首先要建立一個(gè)對(duì)實(shí)體的回滾系統(tǒng),,保證飛彈能回滾。
  但是還有一個(gè)問題,,在回滾的過程中要先把這個(gè)飛彈銷毀,,但是如果重計(jì)算的結(jié)果是仍然創(chuàng)建這個(gè)飛彈呢?難道要再把這個(gè)飛彈再創(chuàng)建一次?雖然我們可以用池來避免頻繁的創(chuàng)建銷毀,,但是粒子系統(tǒng)從池中取出仍然有重新構(gòu)建的開銷,。

  很自然的想到可以延遲派發(fā)創(chuàng)建的事件,在數(shù)據(jù)層面這個(gè)實(shí)體已經(jīng)被重計(jì)算的很多次了,,但只要這個(gè)實(shí)體仍然存在我就不再派發(fā)這個(gè)實(shí)體的創(chuàng)建事件,。銷毀也是一樣。

  但是我如何確定我兩次創(chuàng)建的實(shí)體的是一個(gè)呢,?要知道我們框架的設(shè)計(jì)目標(biāo)是開發(fā)時(shí)盡量避免對(duì)同步系統(tǒng)的感知,,也就是我們游戲邏輯并不知道現(xiàn)在的數(shù)據(jù)是真實(shí)的數(shù)據(jù)還是預(yù)測的數(shù)據(jù),要在創(chuàng)建這個(gè)體的的時(shí)候判斷這個(gè)實(shí)體是否已經(jīng)在預(yù)測時(shí)創(chuàng)建過了顯然不應(yīng)該是我們游戲邏輯應(yīng)該做的,,可我們的框架又如何確定兩個(gè)實(shí)體是否一致呢,。

  直接比較它們兩個(gè)是否相等肯定不行,把他們的數(shù)據(jù)取出來一一比對(duì)又太耗時(shí),。
  我采用的方法是我稱之為特征碼的方法,,在構(gòu)建一個(gè)實(shí)體的時(shí)候,用一個(gè)字符串去描述這個(gè)實(shí)體,,這個(gè)字符串要盡量簡略而又不能與其他特征碼重復(fù),,然后自己實(shí)現(xiàn)的hash方法(.NET自帶的GetHashCode有平臺(tái)差異)把這個(gè)字符串轉(zhuǎn)化為一個(gè)Int作為這個(gè)實(shí)體的唯一標(biāo)識(shí)符,在創(chuàng)建實(shí)體的時(shí)候,,只需要判斷這個(gè)實(shí)體ID和緩存中的ID是否一致就可以判斷這個(gè)實(shí)體是否已經(jīng)在預(yù)測中存在了,,從而實(shí)現(xiàn)延遲派發(fā)。

  第三個(gè)難點(diǎn)是重計(jì)算的性能,,在我開發(fā)的早期版本時(shí),,游戲邏輯執(zhí)行一幀要耗時(shí)5ms,如果此時(shí)客戶端預(yù)測了5幀,,那么收到服務(wù)器消息再重計(jì)算需要25ms才能計(jì)算的完,,在網(wǎng)絡(luò)延遲更大的時(shí)候,游戲性能是不可接受的,。

  解決這個(gè)問題要從優(yōu)化游戲性能和限制預(yù)測幀數(shù)入手,,我優(yōu)化了ECS的幾個(gè)最基本API的執(zhí)行性能,再用四叉樹優(yōu)化了碰撞系統(tǒng),把游戲邏輯的執(zhí)行時(shí)間縮短到1ms左右,,然后又通過服務(wù)器控制客戶端的預(yù)測幀數(shù),,使其不至于過大導(dǎo)致沉重的重計(jì)算負(fù)擔(dān)。

再說一點(diǎn)其他的技術(shù)細(xì)節(jié)

  1.實(shí)體的集中創(chuàng)建與銷毀
    現(xiàn)在的架構(gòu)中可以看到是一幀結(jié)束后把所有的實(shí)體集中的創(chuàng)建與銷毀,,為什么要這樣做呢,,實(shí)際上是為了重計(jì)算服務(wù),當(dāng)重計(jì)算進(jìn)行時(shí)要先進(jìn)行回滾,,我根據(jù)回滾數(shù)據(jù)得知它是在某一幀里被創(chuàng)建的,,但是不知道在哪個(gè)系統(tǒng)中,這就有可能造成,,在實(shí)際計(jì)算中某個(gè)對(duì)象實(shí)際上在稍晚的時(shí)間被創(chuàng)建,,不會(huì)被較早時(shí)間執(zhí)行的系統(tǒng)所影響,但是回滾后,,這個(gè)對(duì)象被創(chuàng)建在了較早的時(shí)刻(這一幀的開始),,導(dǎo)致較早執(zhí)行的系統(tǒng)也影響了他導(dǎo)致計(jì)算錯(cuò)誤,為避免這一問題,,我采用統(tǒng)一創(chuàng)建和銷毀時(shí)刻的方式解決,。
    這一方式有一個(gè)問題,就是創(chuàng)建飛彈等對(duì)象時(shí)至少要延遲一幀,,在主觀感覺上就慢一點(diǎn),,守望先鋒也提到了這一問題,他們提到后來把創(chuàng)建的時(shí)刻重新拿回了游戲邏輯內(nèi),,我估計(jì)是在保存回滾數(shù)據(jù)時(shí)把是在哪個(gè)系統(tǒng)創(chuàng)建的實(shí)體也保存了下來,,這樣就可以避免計(jì)算錯(cuò)誤的問題。(目前在我的框架里已經(jīng)優(yōu)化了這一問題)

  2.斷線重連
    關(guān)于斷線重連這一點(diǎn),,使用ECS架構(gòu)的優(yōu)勢就體現(xiàn)出來了,傳統(tǒng)的幀同步斷線重連只能把所有的玩家的數(shù)據(jù)從頭輸入一遍重計(jì)算,,時(shí)間很長,,而ECS可以很方便的把服務(wù)器的當(dāng)前數(shù)據(jù)全部發(fā)送給重連的客戶端,讓客戶端直接從重連的那一幀開始游戲,,避免了漫長的重連過程,。

  3.常見的不同步情況
    1.MomentComponentBase 組件的DeepCopy方法沒有正確的拷貝全部數(shù)據(jù)
    2.有些組件從本地創(chuàng)建和通過服務(wù)器同步消息創(chuàng)建的值有差異(比如有些組件有特殊的構(gòu)造方法,而通過服務(wù)器同步的組件不執(zhí)行構(gòu)造方法)
    3.前后端代碼不統(tǒng)一
    4.前后端數(shù)據(jù)表不統(tǒng)一
    5.在進(jìn)行整數(shù)計(jì)算的時(shí)候,,數(shù)值溢出
    6.浮點(diǎn)數(shù)計(jì)算誤差(讀表也會(huì)出這個(gè)錯(cuò)誤)
    7.同步邏輯之外的數(shù)值修改(例如付費(fèi)復(fù)活)

參考資料:

  云風(fēng):淺談《守望先鋒》中的 ECS 構(gòu)架
  云風(fēng):繼續(xù)談網(wǎng)絡(luò)游戲的同步問題
  GAD:《守望先鋒》架構(gòu)設(shè)計(jì)與網(wǎng)絡(luò)同步
  GAD: Unity3D RTS游戲中幀同步實(shí)現(xiàn)
  GAD: 游戲網(wǎng)絡(luò)開發(fā)(五):浮點(diǎn)數(shù)的確定性
  知乎: 《王者榮耀》技術(shù)總監(jiān)復(fù)盤回爐歷程
  skywind: 再談網(wǎng)游同步技術(shù)
  skywind: 幀鎖定同步算法
  

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多