1. 程式人生 > >【Egret優化分享】白鷺引擎王澤:重度H5遊戲效能優化技巧

【Egret優化分享】白鷺引擎王澤:重度H5遊戲效能優化技巧

本文轉自:https://mp.weixin.qq.com/s/GIzXA51D7_hMqajCRuJE2g

9月15日,無懼17級颱風“山竹”,320名開發者齊聚廣州貝塔空間共同探討“怎樣做一款賺錢的小遊戲”。針對眾多開發者關心的重度H5遊戲效能優化技巧,我們整理了現場速記分享給大家,詳見下文:

王澤:各位開發者下午好!我叫王澤,是白鷺引擎的首席架構師。

今天給大家分享的題目是《重度H5遊戲效能優化技巧》。之所以決定用這個題目,是因為我最近幾周在廣深一帶拜訪了很多使用白鷺引擎的開發者,發現特別是在廣州一帶,大部分開發者都在做重度H5遊戲。在幾周的拜訪過程中,我協助許多廣州的遊戲開發團隊進行遊戲效能優化,並在在這個過程中發現很多開發者遇到的問題是非常相似的,於是我借這次開發者沙龍的機會,把這些優化技巧整理出來與廣大開發者分享。

這些技巧都是我在實際專案中得到驗證過的優化技巧,並且都是投入產出比很高的優化項,建議做重度H5遊戲的開發者重點針對您們的遊戲進行這些優化。

在開講之前,我首先為大家回顧一下白鷺引擎的歷史上幾個比較重要的版本。2014年3月白鷺引擎釋出了0.9版本,併產生了《圍住神經貓》這一爆款。緊接著一鼓作氣釋出了1.0版本引入了完整的GUI,這使得開發者可以開始做具備大量複雜 UI介面的遊戲,《愚公移山》就是這樣的一款產品,他成功的在 2014年底就取得了每月180萬流水的成績。

接著在 2014年11月釋出了 2.0版本,重點包含了白鷺引擎的工作流工具集,特別是 UI 編輯器與動畫編輯器,這對遊戲開發效率帶來了重大提升,因此在 2014年底,使用了白鷺引擎 2.0 版本的《傳奇世界H5》 成為了第一款月流水超過千萬的 H5 遊戲。2016年1月,白鷺釋出了3.0版本,這個版本引入了 WebGL 渲染器,大幅提升了遊戲渲染效率,這個版本得到了開發者的普遍歡迎,《決戰沙城》更是用這個版本實現了 H5 遊戲月流水三千萬的成績。

2016年底,白鷺引擎自研了一款名為《莽荒紀》的產品,並同步推出白鷺引擎4.0版本。這款遊戲使用了大量的骨骼動畫,對動畫渲染效能和資源載入效率的要求非常高,得益於4.0版本針對這兩點的優化,這款產品在絕大部分裝置包括千元機上都有非常好的表現。2017年5月份,白鷺引擎釋出了 5.0版本,市場上也出現了第一款月流水過億的H5遊戲——《傳奇來了》。

緊接著,在微信釋出小遊戲後,第一時間釋出了引擎5.1版本正式支援微信小遊戲,《海盜來了》通過白鷺引擎的底層支援,僅用3天便將遊戲從H5版本釋出到微信小遊戲平臺,成為首款月流水過億的微信小遊戲。

回顧白鷺引擎的歷史,我們會發現,隨著軟硬體的不斷升級和白鷺引擎持續迭代,H5遊戲行業高度不斷被拉昇。但我們堅持不忘初心,仍然持續高效的對版本進行優化。

白鷺引擎目前按照雙週迭代的節奏,同時釋出5.2穩定版與5.3新特性版本,如果開發者想要開發商業化遊戲,可用5.2穩定版本來做;如果你想使用一些新的特性,比如說3D以及一些2D的高階優化技巧,就可以使用白鷺引擎最新的5.3版本來做。

