原文地址:https://blog.csdn.net/erlib/article/details/24301221
服務(wù)器的廣播
服務(wù)器的廣播的重點(diǎn)就在于如何計(jì)算出廣播的對(duì)象。很顯然匀们,在一張很大的地圖里面来惧,某個(gè)玩家在最東邊的一個(gè)動(dòng)作,一個(gè)在最西邊的玩家是應(yīng)該看不到的摹迷,那么怎么來(lái)計(jì)算廣播的對(duì)象呢?最簡(jiǎn)單的辦法郊供,就是把地圖分塊峡碉,分成大小合適的小塊,然后每次只象周?chē)鷰讉€(gè)小塊的玩家進(jìn)行廣播驮审。那么究竟切到多大比較合適呢鲫寄?一般來(lái)說(shuō)吉执,切得塊大了,內(nèi)存的消耗會(huì)增大地来,切得塊小了戳玫,CPU的消耗會(huì)增大(原因會(huì)在后面提到)。個(gè)人覺(jué)得切成一屏左右的小塊比較合適未斑,每次廣播廣播周?chē)艂€(gè)小塊的玩家咕宿,由于廣播的操作非常頻繁,那么遍利周?chē)艍K的操作就會(huì)變得相當(dāng)?shù)念l繁颂碧,所以如果塊分得小了荠列,那么遍利的范圍就會(huì)擴(kuò)大,CPU的資源會(huì)很快的被吃完载城。
切好塊以后肌似,怎么讓玩家在各個(gè)塊之間走來(lái)走去呢?讓我們來(lái)想想在切換一次塊的時(shí)候要做哪些工作诉瓦。首先川队,要算出下個(gè)塊的周?chē)艍K的玩家有哪些是現(xiàn)在當(dāng)前塊沒(méi)有的,把自己的信息廣播給那些玩家睬澡,同時(shí)也要算出下個(gè)塊周?chē)艍K里面有哪些物件是現(xiàn)在沒(méi)有的固额,把那些物件的信息廣播給自己,然后把下個(gè)塊的周?chē)趴炖餂](méi)有的煞聪,而現(xiàn)在的塊周?chē)艍K里面有的物件的消失信息廣播給自己斗躏,同時(shí)也把自己消失的消息廣播給那些物件。這個(gè)操作不僅煩瑣而且會(huì)吃掉不少CPU資源昔脯,那么有什么辦法可以很快的算出這些物件呢啄糙?一個(gè)個(gè)做比較?顯然看起來(lái)就不是個(gè)好辦法云稚,這里可以參照二維矩陣碰撞檢測(cè)的一些思路隧饼,以自己周?chē)艍K為一個(gè)矩陣,目標(biāo)塊周?chē)艍K為另一個(gè)矩陣静陈,檢測(cè)這兩個(gè)矩陣是否碰撞燕雁,如果兩個(gè)矩陣相交,那么沒(méi)相交的那些塊怎么算鲸拥。這里可以把相交的塊的坐標(biāo)轉(zhuǎn)換成內(nèi)部坐標(biāo)拐格,然后再進(jìn)行運(yùn)算。
對(duì)于廣播還有另外一種解決方法刑赶,實(shí)施起來(lái)不如切塊來(lái)的簡(jiǎn)單禁荒,這種方法需要客戶(hù)端來(lái)協(xié)助進(jìn)行運(yùn)算。首先在服務(wù)器端的連接結(jié)構(gòu)里面需要增加一個(gè)廣播對(duì)象的隊(duì)列角撞,該隊(duì)列在客戶(hù)端登陸服務(wù)器的時(shí)候由服務(wù)器傳給客戶(hù)端呛伴,然后客戶(hù)端自己來(lái)維護(hù)這個(gè)隊(duì)列,當(dāng)有人走出客戶(hù)端視野的時(shí)候谒所,由客戶(hù)端主動(dòng)要求服務(wù)器給那個(gè)物件發(fā)送消失的消息热康。而對(duì)于有人總進(jìn)視野的情況,則比較麻煩了劣领。
首先需要客戶(hù)端在每次給服務(wù)器發(fā)送update position的消息的時(shí)候姐军,服務(wù)器都給該連接算出一個(gè)視野范圍,然后在需要廣播的時(shí)候尖淘,循環(huán)整張地圖上的玩家奕锌,找到坐標(biāo)在其視野范圍內(nèi)的玩家。使用這種方法的好處在于不存在轉(zhuǎn)換塊的時(shí)候需要一次性廣播大量的消息村生,缺點(diǎn)就是在計(jì)算廣播對(duì)象的時(shí)候需要遍歷整個(gè)地圖上的玩家惊暴,如果當(dāng)一個(gè)地圖上的玩家多得比較離譜的時(shí)候,該操作就會(huì)比較的慢趁桃。
服務(wù)器的同步
同步在網(wǎng)絡(luò)游戲中是非常重要的辽话,它保證了每個(gè)玩家在屏幕上看到的東西大體是一樣的。其實(shí)呢卫病,解決同步問(wèn)題的最簡(jiǎn)單的方法就是把每個(gè)玩家的動(dòng)作都向其他玩家廣播一遍油啤,這里其實(shí)就存在兩個(gè)問(wèn)題:1,向哪些玩家廣播蟀苛,廣播哪些消息益咬。2,如果網(wǎng)絡(luò)延遲怎么辦帜平。事實(shí)上呢幽告,第一個(gè)問(wèn)題是個(gè)非常簡(jiǎn)單的問(wèn)題,不過(guò)之所以我提出這個(gè)問(wèn)題來(lái)罕模,是提醒大家在設(shè)計(jì)自己的消息結(jié)構(gòu)的時(shí)候评腺,需要把這個(gè)因素考慮進(jìn)去。而對(duì)于第二個(gè)問(wèn)題淑掌,則是一個(gè)挺麻煩的問(wèn)題蒿讥,大家可以來(lái)看這么個(gè)例子:
比如有一個(gè)玩家A向服務(wù)器發(fā)了條指令,說(shuō)我現(xiàn)在在P1點(diǎn)抛腕,要去P2點(diǎn)芋绸。指令發(fā)出的時(shí)間是T0,服務(wù)器收到指令的時(shí)間是T1担敌,然后向周?chē)耐婕覐V播這條消息摔敛,消息的內(nèi)容是“玩家A從P1到P2”有一個(gè)在A附近的玩家B,收到服務(wù)器的這則廣播的消息的時(shí)間是T2全封,然后開(kāi)始在客戶(hù)端上畫(huà)圖马昙,A從P1到P2點(diǎn)桃犬。這個(gè)時(shí)候就存在一個(gè)不同步的問(wèn)題,玩家A和玩家B的屏幕上顯示的畫(huà)面相差了T2-T1的時(shí)間行楞。這個(gè)時(shí)候怎么辦呢攒暇?
有個(gè)解決方案,我給它取名叫 預(yù)測(cè)拉扯子房,雖然有些怪異了點(diǎn)形用,不過(guò)基本上大家也能從字面上來(lái)理解它的意思。要解決這個(gè)問(wèn)題证杭,首先要定義一個(gè)值叫:預(yù)測(cè)誤差田度。然后需要在服務(wù)器端每個(gè)玩家連接的類(lèi)里面加一項(xiàng)屬性,叫l(wèi)atency解愤,然后在玩家登陸的時(shí)候镇饺,對(duì)客戶(hù)端的時(shí)間和服務(wù)器的時(shí)間進(jìn)行比較,得出來(lái)的差值保存在latency里面琢歇。還是上面的那個(gè)例子兰怠,服務(wù)器廣播消息的時(shí)候,就根據(jù)要廣播對(duì)象的latency李茫,計(jì)算出一個(gè)客戶(hù)端的CurrentTime揭保,然后在消息頭里面包含這個(gè)CurrentTime,然后再進(jìn)行廣播魄宏。并且同時(shí)在玩家A的客戶(hù)端本地建立一個(gè)隊(duì)列秸侣,保存該條消息,只到獲得服務(wù)器驗(yàn)證就從未被驗(yàn)證的消息隊(duì)列里面將該消息刪除宠互,如果驗(yàn)證失敗味榛,則會(huì)被拉扯回P1點(diǎn)。然后當(dāng)玩家B收到了服務(wù)器發(fā)過(guò)來(lái)的消息“玩家A從P1到P2”這個(gè)時(shí)候就檢查消息里面服務(wù)器發(fā)出的時(shí)間和本地時(shí)間做比較予跌,如果大于定義的預(yù)測(cè)誤差搏色,就算出在T2這個(gè)時(shí)間,玩家A的屏幕上走到的地點(diǎn)P3券册,然后把玩家B屏幕上的玩家A直接拉扯到P3频轿,再繼續(xù)走下去,這樣就能保證同步烁焙。更進(jìn)一步航邢,為了保證客戶(hù)端運(yùn)行起來(lái)更加smooth,我并不推薦直接把玩家拉扯過(guò)去骄蝇,而是算出P3偏后的一點(diǎn)P4膳殷,然后用(P4-P1)/T(P4-P3)來(lái)算出一個(gè)很快的速度S,然后讓玩家A用速度S快速移動(dòng)到P4九火,這樣的處理方法是比較合理的,這種解決方案的原形在國(guó)際上被稱(chēng)為(Full plesiochronous),當(dāng)然讳窟,該原形被我篡改了很多來(lái)適應(yīng)網(wǎng)絡(luò)游戲的同步,所以而變成所謂的:預(yù)測(cè)拉扯跨细。
另外一個(gè)解決方案,我給它取名叫 驗(yàn)證同步河质,聽(tīng)名字也知道,大體的意思就是每條指令在經(jīng)過(guò)服務(wù)器驗(yàn)證通過(guò)了以后再執(zhí)行動(dòng)作震叙。具體的思路如下:首先也需要在每個(gè)玩家連接類(lèi)型里面定義一個(gè)latency掀鹅,然后在客戶(hù)端響應(yīng)玩家鼠標(biāo)行走的同時(shí),客戶(hù)端并不會(huì)先行走動(dòng)媒楼,而是發(fā)一條走路的指令給服務(wù)器乐尊,然后等待服務(wù)器的驗(yàn)證。服務(wù)器接受到這條消息以后划址,進(jìn)行邏輯層的驗(yàn)證扔嵌,然后計(jì)算出需要廣播的范圍,包括玩家A在內(nèi)夺颤,根據(jù)各個(gè)客戶(hù)端不同的latency生成不同的消息頭痢缎,開(kāi)始廣播,這個(gè)時(shí)候這個(gè)玩家的走路信息就是完全同步的了世澜。這個(gè)方法的優(yōu)點(diǎn)是能保證各個(gè)客戶(hù)端之間絕對(duì)的同步独旷,缺點(diǎn)是當(dāng)網(wǎng)絡(luò)延遲比較大的時(shí)候,玩家的客戶(hù)端的行為會(huì)變得比較不流暢寥裂,給玩家?guī)?lái)很不爽的感覺(jué)嵌洼。該種解決方案的原形在國(guó)際上被稱(chēng)為(Hierarchical master-slave synchronization),80年代以后被廣泛應(yīng)用于網(wǎng)絡(luò)的各個(gè)領(lǐng)域封恰。
最后一種解決方案是一種理想化的解決方案麻养,在國(guó)際上被稱(chēng)為Mutual synchronization,是一種對(duì)未來(lái)網(wǎng)絡(luò)的前景的良好預(yù)測(cè)出來(lái)的解決方案诺舔。這里之所以要提這個(gè)方案鳖昌,并不是說(shuō)我們已經(jīng)完全的實(shí)現(xiàn)了這種方案,而只是在網(wǎng)絡(luò)游戲領(lǐng)域的某些方面應(yīng)用到這種方案的某些思想混萝。我對(duì)該種方案取名為:半服務(wù)器同步遗遵。大體的設(shè)計(jì)思路如下:
首先客戶(hù)端需要在登陸世界的時(shí)候建立很多張廣播列表,這些列表在客戶(hù)端后臺(tái)和服務(wù)器要進(jìn)行不及時(shí)同步逸嘀,之所以要建立多張列表车要,是因?yàn)橐獜V播的類(lèi)型是不止一種的,比如說(shuō)有l(wèi)ocal message,有remote message,還有g(shù)lobal message 等等崭倘,這些列表都需要在客戶(hù)端登陸的時(shí)候根據(jù)服務(wù)器發(fā)過(guò)來(lái)的消息建立好翼岁。在建立列表的同時(shí)类垫,還需要獲得每個(gè)列表中廣播對(duì)象的latency,并且要維護(hù)一張完整的用戶(hù)狀態(tài)列表在后臺(tái)琅坡,也是不及時(shí)的和服務(wù)器進(jìn)行同步悉患,根據(jù)本地的用戶(hù)狀態(tài)表,可以做到一部分決策由客戶(hù)端自己來(lái)決定榆俺,當(dāng)客戶(hù)端發(fā)送這部分決策的時(shí)候售躁,則直接將最終決策發(fā)送到各個(gè)廣播列表里面的客戶(hù)端,并對(duì)其時(shí)間進(jìn)行校對(duì)茴晋,保證每個(gè)客戶(hù)端在收到的消息的時(shí)間是和根據(jù)本地時(shí)間進(jìn)行校對(duì)過(guò)的陪捷。那么再采用預(yù)測(cè)拉扯中提到過(guò)的計(jì)算提前量,提高速度行走過(guò)去的方法诺擅,將會(huì)使同步變得非常的smooth市袖。該方案的優(yōu)點(diǎn)是不通過(guò)服務(wù)器,客戶(hù)端自己之間進(jìn)行同步烁涌,大大的降低了由于網(wǎng)絡(luò)延遲而帶來(lái)的誤差苍碟,并且由于大部分決策都可以由客戶(hù)端來(lái)做,也大大的降低了服務(wù)器的資源撮执。由此帶來(lái)的弊端就是由于消息和決策權(quán)都放在客戶(hù)端本地微峰,所以給外掛提供了很大的可乘之機(jī)。
下面我想來(lái)談?wù)勱P(guān)于服務(wù)器上NPC的設(shè)計(jì)以及NPC智能等一些方面涉及到的問(wèn)題二打。首先县忌,我們需要知道什么是NPC,NPC需要做什么继效。NPC的全稱(chēng)是(Non-Player Character)症杏,很顯然,他是一個(gè)character瑞信,但不是玩家厉颤,那么從這點(diǎn)上可以知道,NPC的某些行為是和玩家類(lèi)似的凡简,他可以行走逼友,可以戰(zhàn)斗,可以呼吸(這點(diǎn)將在后面的NPC智能里面提到)秤涩,另外一點(diǎn)和玩家物件不同的是帜乞,NPC可以復(fù)生(即NPC被打死以后在一定時(shí)間內(nèi)可以重新出來(lái))。其實(shí)還有最重要的一點(diǎn)筐眷,就是玩家物件的所有決策都是玩家做出來(lái)的黎烈,而NPC的決策則是由計(jì)算機(jī)做出來(lái)的,所以在對(duì)NPC做何種決策的時(shí)候,需要所謂的NPC智能來(lái)進(jìn)行決策照棋。
下面我將分兩個(gè)部分來(lái)談?wù)凬PC资溃,首先是NPC智能,其次是服務(wù)器如何對(duì)NPC進(jìn)行組織烈炭。之所以要先談NPC智能是因?yàn)橹挥挟?dāng)我們了解清楚我們需要NPC做什么之后溶锭,才好開(kāi)始設(shè)計(jì)服務(wù)器來(lái)對(duì)NPC進(jìn)行組織。