口水遊戲主迴圈和元件
遊戲主迴圈和是否是單機遊戲並沒有多少關係。因為單機遊戲只不過是同時擁有伺服器和客戶端邏輯。那麼客戶端主迴圈和服務端主迴圈的差別也僅僅是裝置的差別,客戶端需要支援輸入裝置的讀取,螢幕的渲染(Render);服務端需要支援寫日誌檔案,處理來自客戶端的遠端訪問。
對於一個服務端來說,他多的或者說擅長的就是輪詢,每秒百億次的計算可以把所有事情都照顧的妥妥的。那麼,在一個無限while迴圈的幫助下,遊戲伺服器就出現了。
Game_Init(); while(true) { Parse_Input(); Module.Update(); Sleep(50); //毫秒 } Game_Save();
一個遊戲,需要不斷的讀取外部的輸入(Input),並用於修正自身的狀態(也就是這裡的Module.Update)。比較有意思的是,遊戲的狀態(可以理解為是記憶體的值,或是變數的值)往往受到兩種外力被修改,其一是玩家的操作影響(Parse_Input),其二是系統的反饋或是主動的迴應(Module.Update),主動迴應包括系統邏輯的更新(每5秒給所有玩家5塊錢),ai單位的邏輯。剛好這兩部分都包含在這個主迴圈的功能之中了。
模組和子模組(IGameModule)
一個遊戲的處理機制,我習慣於用很多一層一層疊加的模組來表示,每個模組各司其職,一個遊戲系統也就呈現出來了。
一個單機遊戲和網路遊戲最大的區別就是SysPlayer下到底有多少個使用者級的例項。系統級的元件(Module)儲存著整個遊戲世界的狀態,而使用者級的元件只會儲存自己使用者的資料(可以理解為存檔)。如果是一個單機向的網遊(比如卡牌遊戲),大部分的伺服器邏輯可能都在實現使用者級的元件,僅有小部分類似公會管理器,pk管理元件在處理一些玩家之間的互動元件。
元件的粒度
比如揹包元件,其中有一些public函式是介面,需要提供給很多元件使用(加一個道具AddItem,加錢AddGold);另一些則是元件的邏輯,只有一個地方需要調到用(整理揹包Sort)。如果這兩塊的函式都雜糅在一起,時間久了往往就無法分辨彼此了。這時候,我們就應該考慮拆分子元件,把系統拆的更細一些。在這裡其實是為了區分API方法和邏輯方法,用一個代理物件來專門負責邏輯方法的呼叫應該就能很好解決。
物件體系
這樣下來整個系統物件的關係,基本都是has-a了。在主架構上沒有太多的繼承關係更容易被理解。這種設計模式下,模組間的關係會變得非常緊耦合,每個模組的API方法被錯綜複雜的呼叫,形成網路。我們可以通過類似pub-sub的設計模式來解耦,通過引入訊息處理模組,把各個模組直接呼叫關係轉化為孤立的呼叫者傳送訊息,被呼叫者處理訊息的模式。