白鷺引擎目前在H5遊戲的市場佔有率達到了70%。統計方法是:取樣愛微遊、瘋狂遊樂場以及QQ空間三個業內公認的頭部渠道的Top30的遊戲,逐一去看這些遊戲採用了哪些遊戲引擎。

除了H5遊戲之外,白鷺引擎在微信小遊戲上的市場佔有率53%。這裡有兩種統計方法:第一種統計方法是把所有能統計到的微信小遊戲都統計起來,一個一個抓包看是什麼引擎做的。

通過這種方式,統計到的白鷺引擎的市場佔有率高達80%,但是我認為這是有失偏頗的資料,因為能統計到的小遊戲有很大一部分是因為與白鷺官方人士有微信好友關係,而這部分好友幾乎都在使用白鷺引擎開發微信小遊戲,所以我認為這種統計方式是並不準確的。於是我們採用了另一種更為公平的統計方法,通過相對權威的阿拉丁統計資料,把阿拉丁榜單Top50拉出來,得出白鷺引擎市場佔有率為53%。

據不完全統計,這五年中,白鷺引擎累計運轉的H5遊戲和微信小遊戲的流水資料約為200億。非常感謝大家對白鷺引擎的支援。今天能有這個成績並不是完全是依靠白鷺自己做的,引擎技術並不能解決所有的問題,最大的外因是依靠各位一起努力的結果,特別是各位遊戲開發者,瀏覽器底層技術提供方、渠道方、以及很多支援夥伴的幫助。

除了外因的幫助之外,白鷺自身也幫助開發者做了很多事情,我個人是Flash頁遊研發出身,白鷺引擎團隊的大部分研發也都是遊戲行業出身,所以白鷺的研發團隊更瞭解也被稱為“手機頁遊”的H5遊戲開發者的痛點需求,並且這五年為各地的H5遊戲開發者提供了總計1400天的駐場支援,飛到各個城市,包括廣州、深圳、上海、廈門、福州、成都、武漢、南京、西安,當然還有北京。我平均基本上有三分之一的時間是不在公司裡的,都在為各地的開發者解決各種各樣的問題。通過這些比較務實、接地氣的方式,保證白鷺引擎可以執行在儘可能多的裝置上,並提升遊戲在低端機上的體驗。

今天廣州開發者沙龍的主題是“怎樣做一款賺錢的小遊戲”。我認為解決技術上的短板決定了遊戲是否能賺錢的下限。針對遊戲運營的幾個關鍵資料,遊戲的執行效能主要影響玩家的線上時長,遊戲載入效能主要影響玩家的前期留存。我今天重點介紹如何提升遊戲的執行效能。

我見過的大部分尋求技術幫助的 CP 都會提及遊戲效能需要提升。但是我認為效能糟糕是一個技術術語,它在產品上一般有如下三種體現方式:

1、幀頻很低。

2、裝置發熱。

3、不定期卡頓。

雖然看起來這些問題都是效能問題,但是產生這些問題背後的原因則是完全不一樣的。

在解決問題之前,需要將這些問題首先輸出一個可量化的資料指標。

幀頻很低可以被量化為:在特定裝置上的幀頻是XX幀,其中 JavaScript 邏輯開銷 XX毫秒,渲染開銷YY毫秒,這些資料在白鷺引擎的效能面板中都有體現。

裝置發熱看似是很難量化的,並不是所有作業系統都提供了裝置溫度的 API。因此我們向開發者推薦另一種方法作為量化方式,首先將裝置充滿電,然後統計遊戲在 XX分鐘後的剩餘電量。由於耗電量和發熱基本成正比,所以解決耗電問題,發熱問題就也能同步得到解決。

至於不定期卡頓。一定要記錄卡頓是否存在規律。比如是播放動畫的瞬間?開啟UI面板的瞬間?或者是毫無規律?

上述問題量化之後,接下來再來逐一地嘗試解決這些問題。

幀頻低和發熱主要有如下四個原因:

1、渲染內容過多。

2、渲染方式不當。

3、計算開銷過大。

