最近幾天正在找工做,因此更新BLOG晚了一些。今天在老婆的催促下終於要更新了。
經過以上兩篇關於對中心服務器和登陸服務器設計的闡述,你們應該對設計它們有了必定的瞭解。可是中心服務器和登陸服務器畢竟是遊戲外圍的部分,也就是說設計好了它們也仍是沒法實現編寫一款遊戲的目的啊。今天咱們就來探討一下如何設計遊戲服務器。
經過對QQ遊戲、遠航、聯衆等遊戲的分析。咱們能夠發現一些規律:
1:每一個已經登陸到大廳的玩家須要在樹形列表中選擇須要進入的遊戲房間。
2:在遊戲房間中咱們能夠進行坐下、更換座位、離開房間等操做。
3:幾乎每一款遊戲進入後,都須要點擊「開始」或者「準備」按鈕。點擊後玩家在房間遊戲桌上的狀態變爲一個舉手的標誌,這代表玩家已經準備好隨時進行遊戲了。
4:當一個桌子的全部玩家都知足「遊戲開始」狀態之後,在遊戲房間中會顯示此遊戲桌爲遊戲狀態。
以上4點是幾乎每一款遊戲都具備的過程。經過分析咱們能夠發現若是以玩家角度來看,一個玩家大體具備一下這麼6種狀態:
一、 空閒:玩家已經進入房間,可是並無作其它的操做。
二、 坐下:玩家點擊了椅子,本身的頭像已經在椅子上顯示,而且遊戲界面已經打開。
三、 舉手:玩家已經點擊了遊戲界面上的開始(有的遊戲叫舉手)按鈕。遊戲界面上已經顯示本身處於「準備」(QQ遊戲顯示準備)或者「等待開始」信息。
四、 遊戲:玩家處在遊戲過程之中。這種狀態也包含相似於連連看、對對碰遊戲中用於本身已經失敗,可是還有其它玩家在遊戲的狀況。
五、 旁觀:玩家點擊一個已經開始的遊戲桌中的一個玩家頭像,能夠看見此玩家正在遊戲的即時信息。
六、 斷線:玩家的客戶端和服務端已經斷開鏈接時的狀態。
而對玩家這6種狀態的維護是×××類遊戲的一個很關鍵的部分。你們能夠看到對於一個玩家來講,從進入一款遊戲到退出遊戲,他的狀態就在這6種狀態中來回變換。
下圖爲玩家狀態轉換圖:
經過上面的分析,咱們在定義玩家信息結構的時候就比較方便了。如下是我定義的玩家信息結構。
RUserSocket = record
Socket:TSocket; //套接字
UserID:Pchar; //玩家編號
UserName:Pchar; //玩家名稱
UserKey:Pchar; //玩家解密和加密時使用的密鑰
Room:Integer; //玩家所在的房間
Dask:Integer; //玩家所在的座位
PawID: Integer; //座位號
Sex: Boolean; //玩家性別
Email:pchar; //電子郵件
GameID: pchar; //遊戲編號
ByName:Pchar; //玩家暱稱
CurrState: Integer; //玩家狀態
Face: Integer; //玩家頭像
Grade: Integer; //遊戲等級
Score: Integer; //積分
TotalScore: Integer; //總成績
winnum: Integer; //贏盤數
losenum: Integer; //輸盤數
LookOnList:TList; //旁觀玩家列表
LogonTimer:TDateTime; //玩家登陸時間
end;
PUserSocket = ^RUserSocket;
對於一個遊戲服務器上的用戶的管理,咱們能夠放在一個全局鏈表中,對這個鏈表的維護咱們能夠放在一個類中(例如叫:TUserControl)。
若是咱們以桌子爲對象來看,遊戲桌的狀態應該分爲:
一、 空閒狀態:桌子沒有開始遊戲時候的狀態。
二、 遊戲狀態:桌子正在遊戲的時候的狀態。
這樣咱們就能夠設計出桌子的結構信息:
RDeskStatus = record
GameID:string[2]; //遊戲編號
RoomID:Integer; //房間編號
DeskID:Integer; //桌子編號
UserNums:Integer; //在這個桌子上的玩家個數(不含旁觀用戶)
Status:Integer; //桌子狀態 0:沒有開始遊戲 1:已經開始遊戲
end;
PDeskStatus = ^RDeskStatus;
對於桌子的信息咱們也放在一個鏈表中,並使用一個類來進行管理。(例如叫:TDeskControl)
接下來的問題就是,如何將玩家的信息和桌子的信息關聯起來呢?
咱們知道,一個玩家進入房間後,這個房間的其它玩家的坐下、舉手、遊戲開始等等的狀態他都應該能夠接收到。因此每個房間的玩家信息都應該由一個鏈表來維護。同時這個鏈表應該還維護這個房間桌子的狀態信息。
如下是我設計的房間信息的結構:
RUserRoom = record
Room:Integer; //房間編號
DeskStatusList:TList; //本房間桌子狀態信息鏈表 存放PdeskStatus指針。
ListUser:TList; //玩家列表 存放PuserSocket指針。
end;
PUserRoom = ^ RUserRoom;
對於這個結構的維護咱們也可使用一個類來作(例如:TRoomControl)。
以上的3個類是遊戲服務器主要編寫的3個類。如何實現咱們將在「實現篇」中來講明。
咱們知道咱們設計出來的遊戲服務器應該具備良好的可擴展性,以便於咱們之後添加一些未知的遊戲和遊戲類型。那如何作到遊戲服務器的可擴展性呢?經過分析咱們發現,每一套遊戲差異主要在於遊戲的自己。例如象棋遊戲和挖坑遊戲,它們的區別在於遊戲的規則(一個是棋類遊戲,一個是牌類遊戲),而不在於玩家的狀態(這兩款遊戲玩家都有坐下、舉手、遊戲等等功能)。因此咱們要作到遊戲服務器的可擴展性,應該將遊戲的邏輯部分和玩家的狀態區分開來。將玩家狀態部分讓遊戲服務器來管理,將遊戲邏輯部分使用腳本或者DLL的方式來動態加載。這樣咱們就能夠實現遊戲服務器的可擴展性。
下一篇咱們就來探討如何讓遊戲服務器動態的加載一個未知的遊戲邏輯信息。