“網絡很卡,英雄動不了。”
“我按技能了啊!可ping值這么高,我有什么辦法!”
我們在自己或是在看主播玩游戲的時候,經常會聽到這樣的抱怨---網絡太差,影響游戲體驗。
不知道大家發現沒有,這種抱怨一般都是玩家在玩RTS/MOBA(王者榮耀、英雄聯盟、DOTA、魔獸爭霸、星際爭霸)游戲時發出的,而在玩其他同樣也需要聯網的游戲,比如卡牌(爐石傳說)、MMORPG(魔獸世界)時,大家很少抱怨網絡卡。這是什么原因呢?
首先,我們先來看看一下什么叫“卡”。中文“卡”,從字面意思了解,差不多是“停住/頓住/不動”的意思,具體來說,是畫面或者人物不動了。而網絡“卡”的英文,是lag,意思則是“延遲”。綜合兩者,我們基本可以認為,卡 = 停住 + 延遲。
其實所有網絡游戲,都會“卡”,因為:
1.網絡歸根結底是通過“電介質”連接的,目前最快速度的傳輸介質是光纖(傳播速度約300000000m/s),假設中國和美國玩家聯網玩游戲(物理距離7000000m),理論上至少也會有0.023秒的延遲;
2.電腦和電腦、手機和手機之間不是直接通過光纖連接的,而要通過路由器、基站...等連在一起,中間任何一個環節出了問題,都會導致“卡”;
3.網絡帶寬是有限的,你的王者榮耀要跟其他人的英雄聯盟爭搶帶寬,爭不過的時候就會“卡”。
4.如何規避上面的問題,盡量做到“不卡”?最好的方法是在一個小空間里組件局域網游戲,比如網吧,比如電競現場,這也是為什么比賽為了做到公平幾乎都要在線下舉辦的原因。
而像卡牌游戲和MMORPG,其實也會卡,只不過玩家感受不明顯而已。一個具體的例子,在玩魔獸世界時,有時會看到人物“瞬移”,會看到技能明明剛釋放就打到了人身上,就是“卡”導致的。
既然所有聯網游戲理論上都會“卡”,那么游戲開發人員要做的是,盡量讓人感覺不到“卡”。舉一個實際的例子,現在英雄在釋放技能的時候都會有“前搖”和“后搖”,其實這個設定最初是游戲開發人員為了讓玩家在視覺上感覺不那么卡所做的技巧。假如“前搖”+“后搖”0.2秒,那么這個技能在0.05秒時放出,或是0.15秒時放出,雖然兩者有0.1秒的延遲,但是對於玩家來說視覺上都能接受。
所有的技巧,都是為了能夠更好的“同步”玩家操作。什么叫“同步”呢?同步,即在一場戰斗中,開發人員采用一定的技術和技巧,讓每個玩家感覺戰斗流暢。
模式一:狀態同步
還是以王者榮耀為例,如果王者榮耀采用的是“狀態同步”(實際並非如此),那么客戶端(玩家手機)和服務器(游戲公司)的數據是這樣交互的:
若玩家1的英雄在地點A,點地點B,此時英雄並不會立即走到地點B,而是會走這樣的流程:
1.客戶端發送一條消息到服務器,包含英雄速度、行走方向;
2.服務器收到消息,不斷計算英雄的位置(例如每0.01秒計算一次結果,取決於服務器性能和物理引擎),然后實時廣播給每個客戶端(發送給玩家1、玩家2....玩家10);
3.客戶端收到服務器發來的英雄位置信息,這才刷新英雄的位置。
可以看到,狀態同步的核心原則是服務器作為計算大腦,戰斗的計算規則布局在服務器上,每個客戶端都只是“告訴”服務器要做什么,由服務器決定怎么做,再“回告”客戶端,客戶端才能做表現。
而如果王者榮耀采用的是狀態同步,會有三個問題難以解決。
一是流量問題。想想看,玩家只是點了一下地板,為了保證英雄每時每刻都在正確的位置上,在英雄停下來之前,客戶端會收到大量的位置信息。如果這個英雄可以發射“霰彈”,假設子彈有50顆,服務器就要每時每刻計算並同步50個子彈的位置,服務器壓力和流量可想而知。
二是卡頓問題。因為網絡延遲總是存在,如果采用狀態同步,英雄會出現只要一卡就靜止不同的情況。比如玩家1的網絡不好時,他會看到所有的英雄和小兵都像幻燈片一樣卡着移動(dota1經常出現),顯得十分“不流暢”。
三是操作合並的問題。例如,玩家A在0.1秒時用了“閃爍”到了玩家B身邊,在0.3秒時用了技能把玩家B殺死,在0.5秒時又閃爍回原地。如果網絡良好的情況下,玩家B看到的是,玩家A先閃到自己身邊,再殺死自己,再回到原地;而如果玩家B的突然網絡不好,延遲大於0.5秒,就會看到玩家A在原地殺死了自己,這是因為延遲導致客戶端在同一時刻收到2個位置,客戶端並不知道之前的位置是在什么時候被改變的(就像一個菜譜如果只有烹飪順序沒有火候時間,你無法還原出這道菜)。這也是在一些游戲上會看到英雄小兵莫名死掉,或者瞬移(據說騰訊的《全名超神》因為采用狀態同步,存在此問題)。
那為什么魔獸世界這樣的MMORPG游戲要用幀同步呢?
一是因為地圖和人數。MMORPG的地圖一般非常大,一個地圖上的人數可能非常多,需要能計算一張地圖上所有人的行為。而對於客戶端來說,既不能要求每個客戶端都能計算所有的行為,也不會把一張地圖上的行為同步給客戶端。一般的做法是AOI(Area of Interest)管理和同步,把大地圖分成很多個小區域(如ABCDE區域),服務器只同步小區域內的玩家行為(玩家在A區域,服務器只告訴他A區域中的人和NPC的行為)。
二是狀態同步難以作弊。因為所有的數據都在服務器上,如果服務器不同步數據,所有客戶端並不知道其他玩家的位置和狀態。而由於魔獸爭霸、王者榮耀用的不是狀態同步,所有玩家的位置和狀態在客戶端都有保存,所以外掛只需要把黑色迷霧隱藏,便可以全圖透視。
總結:狀態同步,戰斗計算寫在服務器上,服務器告訴客戶端如何表現,怎么移動、是否回血/死亡/放技能都由服務器決定。缺點:耗費流量、服務器計算壓力大、網絡差時會出現“莫名移動及死亡”。優點:難以作弊,超大地圖依賴服務器計算必須使用狀態同步。
模式二:幀同步
我們看看幀同步是如何解決狀態同步的三大難題的。
一流量問題。狀態同步所有的計算都放在客戶端,服務器只是轉發操作。例如玩家A點了移動,服務器並不需要每時每刻同步英雄的位置,只需要告訴每個客戶端,玩家A移動的速度和范圍,由客戶端自行計算移動軌跡;
二卡頓問題。幀同步在網絡抖動時也會卡頓,不過可以用指令buffer和time warp來“掩蓋”,讓玩家感覺流暢;
三操作合並的問題。幀同步同步的是指令而不是狀態,並給每個指令加上了時序(幀序號),不管延遲多大,指令都是有順序的。
關於第三點,舉一個生活中的例子便於理解。
狀態同步就像天氣預報,幫你算好天氣的變化,每小時告訴你是出太陽還是下雨,比如12時太陽,1時也是太陽,可是如果12:20開始下雨,12:40停雨了,你就沒辦法了;
幀同步像氣象站,直接告訴你12:10有大風、12:15有暖濕氣流,你自己計算整個天氣的變化。
幀同步的核心原則是客戶端是計算大腦,服務器只作為轉發點,戰斗的計算規則布局在每個客戶端上。
幀同步實現方案也被稱為“鎖步”,即:
1.服務端定好一個時間間隔(例如0.1秒,每秒10幀);
2.每個客戶端收集操作指令,按照這個間隔上傳指令,如第一幀,客戶端在第0.05秒時上傳操作;
3.服務器收集所有客戶端的第一幀指令后,在第0.1秒時廣播給所有客戶端;
4.所有客戶端收到指令集合(第一幀),放入邏輯引擎中(如box2d引擎),根據引擎計算結果展現效果;
5.客戶端把第二幀(0.10秒-0.15秒,或0.06秒-0.15秒)的操作上傳服務器,以此類推。
關於第4點,如果是收集的是0.10秒-0.15秒的操作,則在0.06秒-0.10秒這段時間的操作都必須隱匿,個人感覺可以采用0.06秒-0.15秒。
以上是在網絡狀況良好的情況下,即網絡抖動不明顯。如果網絡抖動太大,比如玩家A的延突然從0.01秒變成0.5秒,由於服務器必須要收集所有客戶端的操作,則其他所有玩家都要等待0.5秒后才能收到指令。客戶端的表現為:本來大家一點都不卡,有一個玩家延遲了0.5秒,所有玩家的畫面都會卡0.5秒,接着所有玩家的操作都變為延遲0.5秒;只要有一個玩家掉線了,所有玩家都會一直卡住,直到這個玩家重新連上線(魔獸爭霸3便是這樣的)。
但是不管怎么卡,該有的操作還是會有,還是以“玩家A在0.1秒時用了“閃爍”到了玩家B身邊,在0.3秒時用了技能把玩家B殺死,在0.5秒時又閃爍回原地。”這個為例,當玩家B的延遲變為0.5秒時,在幀同步下,客戶端表現為:所有玩家都看到“玩家A在0.1秒時用了“閃爍”到了玩家B身邊,在0.8(0.3+0.5)秒時用了技能把玩家B殺死,在1.0(0.5+0.5)秒時又閃爍回原地。”
一人卡而卡全部人的方式顯然不符合人性,玩家希望看到的是,你卡就卡你自己好了,不要連累其他人。於是,就有了“樂觀幀鎖定”的概念。
具體流程是這樣的:
1.服務端還是定好一個時間間隔(例如0.1秒,每秒10幀);
2.每個客戶端收集操作指令,還是按照這個間隔上傳指令,如第一幀,客戶端在第0.05秒時上傳操作;
3.服務器不再等收集所有客戶端指令,只要到了該幀結束的時候(第一幀為第0.1秒),直接廣播指令。
這樣就會出現一個情況,如果客戶端A延遲很大,他的第一幀指令會在第1+N幀時被服務器接收到,這時服務器可以按照第1+N幀的指令才處理。
也就是說,玩家A在0.09秒時放大招,如果他的延遲時0.5秒,則服務器告訴大家,玩家A是在0.59秒時放大招,每個客戶端再去判斷這時是否能放大招(如在0.2秒時時英雄死了,則指令無效)。
解決了其他玩家卡的問題,有沒有辦法把這個延遲高的玩家卡的問題也解決呢?有的。只要讓他“感覺”不到卡即可。
方法一:緩存指令(buffer),讓這名玩家所有的指令都延遲0.5秒執行。這樣做玩家A會感覺一直延遲0.5秒,但不會卡(即不會一下又延遲一下無延遲);
方法二:客戶端先行(time warp)+插值。例如玩家A延遲0.5秒,則他點下地板的同時,立即讓他(比原來慢得多的速度)開始轉身並開始走動,因為這時還沒有收到服務器回包,所有的行動都是“預測”行動。等0.5秒后收到指令,如果跟預測的操作一直,則加速按照原路徑前進;如果預測錯了,比如在中間被其他英雄凍住了,由於開始走的速度很慢,離正在的位置偏移很小,玩家不太能感覺到。這里的關鍵是,所有的“預測”行動,都只是客戶端表現而已,並不能在邏輯上觸發機制。比如玩家A走到了泉水旁,並不能真正回血,只有等收到服務器回包,“預測”正確后才能回血。因此把邏輯層和表現層分離很關鍵。
參考資料:
http://youxiputao.com/articles/11842
https://zhuanlan.zhihu.com/p/38468615