4、 大量建立物件。

這四點又分屬兩個類別,分別是 JavaScript邏輯開銷和引擎渲染開銷。關於渲染內容和渲染方式不當最終是可以在引擎渲染層這個環節想辦法解決的。而計算開銷過大和大量建立物件都是在使用者邏輯的JavaScript層去解決的。這兩塊的解決方式是完全不一樣的,對渲染來說,你需要去嘗試理解WebGL底層的渲染原理是什麼,而對於JavaScript,你需要了解JavaScript底層的一些原理。

首先聊聊引擎渲染層面的東西:

1、渲染內容過多。在螢幕之外的內容,可以設定隱藏,不要執行渲染。這就提到一個很有意思的問題了,看似很簡單優化方法為什麼不在白鷺引擎內部實現呢?其實這涉及到白鷺引擎的一個核心設計理念:不要替開發者去做“自作聰明”的優化。這樣才能保證優秀開發者做出更好的遊戲。除了螢幕外的內容不進行渲染之外,遊戲普遍有很多UI彈窗,當你開啟彈窗的時候,強烈建議你把遊戲背景隱藏,這同樣可以節省大量的渲染開銷。

2、渲染方式不當。來看看底層原理:

白鷺引擎2D是如何渲染遊戲的多張紋理的? 在白鷺引擎裡,2D是一次性提交所有的資料,然後設定渲染模式,執行渲染批次,再設計渲染模式,再執行渲染批次。如果你能保證渲染模式這個東西是沒有發生變化的,就可以一次儘可能多地渲染,在這種情況下就可以做一次的渲染批次,這個優化聽起來很簡單,我說說在實際遊戲裡的典型案例。

這張圖是我昨晚自己畫的示例圖。做遊戲時經常會遇到這樣的場景,就是有很多人、很多怪,每個人都包含了影子、模型動畫、血條三個部分。最簡單的渲染方式是,將一個人設定成一個 DisplayObjectContainer,這個物件有三個子物件:一個人、一個影子和一個血條,這樣每個人的渲染次數就是3,進而8個人的渲染次數就是24。優化後是10,如何做這個優化?方法非常簡單,就是你把所有的影子放在一個Container上,把人放在一個Container上,再把血條放在一個Container上。

由於所有影子的紋理都是一樣的,所以引擎底層會自動開啟批次合併,渲染次數是是1,然後渲染8個人,這8個人的紋理一般都是不一樣的,所以就是8,上面就是血條的紋理也是一樣的所以也是1。把這三者加起來,最終的優化結果就是從 24降低到10。

第二個示例。這是大家做的重度遊戲的典型UI,DrawCall是30,這種遊戲可以做很多優化,就是把所有的圖片、文字合成一張紋理集。這個全做完之後,渲染批次就從30變成2,之所以不是1而是2,是因為右上角的lv888肯定是個動態文字,無法參與批次合併。

所以這就是一個簡單的例子,希望大家以後做UI 時可以嘗試著去把所有的動態文字都儘可能放在最上層,把圖片都放在下層,並將這些圖片合併成紋理集。特別是在遊戲的 ListItemRenderer 之中,一般遊戲中的一個 List 至少會顯示 5個 ListItemRenderer,如果你能將 ItemRenderer的DrawCall降低5,那整體的 DrawCall就能降低 25,所以針對ListItemRenderer的優化是投入產出比非常高的,強烈推薦各位開發者重點優化這裡。

3、計算開銷過大。對骨骼動畫使用快取,優化骨骼開銷;避免大量的數學計算與浮點數計算;邏輯幀與渲染幀分離。這個提升是比較明顯的,因為很多遊戲都是做30幀的,但是現在有些是60幀,所以要作一些邏輯幀和渲染幀的分離,邏輯上可以是15幀,然後渲染上做60幀,那麼邏輯的開銷就可以少很多。

