如何从零开始开发一个实时联机游戏?
本文作者為明星團隊漢家松鼠游戲工作室的CEO成功(CG),他將于11月10日在深圳舉辦的第四期騰訊游戲學院品鑒會上,分享漢家松鼠旗下《漢家江湖》、《江湖X》等游戲從立項、研發到上線的實戰經驗,并談談獨立小團隊該如何長線經營。
這是一篇嚴肅的聯機游戲開發入門介紹,本文所述代碼開源,文末可獲得地址。
關于游戲的實時聯機對戰,目前是很多游戲開發者研究課題,也延伸出了很多概念,如“狀態同步”、“幀同步”,目前很多游戲開發框架也提供了這樣的開發一些理念和組件,比如unity的The High Level API等等。
本文不希望傳授如何用框架、服務端引擎等來搭建一套商業級的框架。而是希望從最基本的網絡通信原理開始,一點點的進行樸素的分解和搭建,旨在從原理上概述聯機游戲的設計思路以及抽象于計算機網絡的通信框架如何設計和構建。
我個人編寫這套DEMO包括完整的客戶端和服務器,也是用于調研工作室游戲《漢家江湖》后續的實時聯機部分如何搭建和開發,并且后續作為我們自己的一個實時聯機通信框架的測試程序。
?
?
實際完成的DEMO如上,每個玩家是一個點,只有一個操作:使用左下的虛擬搖桿進行方向控制。在移動過程中不斷噴射子彈,命中對方即可扣血,在規定時間內比誰的擊殺的人數多。由于是一個簡單的DEMO,我們不在美術上做太多東西(請原諒我的五毛錢PS技術),主要是為了講解程序如何來設計。
工具部分,客戶端我們使用unity開發,服務端我們直接從0開始寫一個基于socket的服務器。
我們使用狀態同步來做這個DEMO(理論上幀同步機制是更加適合這種IO類游戲的,我們為了邏輯清晰簡單,先用狀態同步來做,后續有時間我再補幀同步的方案。)
狀態同步
什么叫狀態同步?
簡單的來理解就是所有的數據在服務端進行計算和校驗,客戶端將操作上發到服務器,服務器不斷的告訴客戶端計算結果,由客戶端進行展現。
?
客戶端邏輯結構
客戶端的整體框架邏輯非常簡單,unity是典型的單線程編程,我們只需要在FixedUpdate里出來所有的消息隊列即可。
?
其實按照socket的非阻塞模型來說,我們客戶端也可以不使用接收線程而使用純粹的單線程。我們在這里不再贅述,再次重申,我們只是很簡單粗暴的一切為了編程簡單易于理解。
客戶端維護一個消息隊列的原因是,我們每次收到的數據需要順序的執行。在unity中我們所有改變UI或者界面相關的邏輯需要寫在主線程中。所以我們使用一個消息隊列來進行傳遞,每次FixedUpdate時依次處理所有的隊列中的消息。然后處理客戶端應該發送的指令。需要注意的是,由于消息隊列被兩個線程同時訪問,作為臨界區數據,需要加線程鎖。
服務端邏輯結構
我們這里通信協議使用tcp(大家實現也可以使用udp,都類似),那么服務端是一個典型的處理連接、處理請求并分發數據的邏輯結構。我們為了編程簡單,也先不顧效率的每一個客戶端連接我們起一個專門的接收線程,然后統一分發處理,邏輯結構如下:
?
使用單線程來處理整個游戲的主邏輯,是一個典型樸素且最簡單的編程思想。當然,這里可能會存在瓶頸問題,所以具體中間有很多可以優化的點,比如一些物理計算實際可以拆分成多線程或者跟進一步使用GPU、比如接收客戶端數據可以使用非阻塞模型或者線程池……這里不再多做說明。
其實我們抽象一下,以上整個框架實際上適用于任何類型的聯機游戲。對于網絡連接層,我們需要很清楚幾個概念:
每一個客戶端連接,在服務端維護一個session,這個session主要處理與客戶端的通信(收發數據)以及該客戶端的一些臨時狀態(我們這個游戲沒有)。
通信協議
服務端維護了一份完整的數據結構,在本游戲就是當前游戲中剩余時間、一共有多少個玩家、玩家的HP、玩家們所在位置和移動方向及速度、一共有多少顆子彈、子彈的移動方向和速度……
我們稱整個以上數據為一份全量狀態,它描述了整個游戲二手QQ當前的情況。
最樸素的思想就是服務端不斷的將整個全量狀態分發到每個客戶端,這樣客戶端就只用管顯示就行了。但實際的開發過程中會發現這樣飛快就會達到性能瓶頸(因為發送的數據量太多,網絡IO吃不住。而且也可能因為不斷的要生成全量數據快照,CPU的計算量也非常大。)所以需要優化,接下來我們具體探討一下通信協議如何來做。
通信協議是客戶端和服務端共同約定的一個數據結構,其包含了雙方可以發送并對方可以識別處理的數據包。
在設計網絡協議的過程中,我們需要有一個的分層概念,我們更多的只需要來關心業務邏輯,也就是具體發送什么樣的數據,底層的話這里我使用protobuf(性能最高的開源序列化、反序列化庫)。
我們使用protobuf將數據結構序列化為二進制數據,并且通過socket來進行發送,接收方收到后使用protobuf反序列化為業務協議,提供給上層邏輯代碼進行解析。所以實際上藍色部分都是使用開源庫來進行的,我們只用關注實際的游戲業務協議(綠色部分)。
我們拆解一下實際的業務協議,如下:
客戶端 -> 服務器:
1、加入游戲
2、角色行動+開火
服務器 -> 客戶端:
1、新玩家加入游戲
2、某個玩家被擊中/擊殺
3、玩家移動+開火
4、全量狀態同步(用于游戲進行到一半有玩家加入,發送給他當前對局的整體情況)
5、時間流逝
所以實際上我們游戲的編程,就是在客戶端和服務端互相生成并處理以上數據包的過程。對應前面的流程圖就是所有的消息隊列處理和生成。由于具體和游戲內容相關,各位有興趣可以看代碼,這里不再多做描述。
代碼結構
另外,我需要更進一步說一下關于代碼結構的一些思考。
由于服務端和客戶端實際上有大量的數據結構交換,我認為一個比較好的方式是一份代碼兩邊使用。所以我服務端也是使用C#編程開發,將一個脫離框架的dll(主要是業務通信協議和各種兩邊使用的數據結構、常量和計算工具)同時分發給兩邊使用。
具體可見服務端代碼中的ShootGameServer.SharedData,這份代碼同時會生成dll到客戶端Unity的/Assets/Plugins目錄下。
?
另外關于網絡層的適配,我整個抽象了出來,所以大家如果有興趣的話,可以使用UDP重寫或者自己來編寫底層的通信鏈路(下圖橙色部分)。其TCP實現位于ShootGameServer.SharedData.Network.Impl
?
其中服務器我寫了一個通信鏈路的集成單元測試,位于:
?
開源代碼中通信鏈路KCP部分代碼我尚未集成完畢,大家可以忽略。
未來的一點點計劃
目前我們工作室計劃針對性的開發一套業務無關的網絡鏈路層框架,主要實現的功能是“開房間”-“加入游戲”-“游戲”-“結束”的一套基于云服務分布式調度的管理框架。
通俗來說就是可以開發 實時聯機的IO類游戲、百人聯機的吃雞類游戲、MOBA類游戲這種高實時性互動性要求的游戲。
我們計劃將各個模塊性能消耗優化到極致,并且未來提供多種高度封裝易用的編程模型。
此模塊我們會先在自己的內部的游戲項目中實踐使用,未來考慮開源+分享出來。或者提供一套便捷易用的SDK,供外部使用。
總結
以上是生活随笔為你收集整理的如何从零开始开发一个实时联机游戏?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 创造开放世界——《看火人》游戏场景设计
- 下一篇: 揭秘重度MMORPG手游后台性能优化方案