記錄戰鬥記錄你,詳解妖尾戰鬥錄像系統

妖尾歷經幾年開發,終於在今年6月底順利上線,至今運營兩個多月,筆者從2017年初參與開發,主要負責妖尾戰鬥系統開發,一路解決了一些技術問題,踩了一些坑,感受有很多點是值得記錄和分享的,但願能借幾篇文字,系統性總結MMORPG戰鬥系統的開發經驗。
本文主要介紹戰鬥錄像系統,戰鬥錄像基本是全部MMORPG遊戲的標配系統,它同時也能成爲開發調試利器,在整個開發階段扮演重要角色。html

首先是調試利器

一些項目組開發戰鬥系統時,可能會優先開發涉及表現的相關功能,迭代的新增戰鬥表現,修復Bug,直到整個戰鬥表現看起來至關完整了,到了後期再應策劃要求,補充戰鬥錄像系統。筆者項目是在開發中期加入戰鬥錄像功能的,在經歷完整個戰鬥開發階段後,得出的經驗是儘量在基礎框架搭建、先後臺開始聯調階段就同步開發戰鬥錄像系統,利用戰鬥錄像來輔助系統開發、調試。到了項目中後期,戰鬥錄像會發揮更大的用處,此時戰鬥系統已經提交到SVN版本控制,項目組全部人均可以體驗到戰鬥系統,全部人都或多或少地扮演測試人員的角色,項目羣會頻繁地反饋戰鬥系統的表現問題,諸如報錯、卡死,單位詐屍等等,什麼反饋都會有,總之發揮你的想象力。當時開發會頻繁地奔波於各個項目組成員的電腦面前,溝通、查看日誌,嘗試弄清問題,有了戰鬥錄像後,咱們會讓對方給錄像文件,在本地環境重放戰鬥錄像,重現現場慢慢定位問題。
戰鬥錄像能多大程度的輔助開發調試,取決於相關工具鏈有多完善,下面介紹妖尾項目對於工具鏈的打造。首先簡單看下戰鬥錄像框架:web


通常來講,網絡底層往上還會有一層業務網絡層,妖尾的業務網絡層分紅兩個,一個負責普通業務邏輯,一個網絡層供戰鬥專用。經過在戰鬥網絡層接口插樁,戰鬥錄像模塊就能收集一場戰鬥的全部數據。戰鬥結束後,自動將該場戰鬥數據保存成本地錄像文件,固然,咱們還要提供手動保存錄像接口,以便戰鬥中途卡死了也能保存錄像。雖然有了戰鬥數據,還要配備一套完整功能的GUI工具才能提升調試效率,所以筆者基於Unity開發了戰鬥錄像播放器工具。

 

上圖是戰鬥錄像播放器通過幾回迭代後的截圖,除了實現最基本的播放錄像、查看數據功能,還有查看設備數據,上傳/下載錄像、生成戰鬥播報、差量構建指定回合戰場包等功能。筆者以爲在開發初期,先實現播放錄像、查看數據的功能就能知足大部分調試需求了,開發時間成本只有2-3天,但它會在以後1-2個月甚至更久的前期開發階段幫你縮短調試定位時間,節省更多時間早(bu)點(cun)下(zai)班(de),或幫策劃作更多需求,重要的是解放心態,再也不疲於溝通Bug,構造現場,由於現場就在錄像裏。數據庫

簡單描述這套調試工具的使用姿式:緩存

  • 開發過程當中遇到了戰鬥Bug,若是在第一時間沒法判斷Bug緣由,先保存錄像,再逐步分析問題。
  • 選擇報錯的戰鬥錄像,經過時間戳/快速模式重跑戰鬥,逐步縮小問題範圍:觀察戰鬥錄像播到第幾個回合報錯,是資源加載、選招仍是表演階段報錯,經過報錯前的日誌,逐步定位是哪一個類哪一個接口的問題,再猜想並驗證某行代碼,直到問題解決。
  • 若是不是卡死報錯,戰鬥也能跑完,但策劃反饋某個技能/Buff表現與預期不一樣,就要查看關鍵表演包的數據,看是後臺傳的有問題,仍是前臺表現沒作對。
  • 上面兩類問題的排查一般是沒法一步到位的,排查過程會不斷追蹤代碼給可疑代碼打Log,會臨時修改某些變量,會臨時修改某段代碼邏輯,依靠不斷重跑戰鬥來驗證。
  • 解決Bug的過程也少不了跟後臺的溝通,在這以前,後臺重數據輕表現,前臺重表現輕數據,致使一種現象就是後臺找前臺問表現,前臺找後臺問數據,溝通成本比較高。有了這套工具,前臺開發對於這場戰鬥包括服務器、角色ID、戰鬥ID、戰場ID,協議數據等信息都瞭如指掌,快速分析出是前臺問題就直接修復,是後臺問題就告訴對方去修復哪塊數據。

