小飛機工做筆記(三)關於預測與校訂的一點思考

    項目雖然採用了幀同步+回滾的方案,可是實際上線後的表現卻不如人意。目前表如今幾個問題上:chrome

    1)不一樣客戶端網絡延時不同,網速越好的客戶端越容易出現回滾。頻繁的回滾對性能形成了很大的影響。在chrome或者messenger中勉強還能夠,可是在微信中因爲執行引擎的落後,JS腳本成了性能的最大瓶頸,卡幀、掉幀很是嚴重;瀏覽器

    2)對戰中發現其它玩家的表現很「飄」,分析是由於本地演算預測與實際的操做指令差異太大致使的。其它玩家指令到來時本地已經向前演算了一段時間,當前客戶端回滾後,渲染層再插值,渲染層中其它玩家的當前位置與邏輯層的當前值差異太大時(包括方向和速度大小)就會致使飛機忽快忽慢,讓人以爲很「飄」。從目的上來說,即便在至關大的延遲內也依然儘可能讓其它玩家的表現儘量平滑,這個方案已經達到了它的目的。可是從效果上來看,則並不能使人滿意;服務器

    3)本地在向前演算中可能會出現不應發生卻發生的行爲,好比玩家B沒有發出子彈,但因爲未能接收到B的熄火指令,致使本地演算產生了子彈。這種狀況下子彈個體就必須予以撤消。而有時則又可能出現該發生卻未及時發生的行爲,好比玩家B打出了子彈,可是因爲未能及時收到玩家B的開火指令,致使子彈未能及時出如今屏幕上。而接收到B的指令時,回滾後會從新演算至本地當前幀,會致使在第N幀產生的子彈,卻在當前是在第M幀時須要渲染出來,向前又演算了M-N幀,渲染層中子彈會出如今離發射點很遠的地方,令玩家莫名其妙。此時(回滾+ECS)在渲染層對邏輯層屬性插值的缺點就暴露出來了。因爲不肯定什麼時候會進行回滾,也不肯定會回滾多少次,每一個狀態幀都是不可依賴的,難以依據它捕捉一個完整的行爲的開始和結束。固然,真的要作也是能夠作的,可是這個行爲隨時可能變更,必須對每一個階段的行爲作充分的撤消/重建/插值,複雜度很是之高。相比之下,若去除回滾,不採用實時對邏輯層插值的作法,而是按照傳統的方式來捕捉事件,由渲染層根據此事件本身肯定子彈的出生點,而且從出生點出生,同時經過時間戳對子彈的速度插值,即可獲得一個完整的加速追趕行爲,而不會出現離開發生點很遠的情形。不過這種方式也有它的問題。因爲去除了回滾,邏輯層沒法再超前於網絡驅動幀演算,只能在渲染層作一些預測插值。微信

    此外,去除回滾後,因爲邏輯幀依賴於網絡驅動幀,網絡抖動的狀況下,本地的AI也會隨之抖動,沒法再平滑地向前演算渲染。至於玩家,由於其沒有AI,只是依賴於玩家的操做,所以是否回滾對其影響不大,可是對機器人AI的影響在網絡抖動較大時會有比較大的差異。在手遊中還能夠在UDP上定製可靠傳輸協議來提高傳輸效果,可是瀏覽器長連接走的是TCP上的WebSocket協議,是沒轍的了。網絡

    從以上分析來看,若想既兼顧性能,又能獲得較好的AI預測體驗,那麼最好是能容許部分範圍內的前向計算+回滾。除非是劃分的模塊狀態相互之間徹底獨立,不然要對單個狀態模塊加以回滾又保持不一樣客戶端的一致性,那隻能在限制反作用的有限範圍內去處理。性能

    另一種方法就是摒棄幀同步,採用狀態同步,天然包括AI的狀態也要同步下來。可是在前向演算時,依然能夠在客戶端編寫AI(這時能夠利用ECS模式複用服務器的AI代碼),而且能夠只依賴於部分狀態進行演算。AI的表現並不要求是徹底準確的,在狀態依賴與演算的策略上也有空間能夠發揮。客戶端能夠選擇在服務器同步部分必要的狀態後便可回滾/演算,而不一樣客戶端並不要求在每一網絡幀計算上都保持一致,只要在關鍵事件上由服務器同步一致便可。好比在一張大地圖上,只對玩家當前所在的區域進行演算便可,服務器AOI同步過來時再作回滾校訂。不過具體細節我也沒有再思考了,有機會再進一步實踐吧。而若是是手遊項目並且附帶至關AI(技能事實上也是AI之一種,一旦釋放就要走完一個固定的流程),對實時要求也很是高的話,我仍是會優先選擇幀同步+回滾的方案的。spa

相關文章
相關標籤/搜索