1. 程式人生 > >【業界】 《王者榮耀》技術總監覆盤回爐歷程

【業界】 《王者榮耀》技術總監覆盤回爐歷程

  2015年8月到10月的兩個月間,《王者榮耀》從資料不達標搖身一變成了接下來兩年國內最大的爆款

《王者榮耀》技術總監覆盤回爐歷程:沒跨過這三座大山,就是另一款MOBA霸佔市場了

    如今已經大獲市場成功的《王者榮耀》一直是業內各方關注的物件,而我們也知道這款產品在成為國民級遊戲之前,也遇到過一段鮮有人知的調優期。也就是在2015年8月18號正式不刪檔測試版本推出之後,被騰訊評級為不達六星之後的時間。


    據瞭解,在8月之後的兩個月間,《王者榮耀》技術團隊對這個產品進行了非常深度的優化,並攻克了局內同步、網路要求,以及效能表現的三大難關,成功達到了騰訊六星產品的標準。比如延遲、卡頓等不同步問題的出現概率從過去的1%,降低到了0.01%,大幅度地改善了遊戲體驗。


    今天在Unity舉辦的Unite 2017 開發者大會上,《王者榮耀》專案組技術總監鄧君針對這款遊戲的調優歷程進行了覆盤,回顧了這款產品在技術層面遇到的三大問題,以及他們的解決方案。


    值得一提的是,參會的開發者也非常關注這次的演講內容,不僅會長當中人員爆滿,圍住了整個演講場地,在演講結束之後,葡萄君也聽到一路上諸多開發者對《王者榮耀》技術經驗的探討。

          


以下內容經遊戲葡萄整理髮布。


   大家好,我是《王者榮耀》的鄧君,很高興今天能夠有這樣一個機會跟在座的同行一起聊聊技術,互相交流,也感謝Unity提供這樣的機會,可以由一個互動。


                      

這次的主題主要是講一下《王者榮耀》從立項之初經歷的慘淡時期到華麗的翻盤,包括碰到技術方面的問題,以及遊戲方向上的改變。


