干貨分享:《歡樂坦克大戰》微信小游戲開發總結
前言
《歡樂坦克大戰》是一款支持 3V3 實時對戰并首批參與上線的微信小游戲中的作品。因為該游戲為微信小游戲中的重度之作,項目開發周期非常短,所以游戲復雜度、開發難度、性能挑戰也是挺大的;項目組在一個月內就完成了單機、網絡對戰玩法的開發。
同時,因為客戶端開發團隊的核心成員有多年的 Cocos2d-x 引擎開發經驗,所以項目組決定使用 Cocos Creator v1.6.1 版本的引擎進行開發。而對于微信小游戲平臺采用的 javascript 語言,開發團隊基本上是從 0 開始做,邊學邊做,對自身挑戰很大。
架構
網絡通信方面項目采用了 WebSocket 協議進行通信,而通信格式是 json。為了迎合 tdr 的 xml 協議,項目組自己開發了 tdr->json 的轉換工具。
為了方便策劃同學使用 excel 表格進行數據配置,項目組又開發了將 excel 轉換成 json 文件的工具,以便供客戶端讀取配置文件。
地圖方面我們沒有使用 cocos 引擎自帶的 TileMap,而是自己實現了一個類 TileMap 機制。策劃同學可以在 excel 中配置地圖信息,使用工具將 excil 轉換成 json 格式的地圖文件供客戶端加載。
由于開發進度緊張,需要同時開發單機和 PVP 玩法。所以我們封裝了一個命令層(CMD 層)來進行戰斗邏輯驅動。比如使用搖桿控制坦克運動,是由表現層發送 CMD 命令給邏輯層進行處理,在單機模式下 CMD 會存儲于客戶端本地列表,然后由命令管理器 CMDMgr 在 Update 時讀取本地命令列表驅動邏輯層進行處理。而在對戰模式中,CMD 命令會被發往服務器,由服務器廣播給所有玩家,玩家客戶端的命令管理器 CMDMgr 在 Update 時驅動邏輯層進行處理。引入命令層(CMD 層)之后,戰斗邏輯層是抽象獨立的,開發不需要關心當前的玩法模式,可以方便的復用,減少了開發成本。
我們 PVP 實時對戰采用的是 c/s 模式的同步架構,客戶端做碰撞檢測,將碰撞檢測結果通知服務器,服務器進行校驗并做傷害計算,然后廣播給其他玩家。游戲支持斷線重連、客戶端 crash 重連機制,服務器擁有戰斗中的所有狀態數據,重連時將所有數據發送給客戶端,客戶端進行戰斗場景還原。
玩家位置同步采用了基于時間戳的位置點同步算法。這個算法原先應用于《全民飛機大戰》的雙打模式、對抗模式中。《全民飛機大戰》中實時對戰采用的是 UDP 通信。而在《歡樂坦克大戰的》WebSocketTCP 環境下也取得了不錯的效果。算法原理如下:
挑戰
在開發過程中,我們也遇到了不少挑戰,但是我們都一一解決了,具體遇到的問題如下:
1、微信小游戲平臺增加了動態執行代碼的限制
微信小游戲平臺增加了動態執行代碼的限制,比如:eval (‘console.log (1)’)、new Function (‘console.log (1)’)、setTimeout (‘console.log (1)’) 等調用方式無法調用。而在 Cocos Creator v1.6.1 源碼中大量使用了 Function,為了解決這個問題,我們和 cocos 引擎開發商溝通了下,又參考 cocos 在 1.7 版本(當時尚未發布)中的修改,修改了一些源碼,解決了此問題。
2、微信小游戲不允許超過 4M
正如標題所示,微信小程序嚴格要求了大小,為了解決這個問題,我們又想了不少辦法。
措施1:引擎定制裁剪去掉不必要的模塊減少引擎體積,這塊通過設置引擎模塊即可。
措施2:圖片壓縮
使用 png 圖片壓縮工具 pngquant,可以有效的減小 png 圖片的文件大小(通常能壓縮 60%-70%)左右。
通過以上 2 個措施,資源仍然會超標,只能采用資源動態下載的方案了。
措施3:資源動態下載
我們在游戲中增加了一個資源更新場景。游戲啟動時,場景進行資源更新時游戲業務模塊都沒有創建,等到游戲場景中再進行業務模塊的創建和初始化工作,然后再進行場景切換。具體方案如下:
1. 先下載一個資源更新配置文件,此文件中有待資源下載列表、資源校驗 MD5 信息。
2. 根據資源下載列表,將校驗 MD5 和本地文件進行對比,如果相同則不下載,如果不同則下載。
3. 下載完畢后,進行 MD5 校驗,如果校驗不通過則刪除本地文件,重新走下載流程。這里的 MD5 校驗,不僅可以校驗資源下載是否正確;對于防止資源被惡意修改,資源反作弊也有一定作用。
4. 修改 cocos 引擎源碼, 在 load-pipeline 中,將資源讀取替換成讀取本地的下載文件。
由于游戲運營中可能會有 Bug 發生,需要下發客戶端補丁。資源更新配置文件可能會被多次修改,而 CDN 更新會有延遲問題,導致部分玩家下載的配置文件可能是較舊的版本。而且有部分中小運營商,為了成本考慮,會緩存舊的文件。以往的項目在發生這種情況時,一般是聯系玩家進行定位,發現是運營商問題再反饋給運維同學,由網絡部門的同事推動運營商進行修改,效率不高。為了減少這種情況發生的可能性,我們使用了雙 CDN 策略。
具體的做法是,對于同名文件增加版本號機制,更新文件時將文件內部存儲版本號 +1,并在 2 個不同的 CDN 進行更新。客戶端下載時,下載 2 份文件,取版本號大的為準。這樣當更新配置文件時,2 個不同 CDN 只要有一個同步到即可,既能減少了 CDN 更新延遲,又降低了運營商緩存問題出現的概率。
3、性能優化
和一般的游戲不同的是,微信小游戲平臺本身的 js 腳本執行效率較弱,iOS 環境小游戲 javascript 引擎目前使用的是 JavaScriptCore,默認沒開 jit 優化,js 執行速度會比手機 safari 慢,從簡單測試結果來看,速度會慢兩倍左右。從 Profiler 來看,js 腳本執行時間會占到 80% 左右。因此減少腳本的計算量也是性能優化一個重要的方面。
優化
為了解決這些問題,項目組做了以下優化
DrawCall
渲染批次合并和大多游戲項目類似,需要合理的規劃圖集的使用,將同一個層次的 GameObj 使用的圖片資源進行拼圖。
可以分為地圖背景層、地表、地圖物件、坦克、子彈、特效、UI 等拼圖,盡量確保同一個層次的游戲對象使用相同的圖集,相鄰的精靈使用的材質相同。
mask
游戲中會顯示玩家的圓形頭像,而微信平臺下載的頭像是矩形。原先頭像顯示使用的是 cocos 的 mask 組件進行渲染,效率較低。我們自己實現了一個基于 mesh 的控件,將一個圓等分為 n 個三角形,給這些三角形頂點賦予相應的 UV,從而畫出一個圓形頭像。減少了頭像渲染時的批次開銷。
碰撞檢測
Cocos Creator 自帶的碰撞系統效率不高,沒有做空間劃分,不適合大量單位的碰撞檢測。并且每幀都需要更新碰撞體的碰撞盒。我們游戲地圖中存在大量的靜態物件(如地圖中的磚塊、主基地、鋼板等),而玩家在場景中移動時,是通過移動攝像機達到地圖視野的變化,所以大量的地圖靜態物件的世界坐標是不變的,他們的碰撞盒只需要計算一次即可。
為了解決這個問題,我們給 cocos 的 node 增加了一個屬性 static,static 節點的計算結果可以緩存起來,避免重復計算。
對象池
游戲中的坦克、子彈、磚塊等采用對象池,進入戰斗場景時有足夠數量的預加載,戰斗過程中進行復用,避免實時的對象創建與銷毀。
避免場景、節點更新
分析 Cocos Creator 的源碼發現,當有節點發生 active,會觸發遞歸遍歷場景,開銷較大。
為了避免這類開銷,游戲中的物體死亡時,不會將其從場景中移除或禁用,而是設置死亡狀態,通過移動坐標到很遠的地方,代碼中不執行相應的邏輯處理。盡量保持幀率平穩,避免性能曲線的毛刺
裁剪
當物體不在主角視野范圍內并且不是持久播放的特效和聲音可以進行裁剪不播放。
機型適配
對于美術資源進行了高、中、低 3 檔分級,由策劃在資源表格中配置不同分級下的資源名稱。游戲過程中,根據機型和實際性能表現,選擇一種檔次進行表現。
圖中橫坐標是時間(單位秒),縱坐標是 FPS,可以看出 FPS 有了明顯提升。通過一系列的優化措施,最終保證了低端機 iphone5S 基本能滿足游戲需要。
以上就是《歡樂坦克大戰》微信小游戲開發總結,有興趣的小伙伴可以一起來交流哦~