這裏另外分享1個Bug調試修復的經驗。我的認爲Bug修復總時間 = 問題溝通時間 + 問題定位時間 + 代碼修改時間 + 編譯驗證時間,像戰鬥這類大型系統,可能會經歷多輪問題定位、代碼修改、編譯驗證才能修好1個Bug。Lua代碼作好Hot reload開關,最好作到修改某處代碼,重進戰鬥就能驗證最新代碼。每次重啓遊戲至少花費30+秒,1個Bug平均幾回重啓驗證就是幾分鐘時間,作好Hot reload節省下的時間至關可觀。服務器

初期在項目組內推行用錄像反饋戰鬥Bug時,咱們讓你們把保存下來的錄像文件單發給戰鬥開發來調試,很快發現用戶體驗並不友好,不是全部人都是開發,你們不清楚錄像保存到哪一個目錄了,找到目錄,他們也弄不清楚要發哪一個錄像給開發。在忍受了一段時間的靈魂三連問後,筆者又加上了錄像上傳/下載功能。網絡

上面兩張圖是錄像上傳/下載流程及錄像下載頁面。咱們將Bug反饋操做簡化成遊戲內一鍵反饋,點擊按鈕就能自動保存錄像文件,並將二進制文件數據Base64編碼成字符串,利用魔方質管組幫忙搭建的Web服務,經過Http請求將數據上傳到Web服務器保存數據庫,開發經過Web頁面就能夠搜索/下載base64字符串格式的錄像文件,最後錄像播放接口作適配,支持二進制/base64字符串兩種格式數據的錄像播放,整個環節就打通了。數據結構

開發階段咱們自行開發了戰鬥錄像來輔助調試,確實也是到了戰鬥系統基本穩定後,策劃們才先後提了戰鬥錄像的正式需求,先作了一版基於服務器保存的活動錄像,又作了一版基於客戶端保存的戰鬥錄像大廳。app

先後作這兩版錄像需求,雖然都是觀看錄像,但其實現大不相同,所以須要謹慎設計整個錄像模塊,讓兩套邏輯獨立並行,能共用底層功能,並儘可能保持外部接口一致性。上圖是整個戰鬥錄像的模塊劃分,可劃分爲實現戰鬥錄像基礎功能的核心模塊,及涉及界面UI的兩版業務功能模塊。BattleReplayManager是核心類,它對外接收錄像相關的控制請求,對內調度其餘核心模塊類,獲取/保存/構造數據,控制錄像播放流程,並經過給戰鬥網絡層發送協議數據影響戰鬥表現。框架

服務器錄像

基於服務器保存的活動錄像,全部數據都由服務器提供。前臺首先發送觀看錄像請求,接收錄像概要數據包,獲取戰鬥波次/回合等信息用於顯示和跳回合。收到初始戰場包後進入戰鬥,在每回合表演完後請求下一回合表演數據。正常播放錄像時,收到的協議數據跟普通戰鬥是同樣的,但若是在戰鬥中途跳回合,除了新回合的表演包,還會收到新回合的戰場包,用於恢復新回合初的戰場單位狀態。這個過程跟戰鬥斷線重連恢復戰場是同一套邏輯,所以把戰鬥斷線重連的坑填完,實現服務器錄像基本沒有難點。ide

客戶端錄像

相對服務器錄像,實現基於客戶端保存的錄像功能要考慮比較多問題:

  1. 肯定錄像數據結構,用什麼數據結構存儲一場戰鬥的全部協議及相關信息較優?
  2. 保證錄製數據完整性。網絡抖動、切出遊戲再切回來等場景可能會致使少了某回合表演數據怎麼辦?
  3. 如何實現跳回合。一場正常戰鬥的協議包,除了初始戰場包,每一個回合只有表演包,沒有戰場包,跳回合怎麼恢復戰場狀態?
  4. 錄像上傳/下載的傳輸策略。協議收發有64kb限制,錄像文件大小超過了怎麼辦?
  5. 保證用戶體驗。評估極限狀況的錄像文件大小,保證流暢的錄像觀看體驗。

模塊開發初期就考慮這些問題,就能夠避免基礎設計出錯,後期積重難返的尷尬狀況。

