摘要:在基于C/S網(wǎng)絡(luò)游戲架構(gòu)下,介紹了網(wǎng)絡(luò)游戲中的網(wǎng)絡(luò)分布式對(duì)象的概念,分析了基于圖形幀移動(dòng)的錯(cuò)誤方法,提出了一種基于時(shí)間移動(dòng)的算法,并使用插值的方法改進(jìn)了該算法。最后在上述基礎(chǔ)上,提出了一種基于客戶端預(yù)測和客戶端修正的移動(dòng)同步算法。在實(shí)時(shí)性要求較高的網(wǎng)絡(luò)游戲中該算法完全能解決移動(dòng)同步問題。
關(guān)鍵詞:網(wǎng)絡(luò)引擎;網(wǎng)絡(luò)游戲;同步;客戶端預(yù)測;分布式對(duì)象
中圖分類號(hào):TP391.9文獻(xiàn)標(biāo)志碼:A
文章編號(hào):1001-3695(2007)05-0207-03
0引言
目前網(wǎng)絡(luò)多人在線游戲主要有以下兩種典型的網(wǎng)絡(luò)拓?fù)浞绞剑阂皇荂/S模式,即所有的網(wǎng)絡(luò)數(shù)據(jù)都必須從一個(gè)客戶端到一個(gè)服務(wù)器,然后又由服務(wù)器中心轉(zhuǎn)發(fā)到其他客戶端上;二是Peer-to-Peer模式,Peer-to-Peer游戲沒有專門的服務(wù)器。在Peer-to-Peer世界中,每個(gè)客戶端都要與其他客戶端進(jìn)行網(wǎng)絡(luò)通信[1]。
在基于C/S架構(gòu)的網(wǎng)絡(luò)多人在線游戲中,服務(wù)器通常扮演著重要的角色,它主宰著整個(gè)游戲世界,負(fù)責(zé)游戲規(guī)則的判定,并處理客戶端傳來的玩家輸入。客戶端和服務(wù)器以極高的速率發(fā)送數(shù)據(jù)量很小的數(shù)據(jù)包進(jìn)行通信??蛻舳私邮债?dāng)前在服務(wù)器上整個(gè)游戲世界的狀態(tài),并通過該狀態(tài)產(chǎn)生視/音頻效果并展示給玩家,客戶端也通過對(duì)輸入設(shè)備(鍵盤、鼠標(biāo)、游戲桿等)進(jìn)行定時(shí)采樣將采樣信息發(fā)送給服務(wù)器作進(jìn)一步的處理[2]。
與單機(jī)游戲相比,網(wǎng)絡(luò)游戲需要處理許多新的問題,如帶寬、網(wǎng)絡(luò)延遲、網(wǎng)絡(luò)丟包問題等。這些問題均會(huì)導(dǎo)致網(wǎng)絡(luò)同步,特別是在實(shí)時(shí)性很高的第一人稱射擊游戲中,網(wǎng)絡(luò)同步問題成了制約游戲性能好壞的關(guān)鍵因素。
1網(wǎng)絡(luò)同步中的關(guān)鍵技術(shù)
1.1網(wǎng)絡(luò)分布式對(duì)象
網(wǎng)絡(luò)分布式對(duì)象在游戲網(wǎng)絡(luò)引擎中非常重要,是多人網(wǎng)絡(luò)游戲中必不可少的技術(shù)。該技術(shù)的大體思路是:如果一個(gè)對(duì)象一旦在一個(gè)客戶端上被創(chuàng)建,則該對(duì)象也會(huì)在所有已經(jīng)連接到服務(wù)器的客戶端上被創(chuàng)建;如果一個(gè)對(duì)象在客戶端上被銷毀,則它就會(huì)在所有已連接到服務(wù)器上的客戶端中被銷毀[3]。
為便于描述,筆者將在本地客戶端上的對(duì)象定義為Player對(duì)象,非本地客戶端上的相應(yīng)對(duì)象定義為GhostPlayer對(duì)象;兩個(gè)對(duì)象具有相同的操作,但可能具有不一致的數(shù)據(jù)。圖1描述了兩個(gè)客戶端加入服務(wù)器后Player和GhostPlayer對(duì)象在網(wǎng)絡(luò)中的分布情況。
當(dāng)創(chuàng)建好分布式對(duì)象后,就可以通過分布式對(duì)象上相應(yīng)的網(wǎng)絡(luò)接口(如RPC)進(jìn)行對(duì)象到對(duì)象的通信,給編程人員的感覺如圖1中的虛線路徑:PlayerA(客戶端A)到GhostPlayerA(客戶端B);但實(shí)質(zhì)上還是經(jīng)過了以下的通信過程:PlayerA(客戶端A) 到GhostPlayerA(服務(wù)器)到GhostPlayerA(客戶端B)。
1.2基于時(shí)間的移動(dòng)
游戲時(shí)間的安排非常重要[4]。一些編程者通常會(huì)把特定的硬件時(shí)間放置到游戲循環(huán)中,即他們可能根據(jù)幀計(jì)數(shù)器更新游戲,而不是根據(jù)所經(jīng)歷的實(shí)際時(shí)間來更新游戲。特別是在網(wǎng)絡(luò)游戲中,保持基于時(shí)間的更新是保證各個(gè)客戶端同步的基礎(chǔ)。典型的錯(cuò)誤方法如下:
float t = 0.0f, TimeStep=0.01;
while (!quit)
{ProcessInput(t);…Player.position.x=Player.position.x+5;…
MoveObject(t);
Render(state);
t += TimeStep;
}
在上述代碼中,每次游戲的更新都會(huì)使玩家在x方向上移動(dòng)五個(gè)單位。假設(shè)有一個(gè)CPU為1 GHz的計(jì)算機(jī),由游戲循環(huán)的處理將消耗計(jì)算機(jī)1/60 s的時(shí)間可知, 1 s內(nèi)游戲?qū)⒏?0次,這將會(huì)使玩家移動(dòng)300個(gè)/s單位;如果一個(gè)計(jì)算機(jī)的CPU頻率為2 GHz,那么游戲?qū)⒁詭蕿?20 fps的速度運(yùn)行,即兩倍于前者計(jì)算機(jī)的幀速運(yùn)行這個(gè)游戲,這將使玩家移動(dòng)600個(gè)/s單位。如果他們是網(wǎng)絡(luò)上的對(duì)手,那么后者將能以兩倍于前者的速度在游戲世界中移動(dòng)。
上述方法顯然是錯(cuò)誤的,因此需要一種基于時(shí)間移動(dòng)的算法。目前的解決辦法是:使用一個(gè)時(shí)間累計(jì)器(Accumulator),在每次更新時(shí)將每次游戲更新的時(shí)間間隔(DeltaTime)加到累計(jì)器上;當(dāng)累計(jì)的時(shí)間大于TimeStep時(shí)就開始一次物理模擬,并將累計(jì)的時(shí)間減去TimeStep。該算法能保證游戲完全以TimeStep的時(shí)間步長進(jìn)行游戲更新。下面是該算法的核心代碼:
float TimeStep=0.01;//表示每0.01 s更新一次
while(!quit)
{float CurrentTime=time();//獲得當(dāng)前的時(shí)間
float DeltaTime=CurrentTime–PreviousTime;
//獲得上一次更新的時(shí)間
PreviousTime=CurrentTime;
Accumulator+=DeltaTime;
while (Accumulator>=TimeStep)
{ProcessInput(t);//處理鍵盤及鼠標(biāo)輸入
SimulationGhostPlayers(t);//模擬GhostPlayer的物理運(yùn)動(dòng)
ConnectionUpdate(t);//網(wǎng)絡(luò)數(shù)據(jù)的更新
Accumulator -=TimeStep;
t++;
//時(shí)間遞增,這里簡化成UINT類型的數(shù)據(jù),加快運(yùn)行速度
}
float alpha=Accumulator/TimeStep;
RenderLocalPlayer(alpha);//渲染本地Player
RenderGhostPlayers(alpha); //渲染已經(jīng)連入服務(wù)器的GhostPlayer
}
在上面的算法中出現(xiàn)了一個(gè)Alpha變量,該變量有什么作用呢?如果幀率為55 fps,物理模擬以60 fps運(yùn)行,那么物理更新有可能在兩次顯示更新內(nèi)完成或者在一次顯示更新內(nèi)完成(當(dāng)Accumulator累計(jì)到一定程度時(shí))。這種不規(guī)則的物理更新頻率將會(huì)在屏幕上呈現(xiàn)出一種感覺不真實(shí)的物理模擬。Alpha變量能保證物體平滑移動(dòng)。實(shí)質(zhì)上是在上一次物理狀態(tài)和當(dāng)前物理狀態(tài)之間進(jìn)行線性插值。
State state=CurrentState×alpha+PreviousState×(1-alpha)(1)
其中,alpha=Accumulator/TimeStep,alpha∈[0,1]。用Alpha來做兩個(gè)狀態(tài)之間的線性插值得到當(dāng)前的狀態(tài),然后對(duì)計(jì)算出的當(dāng)前狀態(tài)進(jìn)行渲染。如果要取得更好的效果,則需采用Slerp方法進(jìn)行插值。
1.3網(wǎng)絡(luò)對(duì)象移動(dòng)的同步算法
要優(yōu)雅并且高效地做到使每個(gè)客戶端遠(yuǎn)程控制自己的角色,并且盡可能真實(shí)地反映服務(wù)器上的物理狀態(tài),就需要將物理模擬做如下的結(jié)構(gòu)化[5](假設(shè)只需要處理對(duì)象的前后左右移動(dòng)):
struct Inputstruct Statestruct Move
{bool left; {Vector position;{Input input;//輸入
bool right;Vector velocity;State state;//狀態(tài)
bool forward; };UINT time;//時(shí)間
bool back;}
};
上述結(jié)構(gòu)化保證了:角色的物理狀態(tài)(State)完全由輸入數(shù)據(jù)驅(qū)動(dòng);物理狀態(tài)能被完全地封裝在一個(gè)state結(jié)構(gòu)中;并由Time來同步服務(wù)器和客戶端的時(shí)間。物理模擬由初始的狀態(tài)和輸入決定。
現(xiàn)在假設(shè)客戶端的時(shí)間已經(jīng)與服務(wù)器端的時(shí)間同步,且只考慮在一個(gè)TimeStep周期內(nèi)的過程。那么網(wǎng)絡(luò)對(duì)象移動(dòng)同步算法的大體描述如圖2所示。
上。該信息主要用于使PlayerA與GhostPlayerA同步。
(6)客戶端A根據(jù)服務(wù)器發(fā)送過來的信息對(duì)PlayerA的狀態(tài)進(jìn)行修正。
(7)客戶端B根據(jù)服務(wù)器發(fā)送過來的Inputserver來模擬GhostPlayerA的運(yùn)動(dòng),如果模擬后的運(yùn)動(dòng)與Stateserver的誤差大于一個(gè)閾值δ,則平滑地運(yùn)動(dòng)到Stateserver。
1.4客戶端預(yù)測與客戶端修正
目前為止,已經(jīng)提出了一個(gè)方法,通過客戶端的輸入來驅(qū)動(dòng)服務(wù)器上的物理狀態(tài),然后廣播該物理狀態(tài)到每個(gè)客戶端。因此每個(gè)客戶端可以維持一個(gè)與服務(wù)器近似的物理狀態(tài)。其中最重要的兩種技術(shù)為客戶端預(yù)測和客戶端修正[6]。實(shí)現(xiàn)同步通常有兩種解決方案:
(1)驗(yàn)證同步,即每條指令在服務(wù)器驗(yàn)證通過后再執(zhí)行動(dòng)作。比如,客戶端A按下前進(jìn)鍵,直到該信息發(fā)送給服務(wù)器并從服務(wù)器返回后才做真正前進(jìn)的運(yùn)動(dòng)。該方法的優(yōu)點(diǎn)是能保證各個(gè)客戶端之間絕對(duì)的同步;其缺點(diǎn)是,在網(wǎng)絡(luò)延遲較大時(shí),玩家客戶端的行為變得很不流暢。顯然這不適合實(shí)時(shí)性要求較高的游戲。
(2)采用客戶端預(yù)測的方法。該方法通過使用客戶端的輸入來預(yù)測服務(wù)器上的物理狀態(tài),而不等待服務(wù)器的狀態(tài)返回。服務(wù)器周期性地發(fā)送修正數(shù)據(jù)到客戶端,客戶端通過該數(shù)據(jù)不斷修正自己,從而保證與服務(wù)器的物理狀態(tài)同步。比如,客戶端A按下前進(jìn)鍵,不等待服務(wù)器的返回信息,而直接做前進(jìn)運(yùn)動(dòng);通過服務(wù)器返回的狀態(tài)來不斷修正客戶端的預(yù)測狀態(tài)。 該方法能保證客戶端圖形的流暢性。
客戶端預(yù)測最復(fù)雜的部分就是處理來自服務(wù)器的同步修正。這個(gè)技術(shù)的具體實(shí)現(xiàn)需要使用一個(gè)存放客戶端的QueueMoves隊(duì)列。該隊(duì)列存儲(chǔ)客戶端的上n次移動(dòng)操作。當(dāng)客戶端收到一個(gè)來自服務(wù)器t時(shí)刻的修正,它遍歷所有存放QueueMoves的隊(duì)列,找到客戶端t時(shí)刻的Move,并將服務(wù)器發(fā)送來的物理狀態(tài)與其當(dāng)前的物理狀態(tài)進(jìn)行比較。如果兩個(gè)物理狀態(tài)的差大于閾值Threshold,那么客戶端將返回到t時(shí)刻,根據(jù)服務(wù)器的狀態(tài)重新模擬t時(shí)刻后的所有過程。以下是該算法的核心代碼:
Move moves[1024]; //假設(shè)隊(duì)列的最大容量為1024
int head=0, tail=100;//假設(shè)已經(jīng)有50個(gè)Move存放到隊(duì)列中
void ClientCorrection(UINT ServerTime, State ServerState, Input ServerInput)
{while (time>moves[head].time head!=tail)
erase (head); // 移出過時(shí)的Move操作
//隊(duì)列不為空,且與服務(wù)器上操作的時(shí)間相同
if (head!=tail time==Moves[head].time)
{if ((moves[head].state.position-CurrentState.position).length>threshold)
{ CurrentTime=ServerTime;//保存服務(wù)器的時(shí)間
CurrentState=ServerState;//保存服務(wù)器的物理狀態(tài)
CurrentInput=ServerInput;//保存服務(wù)器的輸入
erase(head);// 移出隊(duì)列首Move
int index=head;
while (index!=tail)// 重新模擬t時(shí)刻之后的所有過程
{const float deltaTime=moves[index].time-currentTime;
updatePhysics(CurrentTime, deltaTime, CurrentInput);
CurrentTime=moves[index].time;
CurrentInput=moves[index].input;
moves[index].state=CurrentState;
index++;
}
}
}
}
2實(shí)驗(yàn)及結(jié)果
按照上述算法,以Visual C++.NET為開發(fā)工具,圖形部分采用OpenGL,網(wǎng)絡(luò)部分采用TNL(www.opentl.org)網(wǎng)絡(luò)引擎,實(shí)現(xiàn)了第一人稱射擊游戲的演示版。在多臺(tái)計(jì)算機(jī)上分別運(yùn)行服務(wù)器和多個(gè)客戶端,運(yùn)行效果較好,基本上能滿足第一人稱射擊游戲的網(wǎng)絡(luò)同步。
3結(jié)束語
同步問題在網(wǎng)絡(luò)游戲中是十分重要的問題,急需解決。目前的措施都是采用一些同步算法來減少網(wǎng)絡(luò)不同步帶來的影響。這些同步算法都是從分布式軍事仿真系統(tǒng)中借鑒過來的。在C/S架構(gòu)下,網(wǎng)絡(luò)游戲中大多采用分布式對(duì)象進(jìn)行通信,采用基于時(shí)間的移動(dòng),移動(dòng)過程中采用客戶端預(yù)測和客戶端修正的方法保持客戶端與服務(wù)器、客戶端與客戶端之間的同步。本文中提出的移動(dòng)同步算法在實(shí)時(shí)性要求較高的網(wǎng)絡(luò)游戲中有較好的效果。
參考文獻(xiàn):
[1]CRONIN E, KURC A R, FILSTRUP B, et al.An efficient synchronization mechanism for mirrored game architectures[J].Multimedia Tools and Applications,2004,23(1):7-30.
[2]BLOW J. A look at latency in networked games[J].Game Development,1998,5(7):28-40.
[3]ISHIBASHI Y, TASAKA S.Causality and media synchronization control for networked multimedia games: centralized versus distributed:proceedings of the 2nd Workshop on Network and System Support for Games[C].Redwood Ciyt:[s.n.],2003:42-51.
[4]BARRON T,LOST L. Multiplayer game programming[M]. Washington,D.C.:Course Technology PTR, 2002:406-408.
[5]BERNIER Y W.Latency compensating methods in Client/Server in-game protocol design and optimization [EB/OL].(2001-01).http://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf.
[6]SMED J, KAUKORANTA T,HAKONENH.A review on networking and multiplayer computer games[EB/OL].(2002-04).http://www.tucs.fi/publications/attachment.php%3ffname=TR454.pdf.
注:“本文中所涉及到的圖表、注解、公式等內(nèi)容請(qǐng)以PDF格式閱讀原文”