基于Unity的弹幕游戏多人联机尝试
給一個已經定型的游戲添加哪怕是一個小特性,都是棘手的事情,很容易引入新的bug,或者破壞已有的功能。復雜的聯網更是如此,它涉及到的改動幾乎遍及系統的方方面面。玩家的一舉一動都需要在其他人的屏幕上展現出來,所以我們需要選擇一個低成本的,容易實現的方式,來開發多人在線的功能。
技術選型
Unity引擎內置了多人聯機的解決方案,涵蓋了從最底層的網絡數據傳輸,到不同玩家之間的消息發送,再到游戲大廳這樣的高級功能。考慮到Unity官方提供的云服務(Internet Services)在國內的延遲較高,而且需要付費,我們決定采用Steam與Unity相結合的方式。底層用Steam發送網絡數據包,中間由Unity負責把各個包整合成游戲邏輯所需要的格式,高層的大廳也使用Steam提供的服務。
說到這里要贊美一下Unity Networking的模塊設計,它把具體的網絡數據傳輸細節和抽象的消息發送功能分離開來。使得開發者既可以使用傳統的“IP地址+端口”的方式實現玩家之間的連接,也可以相對方便地接入Steam或WeGame,利用這些平臺SDK包含的更高級的功能去收發網絡數據。而且Unity的網絡模塊是開源的,不僅方便查閱,還可以根據自身需求進行修改,然后替換掉引擎自帶的模塊。
服務器
聯網游戲需要一個服務器,用于協調多個玩家之間的游戲進程。否則大家的電腦有快有慢,很容易出現游戲節奏不一致的情況。比如,玩家A的電腦配置高,運行流暢;玩家B的電腦有點卡,會掉幀。那么,當游戲需要3個小飛碟從上方飛入屏幕攻擊玩家的時候,可能玩家A那邊的飛碟已經全部就位,開始發射子彈了;但玩家B那邊剛剛創建出第二個飛碟。這樣就導致不同玩家屏幕上顯示的內容完全不同,很難進行正常的游戲。引入服務器就是要避免出現各種各樣的不一致行為,讓速度快的機器等等速度慢的,大家盡可能保持相同的步調去執行游戲邏輯。
這個服務器可以是獨立的后臺,就像一個網站那樣托管在某個云計算廠商那里;也可以讓某個玩家來充當服務器,在運行自己游戲邏輯的同時也負責調度其他玩家的游戲。不過,開發并維護一個獨立服務器的成本相對而言還是挺高的,所以我們選擇了第二種方案,就讓創建房間的玩家來兼職做服務器。Unity恰好有一個Host模式,支持一個玩家同時扮演服務器和客戶端。
游戲房間
完成了前期的技術準備后,我們就開始著手制作游戲房間。目前我們僅支持雙人協作,這部分的流程也比較直觀:
?
- 創建房間:玩家A向Steam發起申請,并設置最大人數為2。如果成功,A就成為房間的所有者,進入角色選擇界面。同時,A還需要啟動服務器(NetworkServer),等待其他玩家的進入。
- 查詢房間:玩家B設置篩選條件去查詢當前列表,Steam會返回還有空余位置的房間。如果A創建的房間符合條件,該房間就會包含在返回結果之中。
- 進入房間:B申請進入A創建的房間。如果成功,A和B之間就可以通過Steam互相發送消息。但這時房間內的玩家只能進行基本的通信,還不能利用Unity提供的狀態同步等機制。
- 建立C/S關系:B向A發送連接請求(NetworkClient),A收到后建立連接。這樣,后續的游戲同步邏輯就可以按照Unity的方式進行。
- 開始游戲:B進入房間,選擇自己的角色。二手手機號碼交易完畢后,A通知雙方加載游戲場景。
角色同步
?
游戲角色作為玩家的控制對象,接收絕大部分用戶輸入,是同步的重點。
?
- 移動:Unity提供了同步物體位置、旋轉的組件(NetworkTransform),可以設置頻率調整每秒同步的次數。但每次同步之間沒有插值,導致物體的移動顯得特別僵硬。所以我們選擇自行發送位置數據,遠程角色在收到數據后進行平滑處理。也就是說不是立即將角色設置到新的位置,而是以一定速率,每幀逐漸靠近新的位置。這種方式雖然會導致角色的位置跟實際位置有所偏差,但會有一個能夠接受的視覺效果。
- 狀態:角色的血量、炸彈數量還有當前動畫等通過Unity提供的狀態同步方式來處理。引擎自行監控這些特別標注的變量(SyncVar),在初始和變化的時候,把新數值同步到客戶端上去。
- 技能:釋放技能和炸彈由玩家自己控制的角色發起,在服務器端執行,然后同步到其他玩家的客戶端。這種類型的操作適合使用Command+ClientRpc方式去實現。
- 子彈:自機的子彈也是通過監測當前是否正處于發射的狀態去同步。角色或敵機中彈則作為事件發送到服務器去處理。
場景同步
游戲場景占據了最多的工作量,主要是因為之前很多工作都是利用PlayMaker插件來做的。這個可視化的狀態機工具能方便非技術人員去調整物體在游戲內的動態行為。截止到目前的1.8.9版本,它還不支持Unity的多人模塊,同步起來比較麻煩。
?
以下是場景中需要注意的事項:
?
- 時間:許多場景的移動、關鍵動作的觸發都跟時間相關,所以當前游戲進行的時間是場景保持同步的關鍵。
- 雜兵行為:我們游戲中有一百多個雜兵,基于這些雜兵有八百多個不同的行為。這些行為都是用PlayMaker編輯的狀態機。要讓如此眾多的狀態機去支持聯網,手動去挨個修改是不可能的,只能利用腳本批量修改。
- 狀態機:我們設計關卡時,會根據游戲進行的時間或者地圖移動的位置去指定某個雜兵的行為。這些行為一般遵循先創建雜兵單位,再移動射擊,最后被擊落或離開屏幕的規律。這里邊包含兩部分,一是用于交互和同步的雜兵,二是控制雜兵行為的狀態機。在單機情況下,狀態機創建出單位緊接著執行后續操作;在聯網模式下為了狀態同步,場景中物體的創建和銷毀需要在服務器端進行。所以,原有的狀態機在服務器和客戶端上的執行不再一致。服務器創建的雜兵單位,會通過Unity的機制自動在客戶端上克隆出來,這樣客戶端不再需要自己創建,而是等待單位被服務器創建出來后作為參數傳入狀態機里去執行后續動作。
- Boss行為:一般的雜兵行為比較簡單,在屏幕中存在的時間也較短,在服務器和客戶端上各自運行也不會產生太大差別。但Boss的行為比較復雜,運行一段時間后就會出現明顯偏差。我們在狀態機內部的關鍵節點上加入等待機制,讓各玩家在運行到節點處同步進入下一狀態。
總結
至此游戲已經可以支持基本的多人體驗了。對于聯網功能的開發,我們總結了以下心得:
?
- 給基于Unity引擎的游戲添加聯網功能的難度并不大,但最好在游戲設計初期就確定是否單機還是在線游戲,能少走彎路,節省工作量。
- 定期清理工程文件,做好素材分類,盡可能多地復用資源,避免冗余。這樣能減少項目后期的維護成本。
- 在寫這篇文章時,碰巧看到關于幀同步的介紹。感覺這項技術對于彈幕這種精確度要求較高的游戲可能更加合適。將來有機會在新的項目中嘗試。
總結
以上是生活随笔為你收集整理的基于Unity的弹幕游戏多人联机尝试的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity性能优化 – 脚本篇
- 下一篇: 游戏服务器的架构演进