1. 錄像文件結構

首先是肯定錄像文件格式,因爲妖尾協議基於pb通訊,錄像文件一開始就沒有打算自定義二進制格式,而是直接基於pb定義數據結構,這樣有幾點好處:

  1. pb傳輸效率高,並且開發熟悉pb,不像自定義格式還有理解成本,開發效率也高。
  2. 協議與錄像文件採用同種格式,比較容易根據查看列表,上傳/下載錄像等業務去反推最優的錄像文件數據結構。讓每份錄像文件既能夠有戰鬥錄像數據,也有關於錄像大廳的業務數據,一次設計,解決兩個問題。
  3. pb支持數據結構嵌套,列表,能作出錄像頭、錄像數據塊設計,上傳/下載協議也容易切分錄像文件作分塊傳輸。

基於幾點考慮,錄像文件由BattleReplayFile錄像頭、BattleReplayFileBlock錄像數據塊兩部分組成。BattleReplayFile的blocks字段用於存放BattleReplayFileBlock列表,BattleReplayFile其餘字段是概要信息。這樣查看錄像列表時,後臺只須要返回不帶blocks數據的BattleReplayFile列表便可。上傳/下載錄像時也能夠先傳錄像頭、再批量分次傳錄像數據塊。

message BattleReplayFile
{
    optional string name = 1;                       // 錄像文件名
    repeated BattleReplayFileBlock blocks = 2;      // 協議文件塊
    optional uint32 block_num = 3;                  // 協議文件總塊數
    repeated string ext_info_keys = 4;              // 錄像額外信息參數Key
    repeated string ext_info_values = 5;            // 錄像額外信息參數Value
    ... // id、時間、雙方成員、回合、波次等錄像概要信息
    ... // 簡介、點贊、收藏等錄像大廳業務信息
}

message BattleReplayFileBlock
{
    optional uint32 index = 1;                  // 協議塊序號
    optional string name = 2;                   // 協議類名
    optional bytes data = 3;                    // 協議數據
    ... //時間、回合等其餘信息
}

2. 錄像文件校驗

網絡抖動、切出遊戲再切回來等狀況致使斷線重連,可能致使戰鬥錄像數據損壞,所以保存本地前先作錄像文件校驗,判斷有沒有丟關鍵協議包,包括初始戰場包、入包表演包、各回合表演包及退出戰場包,保證協議包序,經過校驗才保存錄像文件,不經過就提示玩家錄像數據損壞沒法保存。

3. 錄像回合跳轉

一場戰鬥錄像單靠收到的協議包,能夠正常順序播放整個戰鬥,卻不能跳轉回合播放,由於中間跳過了幾次合的表演演算,戰鬥邏輯層沒法將戰場數據修正成跳轉回合的狀態。服務器錄像能夠依靠後臺發跳轉回合戰場包作恢復,客戶端錄像就要靠前臺本身處理,用錄像表演包演算出跳轉回合的戰場狀態。

第一直覺是在戰鬥邏輯層處理跳出的表演包,只是跳過表演,直接作數據演算,但稍加思考會發現有不少問題:戰鬥邏輯層裏,數據與表現基本耦合在一塊兒,畢竟這樣的編碼實現方式最直觀。想抽離表現只演算數據,只能在原有代碼里加ifelse分支,重寫數據演算邏輯。幾十個表演類,新增這麼多分支,編碼再加調試,必然失去對代碼的把控,也破壞了原有系統穩定性。即便哼哧哼哧硬寫下來,也會發現只實現了向後跳轉回合,沒實現向前跳轉回合,由於戰鬥邏輯層實現的是按回合往下演算的邏輯。

跳出這個誤區,咱們認爲戰鬥錄像數據應該要有每一個回合的戰場包,跳轉時供戰鬥邏輯層重置回合戰場,所以後臺修改了戰鬥邏輯,每回合都會發當回合戰場包,這些戰場包作了特殊標記,只用於錄像存儲,不會影響戰鬥邏輯,實現起來很快,但也清楚有明顯效率問題。