4、還有一個是非常重要的大家可能不太注意的,就是大量建立物件。JavaScript虛擬機器有一個特點,就是物件建立的開銷遠遠大於物件計算的開銷,並且物件建立會導致垃圾回收,而垃圾回收會導致遊戲不定期卡頓,所以有一個很重要的原則就是不要在你的主迴圈裡建立任何物件,強烈建議遊戲中的人物、怪物、技能特效統統做成物件池,這樣可以大幅降低遊戲的不定期卡頓現象的出現。分享一個常用的測試函式。

來看這個函式的原理。它就是顯示了每一秒鐘去拿一個hashCount跟上一個hashCount作對比,這個hashCount是由白鷺引擎內部 API,用於統計引擎物件的建立數量。如果你的遊戲靜止放置不動,那麼理論上hashCount diff的結果應該是0,實際上要儘可能控制在120以下,我給大家分享一個數據,我見過的最賺錢的那一批遊戲的 hashCount diff 都控制在120以下的。

如果這個數字超標,應該如何去解決呢?只需要在引擎的 HashObject 的建構函式這裡新增一個斷點,在執行時去檢查呼叫堆疊就可以了。

我協助優化過一款產品,它的hashCount diff數字高達4000,每秒建立4000個物件,我除錯他的程式碼後發現,其實只是一個很小的問題導致了這個結果,花了15分鐘修復之後,遊戲的發熱、卡頓等問題都得到了大幅的緩解。

接下來我跟大家介紹一下白鷺的3D引擎的核心功能,以及內部優化技巧,也給大家做重度遊戲時以一些啟發。

Egret3D內部的所有資源都採用了GLTF檔案格式。這是一種對OpenGL ES、WebGL非常友好的3D內容格式標準。面向實時渲染,儘量提供可直接傳輸給圖形API的資料格式,而不再需要反序列化。

剛剛我提到了儘量提供可直接傳輸給圖形API的資料格式,在 Egret3D內測版本中,在3D引擎載入一個模型檔案,需要首先載入了模型檔案,然後解析模型檔案,這就像配置檔案一樣。第三步要生成WebGL所需要的資料格式,最後把它提交到GPU。而在正式版本的流程變成了載入新的 GLTF檔案,進而由於GLTF的檔案格式和GPU想要的檔案格式是幾乎一樣的,所以不需要解析也不需要生成,直接把它作一個簡單的ArraryBuffer切割,然後提交到 GPU就可以了。

通過這個優化達到什麼樣的效果?模型解析速度提升170%,記憶體佔用降低1倍,載入速度提升30%,所有這些優化的底層的本質原因是由於底層採用了GLTF的模型檔案的標準正規化。這就相當於是白鷺引擎3D版本的比較常見的引擎優化。

《泡泡學園OL》是白鷺自研團隊打造的一款標杆品質的3D微信小遊戲。在這款遊戲製作過程中,不斷挑戰 Egret3D 與微信小遊戲的效能極限極限,具體技術指標包括: 100,000 Vertex , Lightmap貼圖、GPU骨骼動畫,GPU粒子動畫,碰撞引擎,幀同步網路通訊,基於行為樹的AI 等。

這款遊戲前期開發過程中使用了 Unity3D 編輯場景,然後通過白鷺引擎的 Unity3D匯出外掛釋出到 Egret3D 中。目前已經使用白鷺引擎正在研發的 3D 編輯器進行後續開發和維護。

目前白鷺科技已經發布了 Egret3D的1.1版本,即將在9月底釋出1.2版本,這個版本重點針對開發者的開發效率進行優化,首先是推出一款視覺化的除錯工具 Egret Inspector 3D ,其次就是將3D編輯器提供給更多開發者進行試用並收集反饋,如果您已經使用 Egret3D 立項並進入專案開發階段,可以優先試用3D編輯器。

以上就是我為大家分享的全部內容。謝謝大家!

後記

早上剛轉發了這篇文章,下午就有幸和作者電話會議請教了一波,學到了不少,哈哈哈,困擾了好一會的載入策略也有了思路,感謝大神!