小飛機工做筆記(四)去除回滾後的預測校訂

    因爲實際投放中的種種問題,去除了回滾機制,總體的同步方案也作了修改:緩存

    1)客戶端在收到服務器下發的驅動幀以前,邏輯幀不能再向前演算;服務器

    2)客戶端在每一個邏輯幀內都會將最新收集的指令打包上傳到服務器(不包含客戶端幀號),服務器在下一個驅動幀中會廣播全部上傳來的客戶端指令集;網絡

    3)客戶端在收到服務器的驅動幀後,解析出指令並緩存在本地下一將要執行的邏輯幀用戶指令中;oop

    4)客戶端本地timer發現能夠繼續執行邏輯幀時,會計算當前的時延,調整自身的間隔,並向後演算邏輯幀;優化

    5)演算邏輯幀時會更新各Entity的速度等屬性,渲染層根據新的速度對各Entity插值。spa

    上述方案存在一些問題。同步

    對於點2),當前假設當前剛執行完邏輯幀N-1,而網絡幀N還沒有到來,邏輯幀N遲遲不能執行,那麼在邏輯幀N-1~邏輯幀N間隔內收集的玩家指令必須在網絡幀N到來以後,也就是執行邏輯幀N時才能發出,最先也要在網絡幀N+1廣播出來,那麼玩家指令最先也要在邏輯幀N+1才能執行,無端增長了延遲時間。後來針對此點作了改進,每一個渲染幀內都會蒐集玩家指令並上傳(部分重複的指令能夠剔除),這樣在網絡幀N到來以前,依然有部分玩家指令有機會在網絡幀N被收集,並廣播執行。it

    非主玩家以及AI在渲染層該如何預測。由於邏輯幀老是落後於渲染幀,所以渲染幀會以邏輯幀的數據做爲起點並向前計算一段時間,以獲得的終點做爲當前目標點插值處理。打包

    可是對於主玩家則情形不一樣。爲了提高玩家的手感體驗,指望玩家的操做指令能當即獲得執行。在狀態同步方案或者支持回滾的幀同步方案下這點都很容易實現,可是幀同步去除回滾後,因爲要等待自身指令廣播回來,形成了玩家操做上的延遲,手感下降了不少。所以考慮玩家操做時當即在渲染層執行,但這樣帶來的問題是,稍後網絡幀回來後,渲染層的位置和網絡幀的位置的誤差如何解決。此時能夠考慮對渲染層的主玩家的移動插值,儘量接近邏輯層的演算目標位置,但這樣一是會影響玩家的操做,二是如何插值接近也很複雜。好比一種辦法是在邏輯層沒有玩家指令執行時對渲染層進行插值處理,但這樣就可能會出現每一個網絡幀之間都有玩家指令時,一直沒有機會作足夠的插值,致使渲染幀與邏輯幀的偏差愈來愈大的狀況。渲染

    所以我又考慮了另外一種方案。玩家不只同步自身的方向,還要同步本身的位置。即主玩家在每一個動做收集幀中都會將當前的運動方向和它下一幀運動後的位置提交到服務器,並在下一個網絡幀中廣播給包括本身在內的全部客戶端,改變邏輯層對應的位置信息,這樣就是邏輯層「追隨」渲染層了,如何減小兩者之間的偏差是重點。它有幾個點能夠優化。一是在服務器的網絡幀N以前,玩家的全部指令都會被收集到幀N中,而無論客戶端本身走了幾個渲染幀或動做幀。玩家能夠在本身的動做幀(與網絡幀間隔相同)預計算出下一動做幀的位置並提交給服務器,而在下一動做幀到來以前,玩家始終按照以前提交的指令運行,這樣就能夠保證位置信息儘量早地同步出去,到達各客戶端時能夠抹去一部分網絡延時;二是同時提交方向信息,若是某一網絡幀內沒有收集到任何動做指令,則有兩種狀況,一種是因爲網絡延遲,致使這個網絡幀內的指令沒有同步下來;二是玩家在這個網絡幀間隔內確實沒有發出新的指令,好比改變運動方向或者中止再啓動等。能夠由其它客戶端繼續模擬,從而減小信息同步量。三是預測各端的位置時根據當前的本地與服務器幀差動態改變各端的速度。

    在實現中又發現一個問題,雖然設定的是每隔25MS執行一幀(Laya.Timer.delay),可是測定實際時間間隔至少在30MS以上。每一邏輯幀是兩個渲染幀,按照50MS的固定數值計算的,而實際的時間間隔則至少是60MS,每一個渲染幀使用實際的時間間隔計算渲染位移。這就致使渲染幀與邏輯幀並未拉開差距,可是渲染位移上的差距卻愈來愈大。爲了解決這個問題,只能在固定渲染幀中以固定的時間間隔(__BattleSimulatorLoopInterval__/2)計算渲染位移。而若是渲染幀與邏輯幀之間拉開差距,則計算倍率factor,代入渲染位移的計算。一種作法是發現幀拉開差距時就動態調整timer的更新間隔,可是這樣帶來的問題是當渲染更新間隔很大時,好比50MS一渲染幀,會形成渲染幀數降低,表現上有跳躍,即飛機位移跳動過大,致使卡頓效果。所以須要以固定的間隔渲染。

相關文章
相關標籤/搜索