編輯:關於Android編程
寫在開頭:已經研究生畢業快一年半了,一直在一家游戲公司做客戶端研發。至於這篇文章講的卻是服務端的東西,主要是因為以前一直沒想寫博客,學到的東西也一直記在本子上就得了。本人喜歡有劇情的東西,像RPG游戲(仙劍愛好者),有劇情的電視、電影,還有競技類型的游戲,像dota/2、王者榮耀等。最近在做的項目和玩的王者榮耀都涉及到游戲的同步問題,王者榮耀做的不錯,但也有自己的問題,就准備研究下這方面。本文的內容是從這兩個網站https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking和 http://www.gabrielgambetta.com/fast_paced_multiplayer.html學習總結出來,並實現了一個demo,效果也確實不錯。
好了,下面說點具體的內容,所有的內容以moba類游戲為例。
所有moba類游戲或者一些對同步要求比較高的游戲都面對一些具體的問題。一、如何讓玩家體驗到非常流暢的體驗,而不是一頓一頓一卡一卡的表現等。二、如何防止玩家作弊。
針對玩家作弊,我進入游戲界最先聽到的一句話就是“永遠不要相信玩家”,一切操作都在服務端實現。但是呢由於手游的崛起以及流量的費用,一些手游還是會部分的相信玩家。
扯遠了,一句話就是“在服務端進行驗證”。那麼最簡單的實現就是玩家的任何操作都先向服務端發送請求,然後等待服務端的響應做出相應的反饋。如圖一所示。客戶端想向右移動一個單位,先向服務端通知,服務端反饋給客戶端結果。客戶端在100ms的延遲之後執行向右移動一個單位的操作。
圖一
很明顯上面的結果是,玩家點了向右移動的按鈕,過一會才會向右移動(體驗很差)。
優化一下:既然上面體驗不好就優化一下呗,可以發現如果我點擊向右移動按鈕,就直接向右移動,而不是等服務端的響應就會比較好。可是這就引發了問題二,玩家作弊怎麼辦?大家可能已經想到了,我先向右走然後等服務端的響應,等服務端發送過來響應的時候再檢查一下,當前客戶端的狀態是不是對的,如果對的就好了,不對的可能就是客戶端作弊了,比如說跑的快了。如圖二所示。圖二
問題:但是上面又有一個問題,如果服務端響應的較慢,在服務端響應回來之前玩家已經又進行了一次操作怎麼辦呢?如圖三所示。
圖三
一個可行的方案是:客戶端沒法送一個操作給服務端的時候,加上一個序號。客戶端繼續按照上面介紹的方式去運行,不用等待服務端的反饋。當服務端的反饋到來的時候,客戶端不再是去檢測當前客戶端的狀態和服務端反饋的狀態是否一致,而是要檢測客戶端當前的狀態與服務端反饋的狀態+還未執行的客戶端狀態是否一致。如圖四,當服務端反饋#1時,客戶端的位置為12而服務端的反饋的位置是11, 但是服務端反饋的位置11+#2的操作的結果是位置12所以驗證時合法的。同理在服務端反饋#2的時候,驗證的結果也是合法的。可見使用這樣的方法,可以保證合法的玩家的用戶體驗很不錯,同時能檢測到作弊的玩家,對於作弊的玩家怎麼處理可以根據項目的具體需求具體操作。
圖四
上面解決了客戶端內只有自己的玩家的問題,當有其他玩家的時候,如何同步其表現呢?
我們還是從最簡單的開始考慮。如圖五所示,那就是服務端直接同步客戶端1的操作結果給客戶端2,客戶端2直接設置玩家1在客戶端2的位置,你會發現在客戶端2上客戶端1的玩家的位置一跳一跳的。
一個可行的簡單的優化是:既然你一跳一跳的表現不好,那我們就讓它平滑一點呗(游戲中常用的方式)。讓它在一段時間內以一個恆定的速度從當前位置移動到服務端反饋的位置。這樣表現會稍微好點,但是當我們設定了一個恆定的時間或者恆定的速度,都會出現玩家很快移動到目的地停下來等待下次操作或者還沒移動到目的地就要向下一次目的地移動,一直跟蹤不上節奏。聰明的讀者可能想到,出現這個的問題是因為我們無法預測玩家下一次到到達的位置,無法動態的平滑的調玩家的移動速度、移動時間來剛好達到目的地。一個巧妙的方式是“我們不要追蹤當前即時的目標點,而是追蹤一個我們已知的目標點”。如圖六所示,我們來介紹這句話的理解。由於服務端會同步別的客戶端的玩家的位置給你。在當前時刻了客戶端2已知知道了客戶端1經過第二次操作跑了位置12和上一次操作玩家在位置11.在當前時刻,根據一個延時,算出客戶端1的玩家在一個延時之前的位置,把它表現出來,這樣客戶端1的表現就會很流暢。比如設置延時時間為0.1s,對人眼而言也沒有什麼大的誤差。對於像cs:go這樣的游戲,還可以通過在服務端上做延時回放可以得到精確的位置,這裡就不介紹了。想看的話可以去提供的鏈接1valve上去看。
圖五
圖六
說了這麼多具體怎麼實現呢?
由於涉及到客戶端和服務端,要是都講的話,只服務端都得介紹很久。這裡我只講下實現的具體思路,特別想知道具體的實現或者自己實現有問題的,可以聯系我一起討論下。
客戶端:
一個全局的操作序號: seq
有一個記錄上傳服務端的操作序列,記為self_op
一個記錄其他玩家的操作反饋,記為other_op
玩家操作時:
記錄到self_op中
同時seq++
在客戶端直接操作
定時器:
定時上傳self_op到服務端上去
服務端回調結果:
如果是自己的:
根據反饋的操作序列+剩余的操作序列self_op與當前客戶端玩家狀態進行驗證
合法 ok 不合法:懲罰
如果是其他玩家的:
記錄到other_op中
logic循環:
根據other_op和設定的延時時間計算並設置其他玩家的狀態
服務端:
根據收到玩家的op,計算玩家的狀態,存儲到status中
logic循環:
同步當前所有的玩家狀態給同一個team的所有玩家
(注:這是原理的實現,具體項目實現肯定還要進行一些針對性的優化)
好了,第一次寫博客。 謝謝大家!
這個CardStackViewpager的靈感來自Github上面的 FlippableStackView開源項目,而我想實現的效果方向上恰好與FlippableStac
前言在開發中常要處理橫豎屏切換,怎麼處理先看生命周期申明Activity 橫豎屏切換時需要回調兩個函數 ,所以在此將這個兩個函數暫時看成是Activity 橫豎屏切換的生
現在市場的Http框架很多,比如我們熟知的NoHttp、Retrofit、Volley、android-async-http等上層框架,HttpURLConnection
使用數據線調試應用難免不方便,本篇博客介紹使用ADB Wireless工具,當手機和電腦處在同一網絡下,實現無線調試應用。ADB Wireless能夠讓手機用無線來取代U