FPS遊戲原理漫談:玩家延時與伺服器同步
易競技前言:在知乎上有玩家提問“在FPS遊戲中玩家延時都不一樣的情況下是怎樣做到遊戲的同步?”知乎網友“周愷華”對此給出較為充實的回答,有興趣瞭解這方面知識的玩家可以通過下面的回答來一窺究竟。
知乎網友“周愷華”的回覆:
宣告:下面會大量使用CSGO作為例子,因為Valve在多人遊戲的網路通訊方面做得較好,可以當做一個典型來分析。
多人競技遊戲中客戶端和伺服器的互動
遊戲中所有的邏輯判定都是由伺服器完成的,客戶端只負責傳送請求和接收伺服器的反饋,並把反饋具象化。拿CSGO做例子,玩家A拿著AK瞄準了玩家B的頭開了一槍,那麼玩家A的客戶端會向伺服器傳送一個數據包,裡面包含了誰(玩家A)拿著什麼武器(AK)從什麼位置(玩家A在地圖上的座標)向什麼方向(角度)開了一槍。伺服器收到後進行判定,這一槍的傷害會經過玩家B的頭部模型,判定為爆頭傷害,數值為XX,判定玩家B死亡。伺服器再向所有玩家的客戶端通訊,更新當前遊戲狀態,其中包括玩家A用AK爆頭擊殺了玩家B,也會包括其他資訊,比如玩家們的位置等等。玩家A收到通訊後顯示擊殺了玩家B,玩家B則會收到被擊殺的資訊。
接著講一個概念,叫伺服器的通訊頻率(tick rate)。事實上伺服器不是百分百實時地向玩家通訊來更新遊戲狀態的,那樣需要的計算量很大,同時對網路頻寬的要求也會高的不現實,因此伺服器會以一定的頻率來進行通訊。
CSGO在娛樂模式下通常採用60Hz的頻率,也就是說每過1/60秒玩家的客戶端就會收到一次新的資訊。如果回到上面那個例子,那麼實際情況應該是:玩家A拿著AK瞄準玩家B的頭開一槍,你的客戶端會向伺服器傳送一個數據包,伺服器接收後進行判定。判定完成後等待至下一次通訊,再向所有玩家更新遊戲狀態。也就是說遊戲過程其實是一個離散的過程而非連續過程。
易競技配圖:CSGO是近年來不錯的FPS遊戲
如果玩家的Ping很大,伺服器會怎麼辦?
上面的例子都假設客戶端和伺服器之間的延遲無窮小,那麼當玩家Ping很大的時候會發生什麼的呢?
假設玩家A與伺服器之間存在100ms的延遲(單向,往返則是200ms),其他玩家的延遲忽略不計,伺服器的通訊頻率足夠大(頻率不夠大還會造成其他很嚴重的問題,這個放在後面講)。玩家A在某一刻向伺服器傳送了一個請求(比如向前走),那麼這個請求會在100ms之後到達伺服器,伺服器判定後返回結果,再經過100ms你的客戶端會收到確認,伺服器已經把你的位置向前移動了若干距離。假設客戶端在沒有收到任何伺服器的更新前畫面都不會變化,那麼在這200ms內你就會覺得遊戲“卡頓”。
實際上很多遊戲裡中你會在這200ms裡看到你自己是在向前走,其實那只是客戶端“擅自”在繪製你前進的樣子,這是一種延遲補償策略,稱為“客戶端預測法”。即客戶端能夠大致預測遊戲未來的走向,因此在接收到伺服器更新前會把預測到的畫面先繪製出來(比如移動、武器的開火效果、彈藥計數的變化等)。客戶端收到伺服器通訊後如果資料有出入則立刻糾正為伺服器提供的資料。因此在延遲很大的時候玩家會發現“明明往前走了過了一會又瞬移回到之前的位置”的原因,或者是“明明開了好幾槍而且也都顯示了但過了一會彈藥計數只減少了一點點”。
既然扯到這裡了那就認真的講下延遲補償策略(lag compensation),一般來講補償策略分伺服器端和客戶端兩類,上面提到的預測法屬於客戶端一類,其他的客戶端策略還有插幀法。所謂插幀法就是客戶端會記錄之前一次從伺服器收到的資訊,然後在接受到下一次通訊的時候不立刻更新遊戲畫面,而是逐漸的更新畫面(比如兩次通訊間玩家B移動了10單位距離,客戶端會繪製玩家B以一定的速度移動了這10單位距離,而非立刻繪製玩家B瞬間移動了10單位距離)。插幀法的問題在於如果玩家並未沿直線運動且其直線路徑中有本應不能通過的物體存在時(比如繞過一堵牆),客戶端會繪製出該玩家穿牆而非繞過去的動作。
伺服器端處理延時同步常用的策略
1.“眼不見為淨”法,伺服器不去補償玩家的延遲是一個合理的做法,特別是當遊戲內進行的事件非常多(想想《行星邊際2》裡面千人同圖混戰的情形)。浪費寶貴的伺服器資源去補償個別玩家的延遲是不明智的。這個策略的缺點很明顯,就是玩家有可能會對遊戲體驗不滿意。
2.“倒帶”法,採用倒帶法的伺服器會記錄剛剛過去一段時間內(比如0.5秒)遊戲內的所有資訊。當一個有延遲的玩家(比如200ms)向伺服器傳送一個請求,那麼伺服器在處理這個請求的時候會調取0.2秒前遊戲的狀態然後進行判定,在把判定結果對所有客戶端進行同步,如此一來該玩家的操作雖然有延遲但也能與他/她所看見的畫面一致。該策略的最大問題在於它讓不同延遲之間的玩家被迫體驗較大的延遲。舉個例子,假設遊戲裡擊殺時間(TTK)足夠小,玩家A(10ms延遲)和玩家B(延遲200ms)對射,兩人都是空血(一次攻擊即死),A比B先開火(時間差很小,比如50ms)。玩家A的次攻擊很快(10ms後)就得到了處理並記錄在伺服器中,玩家B被判死亡。然而在200ms後玩家B的請求到達,伺服器倒帶0.2秒,此時玩家AB都未死亡,因此玩家B的攻擊有效,玩家A也被判定為死亡。如果沒有延遲,那麼伺服器應該會判定玩家B死亡,因此玩家B將無法攻擊,玩家A應該存活。換句話說採用倒帶法的伺服器裡如果有一個延遲很大的玩家將會拖累其他低延遲玩家的遊戲體驗。
下一頁:低通訊頻率會帶來的災難
以BF4為例,低通訊頻率會帶來的災難
前面的例子都是以伺服器的通訊頻率足夠高為前提的,下面簡要描述一下低通訊頻率帶來的問題。這裡我要舉的例子是大名鼎鼎的《戰地4》(下稱BF4)。
BF4在剛剛釋出的時候可謂是Bug滿天飛整一個就是半成品,其中非常嚴重的就是網路通訊問題(netcode issue)。根據製作組DICE提供的資訊,BF4的通訊頻率是10Hz。這是一個相當低的設定了,大多數FPS的通訊頻率在30Hz左右,而CSGO為60Hz,電子競技比賽時一般伺服器的通訊頻率還會提高到120Hz(因為比賽時大家都是在一個局域網裡所以延遲很小高頻通訊能夠更加精確反映遊戲內狀態)。由此帶來什麼糟糕的後果呢?
易競技配圖:銷量很高但是口碑爭議的BF4
1.遊戲體驗的不連貫。BF4的客戶端,和其它很多遊戲一樣有應用客戶端預測法來補償延遲。但是因為伺服器更新的頻率是在太慢了(0.1秒才更新一次,人類的反應時間差不多略小於0.1秒,即玩家已經可以感覺到其中的不連貫)。拿移動來舉例吧,玩家正常的前進,突然玩家的延遲很短暫的增加了一下然後又回到正常,那麼其中某一次移動的請求就會花更多的時間到達伺服器。客戶端這邊因為沒有收到伺服器的通訊而繪製了玩家前進的樣子,0.1秒後伺服器告知客戶端實際的位移小於繪製的距離,客戶端進行糾正(瞬間向後退),而0.1秒可以前進很大一段距離了(至少肉眼可以感覺出來有變化),如此瞬移回退讓玩家感覺非常不舒服,即使延遲很小隻要有變化就有可能發生這種情況。同樣的情況適用於玩家看到的其他玩家所在的位置。在伺服器中敵人的位置往往比客戶端上顯示的要滯後,因此很多時候明明瞄準了卻打不中。相對的,如果提高通訊頻率,客戶端“擅自”繪製的畫面和伺服器資料能夠以更高的頻率進行糾正,其中每次產生的變化都不會讓玩家察覺。
2.“瞬間”擊殺(Instant Kill)。BF4裡的自動武器射速絕大多數都超過了600RPM(即每0.1秒一發),高射速武器如AEK971可以達到900RPM甚至更高,而大多數武器在近距離內都是造成25傷害(玩家滿血100)。因此玩家A完全可以在0.1秒內發射至少2發子彈,在近距離內擊殺生命值小於等於50的玩家B(這個邊界值隨射速提高而提高,如AKE971可以在0.1秒於近距離擊殺生命小於等於75的玩家)。假設玩家A開始射擊的一瞬間伺服器剛好進行了一次對客戶端的通訊,玩家A在之後的兩次傳送射擊請求都被伺服器接收並判定(玩家B死亡),而一直到玩家A開火後的0.1秒內玩家B都沒有接收到任何被攻擊的資訊。0.1秒後玩家B死亡,但玩家B的客戶端只能繪製一次玩家A開火(雖然收到兩次開火的資訊),簡單來說玩家B完全沒有還手或者躲藏的機會,因為玩家B一直都認為自己沒有受到攻擊,只是在一瞬間就被A打死。對玩家A來說整個過程沒有任何問題,而玩家B則是個冤大頭。因此BF4已經失去了競技的平衡性。
在BF4釋出八個月後(沒錯,八個月,期間釋出了數款BF4的DLC),發行商EA(果然不要臉)終於決定要修復這些問題。一開始他們決定吧BF4的伺服器端通訊頻率提高到30Hz,但是BF4是一款複雜度遠遠超過其他FPS遊戲的一款作品,30Hz的通訊頻率就已經讓很多玩家的電腦吃不消了。後來採取了一個折中策略,玩家附近的遊戲狀態以30Hz的頻率進行更新,而距離玩家較遠的資訊繼續以10Hz的頻率來更新。同時還提供一個選項允許客戶端以更高的頻率向伺服器“索要”資料(當然其中造成的硬體負擔不可小覷)。目前BF4的網路通訊已經恢復到“可玩”的狀態。根據某些資深玩家推測,BF4的伺服器無法以超過30Hz的頻率進行通訊,主要原因是其引擎寒霜3核心有缺陷。因此BF4不太可能成為一款競技遊戲走向電競舞臺。