1. 程式人生 > 實用技巧 >CocosCreator客戶端優化系列(四):CPU佔用以及效能優化

CocosCreator客戶端優化系列(四):CPU佔用以及效能優化

轉載請保留原文連結:https://blog.csdn.net/zzx023/article/details/88991314

這篇文章是優化系列的最後一篇了,內容會長一些,更多的是提供一些方向以及思路,具體的一些細節由於篇幅有限,就不一一說明了,只要找對方向,就可以慢慢研究。希望大家可以耐著性子看完。

使用Performance定位問題


針對CPU佔用的情況,我們有很多的優化方案,但實際的專案開發過程中,更重要的是找到哪個地方是問題點。只要我們能夠快速的找到問題點,那麼解決方案也就應運而生了。
通過google開發者工具中的Preformance工具,可以擷取一段時間內,程式執行的情況進行分析。如果在某一段時間內,CPU佔用過高,導致幀率下降卡頓,那麼我們可以在結果中像上圖一樣看到存在紅色的熱點幀。
通過針對這些熱點幀的呼叫棧的分析,我們可以找到是哪一些介面的CPU佔用情況比較嚴重。在找到熱點函式後,我們就可以針對具體的函式做優化。

總之需要注意的是:
1、觀察整體效能
2、觀察分析區域性熱點幀
3、通過呼叫棧分析熱點

具體的使用方法可以參考google的文件

使用ESLint

很多時候我們的CPU佔用過高,經常是由於我們在編碼過程中對於語法的不注意,或者是疏忽導致的。包括一些記憶體洩漏的問題,閉包的處理等等。
這裡推薦一款外掛,可以幫助我們強制寫出高質量且整潔的程式碼,使得一些語法規範上面導致的問題可以被我們提前預防。

ESLint這是一款js語法規則和程式碼風格的檢查工具,類似的工具還有TSLint,JSHint這些。
使用的話只需要通過npm進行安裝

npm install -g eslint
  • 1

在安裝完成後,通過

eslint --init
  • 1

生成配置檔案,在配置檔案中可以根據專案的語法規範進行相應的配置。
具體可以參考:https://cn.eslint.org/docs/user-guide/configuring

控制遊戲幀率

分離邏輯幀以及渲染幀


參考上面mainloop的程式碼,在預設的情況下面,我們的邏輯幀和渲染幀是相同的幀率,預設都是60幀。
但實際的專案開發過程中,像一些回合制的遊戲,卡牌類遊戲,或者是一些不需要太實時的遊戲,實際上並不需要邏輯保持60幀的幀率在執行,從而造成效能的浪費,導致手機的電量消耗以及發熱嚴重。

這時我們就可以將這一段程式碼進行改造,定製一下引擎,利用fixUpdate的方式,我們可以很輕鬆的將邏輯幀以及渲染幀進行分離。比如渲染幀保持60幀,而邏輯幀按照30幀的幀率執行。

這樣邏輯幀和渲染幀就分離開來了,在執行時,邏輯幀的幀率我們可以統一設定,也可動態控制。
比如在需要建立大量物件時,我們就可以動態的提高邏輯幀的幀率,配合之前說過的async,從而加速物件的建立。
又比如在一些需要網路同步的遊戲中,我們可以通過渲染幀幀率比邏輯幀高的特性,將一些因為網路問題導致的堆積資料取出來,採用一些插值演算法,讓渲染幀進行平滑的過渡渲染。這樣就不會發生突然的瞬移同步。

控制物理系統以及碰撞系統的幀率

如果有使用到物理系統,也可以去控制物理系統的幀率。參考官方文件

預設物理系統的幀率與遊戲幀率是一致的,但有些時候我們如果只是做粗略模擬的話可能並不需要這麼高的幀率。這時我們可以通過一下的一些引數設定來控制物理系統的幀率:

對於碰撞系統來說,我們就需要使用fixUpdate的方法去定製一下引擎,修改CCCollisionManager.js中的這段程式碼。

需要注意的是,當我們降低了物理系統的幀率或者碰撞系統的幀率時,有可能會發生漏判的情況。這時由於物體的運動速度較大導致的,所以在調整幀率時,我們需要確認在遊戲的設計中,物體的最大速度是多少?需要判斷的物體最小體積是多大?再根據這些資訊去調出合適的幀率,這是個需要反覆調整的過程。

JS程式碼級優化

很多時候我們對於CPU的優化都會從演算法的方面著手,關於演算法這一塊優化方案多種多樣,很多都需要根據專案的實際情況以及遊戲的設計出發的。
我們經常會聚焦於演算法級的優化上,而忽略掉程式碼級的優化。
這裡帶來的就是程式碼級的優化

1、陣列操作
增加陣列元素時,更推薦使用:

array[array.length] = 0;
  • 1

相比較push的方法,程式碼執行效率上我們可以看下對比:

會有一定的提升,但提升的空間有限,因此不是強烈推薦,只是針對需要反覆大量執行的程式碼時,更推薦使用。比如大量的a星尋路計算時,可以在編碼時隨手注意一下

2、for迴圈
常用的for迴圈我們有幾種方式

for(let i = 0;i < arr.length; i++)
for (const key in arr)
for (const key of arr)
arr.forEach(element => {})
  • 1
  • 2
  • 3
  • 4

除了for-in這種方式外,其他三種for迴圈方式沒有什麼太大的差距

強烈不推薦使用for-in的方式進行for迴圈,效率極其低下,同時隨著遊戲不斷的執行,for-in還不能被jit優化。因此千萬不要使用for-in

3、arguments
js中如果要實現類似C++中的多型,可以通過arguments去達到。
這樣我們可以通過相同的介面,只是引數的不一致從而達到不同的邏輯執行效果

        function argumentsTest () {
            if (arguments.length === 1) {
                //......
            }
            else if (arguments.length === 2) {
                 //......
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

類似上面這樣的使用。
這種方式雖然使用上看起來很秀,但要注意的是,效能也是極其低下的,需要這種情況,不要偷懶,多寫幾行程式碼,拆分成幾個不同的函式進行呼叫會更好。
強烈推薦不要在工程中使用arguments

4、try-catch or try-finally 以及 eval
強烈建議不要使用任何的try-catch or try-finally 以及 eval,執行效率極其低下,很容易造成遊戲的卡頓。
例如try-catch or try-finally系列,如果沒有錯誤丟擲,那就還好。一旦有錯誤丟擲,效率直線下降。

5、global value
在使用全域性變數時,類似下面這樣

        gIndex = 0;
        for (let i = 0; i < n; i++) {
            gIndex += i;
        }
  • 1
  • 2
  • 3
  • 4

執行結果:
不要直接使用gIndex,使用區域性變數進行一下轉換,效率會快很多:

        var localIndex = gIndex; 
        localIndex = 0;
        for (let i = 0; i < n; i++) {
            localIndex += i;
        }
  • 1
  • 2
  • 3
  • 4
  • 5

執行結果:

以上就是一些程式碼級優化上需要注意的地方,可以看到,如果時一些熱點函式,需要大量重複執行的話,如果使用這些優化方案,通常會帶來很大的提升。這也是從另一個角度去進行CPU效能的優化