基本上,戰場包都會比表演包大,甚至大不少,若是某個回合技能不太複雜,那表演包數據其實很是小,爲了實現跳回合,由後臺給每一個回合加發戰場包,會很是影響戰鬥的協議數據量,保存錄像文件變大,也會增長上傳/下載錄像時的負擔。這麼實現不合理的點在於,每回合戰場包實際上是冗餘數據,每回合狀態是能夠經過初始戰場包加表演包推算出來的。爲了優化這個問題,前臺實現了一個戰場包構建器,以初始戰場包、回合1~n-1表演包爲輸入,輸出目標回合n的戰場包。這樣在保存錄像時不須要保存回合戰場包,錄像跳轉回合時由構造器動態生成戰場包便可。編寫調試戰場包構建器時,要注意檢查先後臺的戰場包差別,咱們會打印戰場包數據,經過Beyond Compare查看差別,不斷調整代碼,直到構建的關鍵數據一致爲止。戰場包構建器調試好後,只要後續不新增表演類型,就能夠保證構建器可信可用,即便新增表演,代碼工做量也不多。

優化完作下簡單測試,打了一場40回合的5v5 pvp戰鬥保存錄像,比較兩種方案的保存錄像文件大小:優化後文件大小是優化前的65%,減小了252KB,因爲5v5pvp表演複雜,所以回合表演包數據自己也很是多,換作是通常的戰鬥,數據優化比率會更高。

4. 錄像上傳/下載策略

妖尾一次協議收發有64KB大小限制,看前面的數據可知,回合數比較多的戰鬥錄像文件大小確定會超過64KB,咱們既不但願上傳/下載錄像單次傳輸的數據量超過64KB,又不但願單次傳輸數據量太少,致使協議發送次數過多,浪費太多時間在RRT上,所以採用的錄像傳輸策略是,首次傳輸單獨發送錄像頭,後續傳輸錄像數據塊切塊傳輸,保證每次傳輸的全部BattleReplayFileBlock的data總大小不超過50KB。採用這樣的策略,5回合之內的小型戰鬥基本都能分2次傳輸完畢,像上面的5v5 pvp大型戰鬥則須要進行11次傳輸。這就引出了下個問題思考,大型戰鬥的錄像觀看會不會有體驗問題。

5.流式傳輸及錄像緩存

戰鬥錄像大廳的設計初衷,是讓玩家能夠自主分享/觀看他們以爲滿意的戰鬥錄像,因此咱們猜想玩家會比較多的上傳/下載/觀看大型pvp戰鬥錄像,對於上傳而言並不會有什麼問題,由於就是一次性操做,但對下載/觀看場景就要儘可能進行優化,咱們不但願玩家每次看錄像,都要有感知地等待一會,等上10次網絡回包,下載完錄像文件才能觀看錄像,也不但願玩家每次看錄像都得重複下載文件,對玩家的手機流量也很不友好。

針對這兩點問題,戰鬥錄像參考網絡視頻的作法,加上了流式傳輸及錄像緩存的特性。

如上圖所示,流式傳輸的目的在於優化玩家觀看新錄像的體驗,無論一個完整的錄像有多大,須要多少次傳輸才能完成,只須要先得到部分頭部數據,就能觀看錄像。前臺只須要頭2次回包,獲取錄像概況、初始回合戰場包和表演包,就足以表演第1回合的戰鬥,進入錄像戰鬥後,靜默下載其他的錄像數據,通常後續的錄像數據下載速度遠遠快於戰鬥表演速度,這樣徹底不影響整場戰鬥的錄像觀看。假設網絡環境極端惡劣,表演完當前回合戰鬥後,後續錄像數據還沒返回,BattleReplayManager會每幀輪詢等待下個回合表演數據,即便網絡斷掉了拿不到數據,玩家仍然能夠點擊按鈕退出戰鬥錄像。

錄像緩存的目的則在於優化玩家重複觀看錄像的體驗,減小流量消耗。當看過一次錄像,下載了完整的錄像數據後,前臺就會把錄像保存到本地緩存起來了,儘管錄像頭裏存儲了部分戰鬥錄像大廳的字段,好比點贊、收藏數等,這些字段數據會失效,但戰鬥數據是不會變的。查看大廳的錄像列表時,後臺會返回只有錄像頭BattleReplayFile,沒有數據塊BattleReplayFileBlock的列表,玩家請求觀看時,判斷本地緩存有沒有該錄像緩存,有就再也不走原來的下載流程,直接讀取緩存文件播放便可。

洋洋灑灑寫了一些關於戰鬥錄像的總結,也確實是由於錄像系統對戰鬥開發調試有所幫助,做爲一個功能系統,也須要在早期考慮一些問題,作設計和優化,但願本文能對MMORPG或其餘類型遊戲戰鬥的設計開發,提供一些借鑑經驗。

附上咱們的遊戲官網[妖精的尾巴:魔導少年],快來玩吧~

相關文章
相關標籤/搜索