1、性能糟糕原因:幀頻低,設備發熱!PS:那么我們要知道為什么導致幀頻低和設備發熱的主要原因呢!
原因如下:
1、游戲渲染內容過多
2、渲染方式不當
3、計算量過大
4、創建大量對象沒銷毀
5、因為用的是egret,所以咋們使用不當導致底層WebGL問題忽略...(咋們按照egret的規則走就是了)
接下來就是優化的主要思路:
- 代碼的呈現方式(代碼的寫法導致計算量過大);
- 其次,排查內存泄露;
- 降低常駐場景的 drawcall ,即游戲主場景、主界面 UI ,界面窗口內容;
- 資源回收機制;
一、代碼的實現方式
- 同時生成的組件過多或者同時刷新的組件過多。(例如:游戲登陸成功后進入游戲主界面、游戲界面同時生成N個組件)
- 做UI時盡可能把所有的動態內容放在最上層,把圖片放在下層,並將這些圖片合成紋理集。
- 同時檢測的東西過多。(例如:游戲中的元寶數據更新了,N個系統同時檢測紅點等數據) 后端同一時間傳N個協議過來,前端同時處理並且渲染。(例如:同屏場景戰斗中的處理分幀處理、就是為了同時處理大量數據和渲染導致性能嚴重下降)
- 邏輯幀與渲染幀分離;這個提升是比較明顯的,因為很多游戲都是做30幀的,但是現在有些是60幀,所以要做一些邏輯幀與渲染幀分離,邏輯上可以是15幀,然后渲染上做60幀,那么邏輯的開銷就可以少很多。
- 屏幕外存在內容,並且沒有隱藏應當設置隱藏。(例如:RPG游戲中的地圖塊在屏幕外的需要隱藏。全屏界面可以把背后的內容隱藏,並且停止刷新數據,一些看不見的組件可以)
- 把不需要顯示的特效、事件、組件進行釋放銷毀。(例如:打開了一個存在特效窗口,切換界面之后開不見應該停止動畫並且隱藏、窗口關閉之后應該把特效進行釋放、界面全局事件以及組件事件應該移除掉。)
- 動態加載和卸載資源,比如在游戲內的時候,我們可以把游戲外的一些UI圖集卸載掉。
- 優化美術資源,比如合理規划圖集,約定好模型的最大三角形面數,制作合理的粒子效果規范。這個可以說是游戲優化中最重要的一個,因此,技術美術在游戲開發中作用巨大。
- 降低資源質量或屏幕分辨率,這是有損優化,一般作為最后的手段。
- 網絡數據包進行合包處理、分幀對回包進行處理、限制一定事件內的發包率等。
- 還有好多就不一一細數了,例如停止不必要的Timer,enterFrame事件,盡可能的避免使用alpha,適當時候使用cacheAsBitmap等等。
二、減少渲染內容(index.html 中 egret 的啟動參數:data-show-fps="true"看drawcall信息)
1. 優化圖集
我們項目使用 egret + eui 來開發的,有相關優化經驗的應該知道,通過打圖集的方式可以達到同一圖集圖片可以通過同一個批次繪制出來,然而 Egret 並不能像 Unity 那樣通過判斷 mesh 是否相同來實現卡節點合批。
例如:兩個圖片在同一個圖集中,但在兩個圖片中間插入了一個文本組件,這樣合批就被打斷,所以盡可能的減少合批處理的打斷。
2. 文本合批
其次,基本文本使用的字體相同,但是在 egret 中,每個文本都需要單獨占用一個繪制批次。要實現文字合批,只能通過自定義字體,使用圖片字體的方式代替原生的字體,但這樣也存在局限性,即只能針對內容變化較小的情況。
例如:一些標題和數值、文字類圖片。
3. 動靜分離
動態內容與靜態內容分離,把不需要改動的部分和需要經常動態改變或者特效部分分離開。
例如:游戲中需要點亮的筋脈系統可以分開,減少drawCall。
三、內存泄露
主要借助 Google Chrome 瀏覽器調試面板中的 Heap Profiling 工具來進行排查,實現方式是通過記錄當前的堆內存(heap)快照,並生成對象的描述文件,給出當時 JS 運行所用到的所有對象、對象占用的內存大小和引用層級關系等。
在調試面板的 Memory 頁簽便是 Heap Profiling 工具,下面是具體的使用操作:
- 如何打快照:選中左側菜單中的 Profiles 選項,在 Select profiling type 中選擇第一項 Take heap snapshot ,點擊 Take snapshot 點擊即可開始打快照。
需要注意的是,假如在控制台中打印對象的話,也會把對象引用住,所以打快照前最好也清空一下控制台中的打印日志。此外,每次打快照之前都需要點擊一下 Collect garbage
按鈕,保證能被回收的資源都被回收了,這樣后面對比快照查詢泄露才准確。
- 對比快照:
- 當打出首個快照之后,我們會進行一些游戲內的操作,例如:打開一個界面或切換一下場景,然后再關閉界面或切回原來的場景。一番操作之后,點擊 Collect garbage 按鈕然后打出第二個快照。現在在 HEAP SNAPSHOT 下便有兩個快照:
- 接下來是對比快照的步驟:
- 選中快照列表中需要進行對比的一個快照;
- 右側操作類型選擇 Comparison (對比);
- 再選擇要進行對比的另一個快照。
定位泄露原因
在對比結果展示表中有幾項數據:
- #New :
- #Deleted :
- #Delat :
- #Alloc. Size :
- #Freed Size :
- #Size Delta :
對比技巧:
- 可以直接在對比數據上方的 Class filter 中輸入打開過的 UI 的類名;
- 假如存在,則表示 UI 關閉后仍被引用住沒有被回收,展開數據可以看到引用鏈;
引起泄露的原因通常是對象被全局的變量引用住導致 gc 時沒辦法自動釋放掉,排查引用鏈時其實可以定位到具體引用住此UI 的對象和引用的代碼所在的位置,下面是分析引用鏈的操作:
- 先選中對象並展開對象的引用鏈,在 Retainers/Object 中第一個對象通常就是引用此 UI 的對象:
不難看出此 UI 是被 gEventCenter 這個對象引用住了。
接下來排查引用代碼的位置,可以將鼠標直接懸停在引用對象上,然后展開對象的 fn 屬性:
直接點擊 [[FunctionLocation]] 后面的腳本信息,即可直接打開引用對象的代碼所在的位置。
小結:
在 JavaScript 中提及的內存泄露,其實並非正真意義上的泄露,通常只是對象被引用住無法無法被回收而導致內存累積。這里需要注意的就是全局引用住的對象,除非是要重復使用,否則會導致對象無法被內存自動回收,從而導致占用內存一直累加。
四、資源回收
對於游戲中不同的資源進行記錄使用和管理。例如區分開什么資源需要即使釋放、什么資源需要做進行定時管理、使用引用次數管理等等。
當然,主動調用資源回收能降低內存占用,但回收太過頻繁反而會導致發燙更嚴重。
五、Egret性能優化之優化渲染
1、Egret在內核中是如何來處理渲染部分的?
MainLoop ——> EnterFrame(幀事件) ——> clear ——> stageUpdateTransForm ——> stageDraw
Egret每刷新一幀的時候,會執行四步操作。
- 執行一次EnterFrame,此時,引擎會執行游戲中的邏輯。並且拋出EnterFrame事件。如果在這里編寫了大量消耗性能的代碼,那么游戲的幀頻就會開始下降。
- 引擎會執行一個clear,將上一幀的畫面全部擦除。
- Egret內核會遍歷游戲中所有的DisplayObject,並重新計算所有顯示對象的transform——visible = false的顯示對象也會參加計算。
- 將所有的圖像全部draw到畫布中。
2、了解了egret的渲染機制,優化游戲:
- 不想要的DisplayObject,請removeChild掉,如果是設置了它的visible屬性的false,確實這個顯示對象不會被渲染出來,但是,它還是會參與到第三步的計算過程。所以也無形中增加了性能的開銷。如果某一個圖像被其他圖像遮蔽,那么你就需要移除被遮蓋的對象。
- 太多的顯示對象不僅會在第三步會消耗性能,更重要的是,在第四步的時候,會嚴重影響性能,讓幀頻下降。
- 可以將畫面中的元素進行合並,合並不是將兩個Bitmap塞到一個Sprite中,這樣並不起作用,無論是嵌套好事並列,都會消耗大量性能。如果可以,最好調整游戲元素圖片的拆分方式,盡量減少DisplayObject數量。
- 使用cacheAsBitmap,讓你的矢量圖在運行時以位圖形式進行計算。這回大大減少你的矢量圖運算。
- 盡量不要在EnterFrame事件中做過多的操作,EnterFrame事件派發太頻繁了。
- 善用臟矩形是一種非常高效的優化手段,但它是把雙刃劍。用的好,性能飆升,用不好,自取滅亡。