我是技術出身的,整個課題也是技術面的,會重點介紹王者榮耀和現在見到大部分不同的技術方案,它實際原理、問題和優化的思路。

                     


    先簡單自我介紹一下,我是2004年加入騰訊,在騰訊做了4年多的應用層面開發,還包括web各種各樣後臺都做過,經歷比較豐富。在2009年我回到成都,剛好成都的崗位也就只有遊戲部門是比較合適的,就轉行做遊戲了。在成都這邊,參與過《QQ封神記》的開發,之後又開發了《霸三國OL》這款遊戲,這款遊戲開發了三年多,我經歷了它從1.0、2.0,再到3.0的版本迭代,這之後才轉型做手遊,直接做的《王者榮耀》。

                       

    現在瞭解《王者榮耀》或者在玩的人確實比較多,但是我們曾經也沒有想過它有這樣的結果。當時端遊很久都沒有做出來成績,業績和收入都面臨比較大的問題。2012、2011年前後,《霸三國OL》做到1.0版本,遊戲中玩家需要控制多個單位,操作起來很難,一開始可以操作5個單位然後變成3個,但即便只有3個單位,操作也讓人覺得也很痛苦,於是我們慢慢5個單位的技能合在一個英雄身上,不斷地優化。能看到,在MOBA領域,你要做創新,還要脫穎而出,是很難的事情。


    在2014年底,2015年初的時候,我們準備組建一個手遊團隊。因為當時國內市場基本上都在開發手遊,能夠繼續開發端遊或者要準備立項端遊的非常少,包括騰訊也只有2、3款端遊在開發。手遊是一個機會,我們當時就希望在2015年把我們的《霸三國OL》端遊在手機上呈現。


    這個時候我們進行了一個初期Demo的驗證,做Demo的只有三個人,引擎、框架、後臺,一系列製作下來大概花了兩週到三週的時間。這個Demo裡有基本的進遊戲、選人,然後可以釋放技能,正常的戰鬥,到結算。


    Demo的引擎我們採用了Unity,做完之後也覺得Unity很好用,開發效率確實比較高。


    2014年年底的時候,我們製作人去公司開會,當時做一個非常明智的決策:我們需要馬上暫停端遊的開發,直接做手遊。就是這樣的一次決策,真正地扭轉了我們整個團隊的命運。如果晚一年,可能今天爆紅的MOBA遊戲就是另外一個,而不是王者了。


    於是接下來在2015年,我們才有了想法開始獨立招聘20、30人來做這個手遊專案。


    從端遊轉型做手遊,肯定要面臨選擇,到底要用什麼樣的引擎,採用什麼樣的方案進行手遊的開發。當時,騰訊以及成都周邊的創業團隊,基本上都用Unity,我們做Demo的時候,也選擇大家用過的,已經有產品進行驗證的引擎,同時我們也考察它適不適合我們的團隊。


    Unity在我們當時做Demo時的理解來看,它確實對中小團隊,甚至對一些大型專案來說,都有幾個比較明顯的優勢。


        1.易上手,我們花三週就可以做出Demo,可以看到易上手是它的一個非常大的優勢。


        2.它的工具都是很完善的,能夠做到一站式解決,你不需要在這裡面下載工具,那裡面額外補充一些外掛。


        3.它外掛資源很豐富,我們從最開始做Demo的時候,基本上都可以從Asset Store中找到一些可以用來驗證我們想法的資源,可以加快我們開發的效率。


        4.上面這三點加起來,就能體現出它非常明顯的優勢,即開發效率特別高。


        5.它還有跨平臺的優勢,它本就是跨平臺的引擎。


        6.最後它還能讓你更方便地補充技術人員,社招也很容易招聘到熟悉Unity的開發人員。


    對比以前我們自己做引擎,或者用過其他的引擎,從效率上來講,最終我們選擇了一個開發效率最高的引擎Unity。

                     

    我們從端遊轉手遊是在2014年底,但真正開始研發《王者榮耀》是在2015年3月份的時候,這個時候專案的要求是讓開發的週期儘量短,儘快把手遊做上線。我們原本在《霸三國OL》的開發團隊大概有40、50個人,再加上後來加入的人員,形成了100多人的團隊,進行了遊戲的開發。


    在2013、2014年的時候,手遊在PvP的方面都比較弱,大部分是卡牌遊戲、單機遊戲。我們原本是做的端遊,它的生命力包括趣味性也是很足的,所以我們做手遊的目標,就是即使遊戲裡會存在PvE的闖關內容,但我們的核心還是要把PvP做好,讓玩家有真正的對抗,讓玩家與玩家有交流,能體會到這樣的遊戲樂趣。


    既然選擇PvP,那麼這款產品就是一個網路遊戲,而選擇網路遊戲的同步機制,就是我們首要考慮的問題。同步機制中最常見的應該是CS狀態同步,我們的端遊也是這樣做的,當然,狀態同步也有他的優缺點。


    先看一下狀態同步的優點。


      第一,它的安全性非常高,外掛基本上沒有什麼能力從中收益。


      第二,狀態同步對於網路的頻寬和抖動包有更強的適應能力,即便出現了200、300的輸入延遲再恢復正常,玩家其實也感受不到不太舒服的地方。


      第三,在開發遊戲過程中,它的斷線重連比較快,如果我的遊戲崩潰了,客戶端重啟之後只需要伺服器把所有重要物件的狀態再同步一次過來,重新再創建出來就可以了。


      第四,它的客戶端效能優化優勢也比較明顯,比如優化時可以做裁剪,玩家看不到的角色可以不用建立,不用對它進行運算,節省消耗。


    再說一下我認為的缺點。


      第一,它的開發效率相對幀同步而言要差一些,很多時候你需要保證伺服器與客戶端的每一個角色物件的狀態之間保持一致,但事實上你很難做到一致。


        比如客戶端和伺服器端更新的頻率,對優化的一些裁剪,網路的抖動等等,你要讓每一個狀態在客戶端同步是比較難的,而你要想除錯這些東西,來優化它帶來的漏洞、不一致的現象,花費的週期也會比較長,想要達到優化好的水平也比較難。


      第二,它比較難做出動作類遊戲打擊感和精確性。比如說你要做一個射擊類角色,他的子彈每秒鐘要產生幾十顆,基於狀態同步來做是比較難的,因為系統在很短時間內,會產生很多資料,要通過建立、銷燬、位置運算來同步。


      第三,它的流量會隨著遊戲的複雜度,而逐漸增長,比如角色的多少。我們做《王者榮耀》時,希望在3G、4G的網路條件下也能夠玩PvP,所以我們希望它對付費流量的消耗能控制在比較合理的水平,不希望打一局遊戲就消耗幾十兆的資料流量。

                    

   而另一種同步策略是幀同步。


   這種技術應用的很廣泛,最早的《星際爭霸》《魔獸爭霸3》都採用了幀同步,他們都基於區域網執行,網路的條件非常好,也不需要伺服器就能搞定。幀同步的優點有幾個:


      第一,它的開發效率比較高。如果你開發思路的整體框架是驗證可行的,如果你把它的缺點解決了,那麼你的開發思路完全就跟寫單機一樣,你只需要遵從這樣的思路,儘量保證效能,程式該怎麼寫就怎麼寫。


        比如我們以前要在狀態同步下面做一個複雜的技能,有很多段位的技能,可能要開發好幾天,才能有一個稍微過得去的結果,而在幀同步下面,英雄做多段位技能很可能半天就搞定了。


      第二,它能實現更強的打擊感,打擊感強除了我們說的各種反饋、特效、音效外,還有它的準確性。利用幀同步,遊戲裡面看到這些揮舞的動作,就能做到在比較準確的時刻產生反饋,以及動作本身的密度也可以做到很高的頻率,這在狀態同步下是比較難做的。


      第三,它的流量消耗是穩定的。大家應該看過《星級爭霸》的錄影,它只有幾百K的大小,這裡面只有驅動遊戲的輸入序列。幀同步只會隨著玩家數量的增多,流量才會增長,如果玩家數量固定的話,不管你的遊戲有多複雜,你的角色有多少,流量消耗基本上都是穩定的。這點延伸開來還有一個好處,就是可以更方便地實現觀戰,錄影的儲存、回放,以及基於錄影檔案的後續處理。


    幀同步也有它的缺點。


      第一,最致命的缺點是網路要求比較高,幀同步是鎖幀的,如果有網路的抖動,一段時間呼叫次數不穩定,網路命令的延遲就會擠壓,引起卡頓。


      第二,它的反外掛能力很弱,幀同步的邏輯都在客戶端裡面,你可以比較容易的修改它。但為什麼《王者榮耀》敢用幀同步,一方面是因為當時立項的時候開發週期很短,半年時間要做上線,要有幾十個英雄,存在時間的壓力,另一方面,MOBA類遊戲不像數值成長類的遊戲,它的玩法是基於單局的,單局的作弊修改,頂多影響這一局的勝負,不會存檔,不會出現刷多少錢刷多少好的裝備的問題,而且作弊之後我們也很容易監測到,並給予應有的懲罰,所以我們認為這不是致命的缺點。


      第三,它的斷線重回時間很長,相信臺下也有很多王者玩家,也曾碰到過閃退以後重回載入非常長的情況,甚至載入完以後遊戲也快結束了,這是幀同步比較致命的問題。


      第四,它的邏輯效能優化有很大的壓力。大家應該沒有見到哪一款大型遊戲是用幀同步來做的,因為這些遊戲的每一個邏輯物件都是需要在客戶端進行運算的。如果你做一個主城,主城裡面有上千人,上千人雖然玩家看不到它,但遊戲仍然需要對他們進行有效的邏輯運算,所以幀同步無法做非常多的物件都需要更新的遊戲場景。

                

    那麼我們為什麼選擇了幀同步而放棄了狀態同步呢?


    我們前面提到它兩個優點缺點是相對的,這邊的優點對於那邊來說就是缺點。對於我們手遊立項的時候,最重要就是時間。當時市面上正在開發的MOBA手遊不止王者一款,大家都在爭上線的時間,所以我們要選擇一個開發週期最短的方案。


    然後我們做端遊的時候也有一個深刻的體會,如果要做有趣的英雄,有趣的技能,它在狀態同步上面很難調出一個比較滿意的效果。所以最後我們依然選擇幀同步的方案。


    現在來看,選擇幀同步方案之後,我們再把它的缺點進行優化或是規避,之後它帶來的好處是非常明顯的。《王者榮耀》重除了英雄的設計以及技能的感覺,還有很重要的一點,就是它確實在做一些非常有特色的英雄,它的技能、反饋、體驗上面都做得不錯,這些都是基於幀同步技術方案帶來的優勢。

                  

   我們選擇了方案之後,當時覺得很high,覺得這樣一個技術方案開發起來得心應手,效率如此之高,做出來的效果也很好。


   但事實上,它也有好的一面,也有壞的一面,技術測試版本上線後質量不好,其中技術層面遇到的問題就是下面這三座大山。


      第一是同步性,同步性這塊容易解決,其實也解決了;


      第二也是最大一塊網路問題,幀同步它的網路問題導致我們對它技術方案的原理沒有吃透,碰到了一些問題,那時候遊戲的延遲很重,畫面卡頓,能明顯感覺走路抖動的現象;


      第三是效能問題,這個問題始終存在,我們也一直在優化。

                      

   第一座大山,最容易解決的同步問題。


    幀同步的技術原理相當簡單,10、20年前在應用這種技術了,從一個相同初始的狀態開始,獲得一個相同的輸入,往下一幀一幀執行,執行時所有程式碼的流程走得都是一樣的,這個結果呼叫完了以後,又有一個新狀態,完成迴圈。相同的狀態,相同的流程,不停的這樣迴圈下去。


    這個原理雖然簡單,但是你要去實現它的時候,還是會有很多坑。

                    

   右邊寫的是實現要點,這是我們在解決第一座大山經驗的總結,也是我們實際開發過程當中做的事情。


      首先,我們所有的運算都是基於整數,沒有浮點數。浮點數是用分子分母表達的。


      其次,我們還會用到第三方的元件,幀元件也要需要進行一個比較嚴格的甄別。我們本身用的公司裡面關於時間軸的編輯器裡面,最初也是是浮點數,我們都是進行重寫改造的。


      再次,很多人初次接觸幀同步裡面的問題,就是在寫邏輯的時候和本地進行了關聯、和“我”相關,這樣就導致不同客戶端走到了不同的分支。實際上,真正客戶端跟邏輯的話,要跟我這樣一個概念無關。


      接下來還有隨機數,這個要嚴格一致。這是實現的要點,嚴格按照這上面的規則寫程式碼還是有可能不同步,本身就很難杜絕這樣的問題。


      最後,真正重要的是開發者要提升自己發現不同步問題的能力,什麼時候不同步了,不同步你還要知道不同步在什麼點,這是最關鍵的。你需要通過你的經驗和總結提升這樣的能力。這個能力還是通過輸出來看不同客戶端不同輸出,找到發生在什麼點。


      比如在《王者榮耀》裡,我們看到不同步的現象應該是這樣,有人對著牆跑,你看到的和別人玩的遊戲是不一樣的,就像進入平行世界。


   最開始測試《王者榮耀》的,我們希望不同步率達到1%,就是100局裡面有1局出現不同步,我們就算遊戲合格,但實際上對於這麼大體量遊戲來說,這個比率是有問題的,經過我們不停的努力,現在已經控制在萬分之幾,一萬局遊戲裡面,可能有幾局是不同步的。


   這個問題不一定是程式碼原因或者沒有遵循這些要點才出現的,有可能是你去修改記憶體,你去載入資源的時候,本地資源有損害或者缺失,或者是異常。說白了,你沒有辦法往下執行,大家走了不同分支,這都可能引起最終是不同步的。


   如果你不同步概率比較低,到了這種萬分之幾概率的時候,很難通過測試來去還原,去找到這樣不同步的點。


    最開始我們遊戲出現不同步的時候,就是在週末玩家開黑多的時候,隨著你的概率越來越低,基本上你就自己就還原不出這些問題了,只能依靠玩家幫你還原這樣的場景,來分析這樣的不同步問題。


    同步性遵循這樣的要點,按照這樣的思路來寫,加上你不同步定位的能力,有了監控手段能夠去發現,這個問題其實就解決了。解決之後,你就可以好好享受幀同步的開發優勢。

                            

    第二座大山就是網路,《王者榮耀》技術測試版本出臺的時候,延遲非常大,而且還是卡頓,現在看一下幀同步裡面比較特別的地方。幀同步有點像在看電影,它傳統的幀同步需要有buffer,每個玩家輸入會轉發給所有客戶端,互相會有編號,按順序輸入幀。


      比如我現在已經收到第N幀,只有當我收到第N+1幀的時候,第N這一幀我才可以執行。伺服器會按照一定的頻率,不同的給大家同步幀編號,包括這一幀的輸入帶給客戶端,如果帶一幀給你的資料你拿到之後就執行,下一幀資料沒來就不能執行,它的結果就是卡頓。


      網路絕對理想的情況下還好,但現實的網路環境不是這樣的。幀同步要解決問題就是除錯buffer,以前有動態的buffer,它有1到n這樣的緩衝區,根據網路抖動的情況,收入然後放到佇列裡面。


      這個buffer的大小,會影響到延遲和卡頓。如果你的buffer越小,你的延遲就越低,你拿到以後你不需要緩衝等待,馬上就可以執行。但是如果下一幀沒來,buffer很小,你就不能執行,最終導致的結果你的延遲還好,但是卡頓很明顯。


      如果調到幀同步的buffer,假如我們認為網路延遲是1秒,你抖動調到1秒,那得到的結果雖然你畫面不抖動了,但是你的延遲極其高。如果連最壞的網路情況都考慮進去,buffer足夠大,那麼記過就跟看視訊是一樣的,平行的東西,看你調大條小。一些區域性的措施我們都做過,都是一樣的問題。


   具體我們怎麼優化卡頓的問題呢?


      剛才提到該幀同步與buffer,這個buffer可以是1也可以到n,我們要解決我們的延遲問題,我們就讓buffer足夠小。事實上《王者榮耀》最後做到的buffer是零,它不需要buffer,伺服器給了我n,馬上知道是n,我收到n,我知道下一次肯定是n+1,所以我收到n之後馬上就把n這一幀的輸入執行了。


   那麼為什麼不卡頓了,畫面不抖動了?


      最後一個關鍵點,是本地插值平滑加邏輯與表現分離。客戶端只負責一些模型、動畫、它的位置,它會根據繫結的邏輯物件狀態、速度、方向來進行一個插值,這樣可以做到我們的邏輯幀率和渲染幀率不一樣,但是做了插值平滑和邏輯表現分離,畫面不抖了,延遲感也是很好的。


      做了這些後,我們還把TCP換成UDP,在手機環境下,弱網的情況下,TCP很難恢復重連,所以最後用了UDP來做。整體來說,在網路好的情況下,它延遲也是很好的,在網路比較差的情況下做插值,也是傳統CS的表現。


      我們經常見到角色A和B,有些客戶端A在左B在右,有些是A在右B在左,幀同步邏輯上面AB之間的距離和座標都是完全一樣,但是畫面上看到他們可能會不重合,那就是你把它們分離之後的表現。網路極其好的情況下,它應該是重合的,但是在網路差的情況下,可能會有些偏差。這裡面是最重要的一塊優化。

                              

    第三座大山,是我們對效能的優化。


      本身幀同步邏輯上面在優化上面存在一些缺點,所有的角色都需要進行運算。這方面我們也是藉助Unity的特性,如果你想追求效能上的極致,有些東西你需要尋求好的方式。


     第一點是熱點的處理。


       我們是不用反射的,它都有GC效能開銷,我們的做法裡面,會把物件的顯示隱藏放在不同的渲染層裡面,儘量讓整個遊戲幀率是平滑的過程。還有我們本身有自己的系統,比如AI,在《王者榮耀》這樣的多角色遊戲中,你如果想要做出比較好的體驗,那麼AI就要做得比較複雜。


      而要去優化熱點,我覺得就只有這三個步驟可以走。


      首先,從程式的結構上面能找到更優的,它的優化效果就是最明顯的其次,如果你的結構都是用的最好,就去挖掘區域性的演算法,調整你程式碼的一些寫法。最後,如果區域性的演算法都已經調到最優還是沒有什麼辦法,那只有一條路,就是犧牲整個質量,就是分幀降頻。


     第二點是GC,這塊剛才說不用反射,還有裝箱和拆箱的行為也是儘量少用。Unity指導過我們的優化,從GC上面的考慮,他們建議每一幀應該在200個位元組以內是比較好的狀態,其實很難做到,王者也是每一幀在1k左右,很難做到200。


     第三點是Drawcall,這些傳統的優化手段大家都用的很熟了。


     第四點是裁剪,幀同步裡面是不能裁剪的,表現裡面我看不到的可以降低頻率或者不更新它,這在表現裡面可以做的。


     第五點是3DUI的優化,比如《王者榮耀》的血條、小地圖上面疊的元素等等,這些UI都比較豐富,這塊我們用了31UI的方式來優化,沒有用UGUI裡面進行血條方面的處理。


     我們也犧牲了一些東西,我們把所有東西都載入了,在遊戲過程當中,我們希望不要有任何IO行為,包括輸出我們都是要佈局的。你處理的決策和複雜度,如果在一幀裡面放出100顆子彈,在放100顆子彈的時候一定要掉幀的,一定要在力所能及的時候把這些東西做到極致。

                              &nbs