What every programmer needs to know about game networking
作者:rellikt@gmail.com
首發鏈接:http://blog.csdn.net/rellikt/archive/2010/09/12/5878447.aspx
原文:?http://gafferongames.com/networking-for-game-programmers/what-every-programmer-needs-to-know-about-game-networking/
介紹
作為一個程序,你想過網絡多人對戰游戲是怎么做出來的嗎?
從外行的角度來看多人對戰游戲是很神奇的:2個或者更多的玩家在同一個時間經歷了相似的游戲經歷,感覺他們就像在同一個虛擬世界中游戲一樣。但是作為程序員我們卻知道事實并不是他們想象的那樣的,他們看到的絕大多數其實都是假象。他們所認為玩家間同時經歷的很多事件其實只是一種逼真的模擬。不同玩家間或多或少會存在不同步,程序員就是讓這些不同步在玩家眼中變得同步起來。 by rellikt
P2P的回合類通信游戲
最開始的時候游戲的網絡拓撲模型是P2P結構的,所有的機器都是P2P網絡中等價的節點,他們互相發送需要的信息,不需要任何中轉。這種網絡拓撲模型在很多RTS游戲中還很常見,甚至很多程序員在思考網絡的時候第一個想到的模型就是這個模型,因為這個模型和我們平時人與人間的交流模型的確很像。by rellikt
這個模型的基礎思路是把游戲抽象成為一輪一輪的回合,在每個回合中把所有的輸入轉換成一個個指令,然后在每個回合的開始時候處理這些指令,這些指令導致的行為自然會推動游戲狀態機的運行。我們在一個類似SC的RTS中最常見的指令會有:移動,攻擊,建筑等。選用這個模型的話,我們只要保證所有的用戶都有相同的指令集,并且他們的初始狀態相同就不會有問題了。
以上這段介紹對于一個類似SC1的RTS游戲的網絡模塊來說,可能有點太過于簡單了。事實上我也只是說了個大概的概念,如果你有興趣可以參考這篇介紹帝國時代的論文。
以上提到的網絡拓撲模型看上去的確是簡單實用,但是同時這個模型的簡單也帶來了以下的一些限制:
這個模型的在可重演性上是要求相當高的:因為我們只負責指令的同步,不負責指令具體運行結果的同步,我們很可能會發現因為一個步兵在尋路的時候稍微走的有點不一樣的地方,就導致了一場戰斗的結果大不相同。而這樣情況導致的蝴蝶效應已經足夠讓我們游戲體驗崩潰了。 by rellikt
這個模型的第二個缺點就是必須保證在一回合開始的時候,所有玩家的指令都已經到齊,不然這回合的指令就無法開始模擬。這就是說,所有玩家的延遲其實是取決于延遲最大的玩家的延遲。RTS為了解決這個問題,通常會設置一些前搖動畫和音樂來讓玩家以為指令已經開始模擬了,但是實際上真正指令開始模擬的時間肯定是會和玩家輸入時間有延遲的。這些偽裝對玩家很好的掩蓋了這點。
這個模型的最后一個缺點就是玩家必須從一開始就加入游戲。也就是說通常這里游戲的模式是在大廳中開一個房間,然后玩家加入游戲開始玩,這種模式不會允許玩家中途加入。雖然把從開始到中途加入這段時間的指令存起來,然后讓想加入的玩家進行模擬,然后再加入是一種在理論上可行的解決方案,但是這個方案牽涉到的問題對游戲中的可預測性,可同步性的要求非常高,另外指令多了模擬時間也是問題,至少現在還沒看到哪個這類模型的游戲做出了類似的嘗試。 by rellikt
盡管我們上面提到的這個模型有種種令人不滿意的缺點,但是在現實中的RTS游戲(比如星際1,紅警1,帝國1)里面基本都采用了這個模型,因為這類游戲中我們會操作成千上萬的單位,要一個個的去同步每個單位的狀態是不科學的,我們只能去同步指令,然后通過指令去同步游戲,推動游戲狀態機的正常運行。
事實上在最新的RTS游戲中可能已經拋棄了P2P模式,但是回合類通信的概念還是會得到保留,否則是無法完成成千上萬個單位的同步的。?
但是時代在進步,游戲的類型也是千差萬別的。原始的P2P的回合制在現代的很多其他類型游戲中已經基本被拋棄了。接下來我們來看看在Unreal,Quake,Doom中引入的FPS游戲的網絡模型吧。
客戶端/服務器端模型(C/S)
在最初的Quake游戲中,Doom采用的也是P2P的回合制通信模型,結果發現除了局域網低延遲高帶寬的情況,其他的情況下游戲性都不能讓人滿意。by rellikt
事實上,玩家的確可以通過虛擬局域網的軟件來模擬局域網,然后通過局域網的模式進行連接。但是連接的情況實在是很悲催。那些用蜘蛛網上網的玩家就不提了(14.4kbps PPP connection或28.8kbps貓上網)。他們肯定是最杯具的。就是有寬帶的玩家也不見得能好到那里去。因為P2P模型帶來的高延遲在FPS類游戲中是無法被很好的掩蓋的,對玩家輸入的正式模擬必須等到所有玩家的指令到達以后才能處理,也就是說你的延遲取決于當前玩家中延遲最爛的那個,如果說你的隊友中有300ms延遲的,那么你點一下射擊鍵,就得過300ms以后才才會做出射擊模擬。這樣的情況讓很多網絡不好的玩家只能望洋興嘆了。
為了讓更多網絡不好的玩家也能正常的玩。1996年,John Carmack在推出Quake的時候使用了C/S架構取代掉了傳統的P2P架構。C/S架構和P2P架構不同,P2P在每個客戶端都會運行游戲邏輯,游戲顯示等完整的游戲,因此P2P對于游戲的可重演性要求是相當高的。C/S架構的概念就是在服務器端跑所有的游戲邏輯和輸入響應,在客戶端只跑所有的游戲顯示,這樣的話客戶端只需要把自己需要的一些狀態同步下來,把用戶輸入發給服務器端,然后顯示結果就可以了。 by rellikt
拿傳統的FPS來說,理想的C/S結構中,客戶端只需要發送自己的輸入比如移動,轉身,開火給服務器端,然后再從服務器端把自己和周圍可見玩家的位置,朝向,動畫狀態等信息同步下來,做一下合理的插值,使其各個角色看起來足夠流暢,然后顯示出來。一個基本C/S架構的FPS就完成了。
比較一下上面兩個模型,我們發現C/S架構最大的優點就是把延遲從最卡的玩家的延遲改變為和服務器連接的延遲。另外使用這個架構中途加入玩家的概念也很容易就能實現了。最后在發包上面來說,在帶寬上的要求也低了不少。只需要把輸入發給服務器端就夠了。
但是純理想的C/S模型還是有不足的地方,那就是延遲,FPS對于延遲的要求是相當高的,互聯網上兩個端點間的lag有300ms是很正常的,如果說一個轉身指令要等300ms以后才能響應的話,以9s/m跑步的速度來算,玩家就已經跑出將近3m了,也許早就掉到溝里面去了。
為了讓更多使用爛網絡的玩家能夠加入到游戲中,John Carmark在推出Quake的時候也引入了客戶端預測的新技術。by rellikt
客戶端預測
其實在早期的FPS游戲中,我們的確會碰到按一個鍵要等半天才能反應的情況,而這個時間就是和你的網絡延遲有關的,有些強的玩家甚至能夠適應這種情況,提前做出預判操作。但是在現代的FPS游戲比如COD等游戲中,你已經再也不會有這種體驗了,那我們現代的FPS游戲是用什么手段來移除這些延遲的呢?
這部分的技術一般分兩塊:客戶端預測和延遲補償。這些技術在Unreal引擎中的網絡部分中都有介紹。這里我們著重先討論一下關于客戶端預測的概念。
John Carmack在推出Quake的時候提到:我會在新的Quake中引入客戶端預測的概念,也就是說客戶端不只是簡單做一些同步和顯示。他們還會做預測,也就是說在得到服務器端數據之前,客戶端就會預先對輸入的結果做預測并且預測其他可見玩家的行動,并且立即進行顯示,這會使得現在的客戶端需要物理,游戲邏輯在本地運行。原本完美的C/S模型在這里就顯得不那么完美,但是還是讓我們面對現實吧,現實本來就不完美。by rellikt
我們現在的情況就是客戶端在接受到本地輸入以后會直接運行一部分的游戲邏輯代碼,對用戶狀態進行判斷,然后進行模擬,再顯示出來,客戶端不會等到服務器端的數據到達以后再進行模擬了,這樣說來對客戶端來說延遲已經不存在了。
但對于客戶端預測來說,重點其實不在預測上,而是在同步上。我們只需要使用相同的代碼,預測自然就不會有問題了。但是當服務器和客戶端對于結果出現分歧的時候怎么同步就是一個大問題了。
談到這里你就會想,如果說客戶端已經預測了游戲的進程,為什么不讓服務器端去同步客戶端的結果呢?這樣的方案看上去是不錯的,但是附帶而來的作弊問題卻是很嚴重的,如果服務器不進行模擬,而是簡單的同步玩家狀態,那么瞬移,無敵等外掛就會很常見了。玩家只需要模擬這些,發出對應的包就可以了。現在流行的許多MMORPG中的外掛就是這種同步而產生的結果。事實上由于對于服務器性能上的考慮,MMO中往往只會最簡單同步一些狀態信息和事件信息。 by rellikt
因此在Unreal的實現中Tim Sweency決定讓服務器和客戶端分別模擬游戲,來實現用戶端預測來消除延遲。Tim Sweency在Unreal Networking Architecture中寫道“The Server is The Man”.
既然這樣的話,我們很容易想到一個有趣的問題。我們的原則是用戶狀態以服務器模擬為準,客戶端必須即時同步服務器上的狀態,使自己的行為和服務器上的一致。現在的問題是:由于延遲是客觀存在的,所以服務器上的數據總是比客戶端的要慢。事實上客戶端能夠同步到的數據只能是ping值(數據包在客戶端和服務器端一個來回的時間)以前那個時間點的數據。
如果客戶端只是簡單同步當前獲得的服務器端的數據,那么結果就是,客戶端會把ping值以前的狀態給同步回來,而做出的修改就正好是我們要做的客戶端預測的那部分,如果真的這么做,那么我們的客戶端預測就完全是無意義的存在了。by rellikt
這里Tim Sweency采用的方法是采用兩個環形的緩沖,一個記錄客戶端的狀態,我們稱其為狀態緩沖,一個記錄客戶端的操作,我們稱其為操作緩沖,這兩個緩沖的長度應該長到至少可以容納ping值這段時間的狀態和操作。我們每過一個固定的時間把客戶端狀態寫入狀態緩沖,每個操作都會被寫入操作緩沖。當服務器端同步來的數據到達客戶端以后,我們先提取這個服務器端數據所帶時間點,然后把這個時間點以前的數據從客戶端的兩個緩沖緩沖中釋放掉,然后再把狀態緩沖中最接近的那個時間點狀態數據提取出來,從那個狀態開始用操作緩沖中存的操作數據進行操作模擬,最后得到現在的狀態,再用得到的這個狀態來進行插值,同步。事實上在客戶端為了消除延遲我們一直在進行回滾然后重演的過程,這個ping值越大,我們需要回滾和重演的時間就越多,同時在得出的新狀態中可能需要插值和同步的幅度也會越大。
Unreal中就是這么處理延遲的,這個技巧運用得當的話,可以很有效的把延遲給掩蓋掉。Tim Sweency在Unreal的白皮書中說,如果我們的游戲的可重演性越好,我們需要的插值就越少,甚至在其他客戶端和環境變量不變的情況下,我們是幾乎不需要任何的插值和修改的。事實上在Unreal中,往往只有撞上敵人或者被火箭彈打飛了才會用到插值修改。
換句話來說只要不涉及到其他玩家的操作或者有人作弊,那么我們采用的客戶端預測往往是很準確的。by rellikt
結論
關于客戶端預測我現在只想談這么多了。如果下期有時間我會再寫一些關于延遲補償的概念或者在本文中直接修改。就是延遲補償技術的存在,是用戶能在延遲的情況下照樣彈無虛發,體驗不到延遲的感覺。
轉載于:https://www.cnblogs.com/njuzhoubing/p/4497138.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的What every programmer needs to know about game networking的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【经验】广西集体户口迁回农村原籍超级攻略
- 下一篇: Android JNI入门第一篇——He