技術乾貨丨《大天使之劍H5》主程與項目總監:H5遊戲的壓縮與優化經驗

2018年3月,三七互娛在其主辦的中國國際互動娛樂大會上稱,《大天使之劍H5》最高單日流水超4000萬元,而單月最高流水超過了1.8億元。

 

上週末,在極光網絡與三七互娛聯合主辦的極光會客廳——“2D小遊戲開發實戰技術沙龍”上,《大天使之劍H5》的主程陳策與網絡項目總監陳源分享了“大型H5遊戲如何登陸微信小遊戲”以及“遊戲性能優化”的研發經驗。

 

 

以下是兩位嘉賓的演講整理:

 

陳策:大型H5遊戲如何登陸微信小遊戲

 

隨着公司業務的發展,我們的項目往往要登陸各種平臺,微信小遊戲就是這些平臺的其中之一。

 

微信小遊戲是微信小程序的一個類目,它即點即玩,無需下載安裝,體驗輕便,可以和微信內的好友一起玩,比如PK,圍觀等。

 

但想讓自己的遊戲登陸微信小遊戲,會有一些方面的限制,下面我們主要說下《大天使之劍H5》這一項目登陸微信小遊戲受到的主要限制和解決辦法。

 

一、《大天使之劍H5》受到微信小遊戲的主要限制

 

1.所有分包大小不得超過8M:分包指的是在微信開發工具裏上傳的所有資源,包括JS代碼和資源,一共不得大於8M;

 

2.單個包的大小不得大於4M:上傳的文件裏,不能有大於4M的文件;

 

3.JS必須放在分包裏纔可以運行,加載進來的JS文件只會被當成文本:加載進的JS文本,無法轉成可執行腳本。

 

《大天使之劍H5》在登陸微信小遊戲前,整個項目大小約有400多M,光JS代碼部分就有大約10M。除邏輯代碼的其它資源(圖片、音效、配置等),可以在遊戲運行時進行加載,不用在開發工具裏上傳,但約10M的JS代碼部分必須全部上傳。因此,《大天使之劍H5》想登陸微信小遊戲,必須縮小JS代碼的大小。

 

二、現有壓縮工具UglifyJS部分功能簡介

 

Layabox引擎裏將項目的AS3部分生成JS時會進行一定的優化,這個功能應該是基於UglifyJS來實現的。其優化內容主要有:

 

1.去掉代碼中無效的空白字符

 

2.去掉代碼中的註釋

 

如圖所示:

 

 

3.把方法中局部變量名縮短

 

如圖所示:

 

 

4.代碼格式優化 (把代碼改爲更省字符的寫法)

 

如圖所示:

 

 

5.壓縮屬性名 (默認不開啓)

 

如圖所示:

 

 

我們先來看看這個例子,這個是一個類,在工具默認不開啓壓縮屬性名稱時,工具就只會壓例子裏橙色的兩處X和Y,因爲這個是參數,也就是剛纔說的方法裏定義的變量,this.x,this.y這都是不壓的,因爲這個是屬性名。如果類名Point,方法名setTo,屬性名X,Y壓了,那其他使用的地方就要根着一起改,如果代碼裏有用到反射來調用的,那就調用不到了。所以壓縮這些名稱是有風險的,這也就是工具默認不壓的原因。那這個功能不就廢了?不會,工具還提供了很多參數讓你可以設置不壓縮的名稱的列表,還允許你定義壓縮的名稱的正則表達式等等,其實還是可以使用的,只是還是要先整出一份針對自己項目的名稱數據出來,整理出來的不壓縮名稱集要和代碼同步進行維護,這樣難度會比較大,所以《大天使之劍H5》項目並沒有使用這個功能。

 

小結:

 

《大天使之劍H5》項目現有的AS3代碼代碼在Layabox生成JS代碼時,已經默認進行了上述前四點優化:

 

· 去掉代碼中的無效的空白字符

· 去掉代碼中的註釋

· 方法中的局部變量名縮短

· 代碼格式優化

 

但生成的JS代碼有10M左右,還沒有達到微信小遊戲的要求,因此,爲了縮小代碼量,我們需要對我們的AS3代碼再做一些優化,從而減少代碼量。

三、減少程序代碼

 

縮小代碼量,最直接的方式就是減少代碼裏的字符,這部分所作的優化,是在我們項目的AS3代碼部分所做的優化,這些優化包括以下幾點:

 

1.將界面佈局的數據改爲從外部加載

 

Layabox的UI編輯器編輯後,會生成對應的UI類文件,其內容如圖:

 

 

這裏面主要內容是UI的佈局數據,不用涉及到邏輯,可提取出來。做爲文本文件保存,在其對應的界面初始化時再加載,在Layabox中,我們可以通過修改UI模式來做調整:

 

 

我們可以隨意創建一個UI來做測試,如圖所示:

 

 

我們可以看到,新建一個TestPageUI界面,使用內嵌模式生成的TestPageUI.as文件共有3283字節,而使用分離模式,生成的文件只有579字節。圖中右邊綠色部分表示的是減掉的部分代碼,爲我們縮小約80%左右的UI佈局相關代碼。

 

 

而在《大天使之劍H5》中,UI佈局文件目前有931個,使用這種方式幫我們減少了1.8M的代碼。

 

2.將類裏不使用的導入刪除

 

我們在開發時,手誤import進入的一些項目並未使用到的類,需要將這些import刪除。如:import Sprite3D 類,2D遊戲用不上3D相關的東西,無需導入。

 

3.將方法裏的this用局部變量代替

 

當我們的AS3代碼轉成JS後,類中的屬性名在方法中的訪問形式,是會在其前面加上this.,這裏的this我們是否能減少呢?如下圖所示:

 

 

上面的方法裏有若干個this。如果把this用一個局部分變量來代替,那就是下面的方法這樣。這裏的局部變量用的是一個字符的變量,因爲最後我們項目會用UglifyJS來壓,所有方法內定義的變量,只要不超過54個,都會是單字符的變量。我們看優化前,一共是有四個this,他們佔用16個字符。優化後,四個this變成了四個n,是4個字符,還多出一個賦值語句,這個語句包括中間的空格,包手後邊的分號,一共是11個字符,加上四個n就是15個字符,比優化前少了一個字符。如果這個方法裏我再加一個this,那優化前的代碼,就要增加4個字符,而優化後的代碼只需要增加1個字符,所以方法裏的this越多,能減少的大小也越多。因此:

 

 

總結來說就是隻要方法裏的this關鍵字多於3個,就能省字符數量。而且this越多,省得也就越多。我在編譯好的JS代碼裏搜索,一共是有近18號個this,這個就可以省很多了。

 

但這個優化要注意,每個function都是一個作用域,每個作用域裏的this指代都是不一樣的,所以每個不同的作用域裏的this要分別進行計算,也就是說,方法裏如果有一個函數,那在計算方法裏的this數量時,不應該計算函數裏出現的this。第二個,是有一些方法,已經寫了內部的變量賦值是this的,那就可以利用這個已經存在的變量,可以進一步減少字符。這個優化最終省了0.3M。這個優化優化的不僅僅是代碼的大小,因爲在JS裏,局部變量的調用效率是比this要高的,所以這還可以加快遊戲的運行效率。

 

4.壓縮包名、類名、方法名、屬性名

 

這一步做的事,是把UglifyJS默認不壓的名稱,我們用我們的方式把他給壓了:

 

① 相同的名稱壓爲相同的短名稱,把代碼裏出現的相同的名稱,壓爲相同的短名稱

 

② obj.abc 與 obj["abc"] 區別處理:

 

默認壓縮規則:obj.abc 寫法的屬性名會被壓縮,obj[“abc”] 寫法的字符串部分不會被壓縮。

 

想到這種方式,主要是因爲UglifyJS也考慮到有些屬性名壓縮後,可能會引起某些屬性訪問不到,UglifyJS的做法是提供個不壓的屬性名的配置列表,但是這僅僅是個配置列表,我們通過這個列表無法定位到代碼裏有用到這些屬性名的地方,有一定的侷限性,因此,通過obj.abc 與 obj["abc"] 區別處理,我們可以在寫代碼的時候就用不同的寫法告訴編譯器,這裏的屬性名是否要壓。

 

有人會有疑問,用obj[“abc”]的寫法,會比obj.abc的寫法多了三個字符。不用擔心,因爲在最後用UglifyJS壓縮的時候,會將[]語法轉成.語法的。

 

③ 自定義標籤 /*[ZIP-JSON]*/

 

爲了不破壞程序員的編程習慣,我們在不得不用字符串的形式去

訪問屬性時,想到了下面的解決方案:在字符串前加上一個/*[ZIP-JSON]*/

如:

 

 

我們常用的緩動類的用法中,上圖的”x”和”y”是屬性名,我們默認情況下字符串是不會被壓縮的。此時我們可以在代碼中加上/*[ZIP-JSON]*/標籤,如:

 

 

這樣,”x””y”就會被壓縮成對應的名字了。

 

通過這些處理,我們的代碼的寫法就會有以下幾種形式:

 

 

形式1:label 這個屬性名,並不會被壓縮,訪問時也用它原名訪問

 

形式2:label這個屬性名在定義時就被壓縮了,可以通過也會被壓縮的.語法去訪問

 

形式3:label這個屬性名在定義和訪問時都有被壓縮

 

當然,/*[ZIP-JSON]*/做爲註釋塊,在最後AS3被轉成JS時,UglifyJS會幫我們把註釋塊給清除掉的,不用擔心加了註釋塊反而代碼會大的問題。

 

④ 特別處理

 

諸如 hasOwnProperty、propertyIsEnumerable 等方法,以及Layabox 裏的 __JS__ 方法。

 

方法裏傳入的字符串,其實是屬性名稱。因爲默認屬性名稱是會被壓縮的,而字符串是不會被壓縮的,所以對這些方法中名字,我們默認進行壓縮。但要壓縮成什麼樣的名字呢?

 

 

上面我們講的,是哪些名稱要壓,壓的時候要注意的一些點,那最終這些名稱,要壓成怎麼樣呢?當然是壓到越小越好,那最小是多少呢?一個字符是最好的。我們先看看要做名稱,受哪些限制。名稱是可以由字母組成的,字母是區分大小寫的,還可以使用數字,還有下劃線,還有一個比較不常用的$符號,要注意的是,名稱的首字符不能是數字。

 

如果我們把名稱全用單個字符,可以有多少個名稱呢?26個小寫字母,26個大寫字母,10個數字不有用,加兩個符號,就是54個。那雙字符的名稱呢,就有3456個,三個字符就是22萬個。當然這裏能用的還會少幾個,爲什麼呢?因爲比如像as,is,if,for這樣的名稱,也是兩個字符三個字符,但他們是關鍵字,名稱不能和關鍵字重名,不過這樣的關鍵字也不多,不多於10個。三個字符可以有22萬個名稱,那是否夠我們使用了呢?

 

 

上圖是《大天使之劍H5》中所用到名稱字數的分佈圖,一共有4萬個名稱,那兩個字符的3千多個肯定是不夠的,三個字符的22萬個就完全可以滿足了。而且我們看看這些名稱的長度分別是多少,可以從表裏看到,95%以上的名稱是大於三個字符的,那可以優化的空間就比較大了。最終我們項目把名稱都壓縮完後,一共減少了1.9M。在壓縮名稱這裏,大部分工作都是用編輯工具去完成的,有一部分是要修改源代碼的,也寫了一個工具去處理,儘量做到用工具去完成,不然要手動去修改,工作量會變得超大。

 

上述的五點對《大天使之劍H5》優化過後,結果如圖所示:

 

 

《大天使之劍H5》的代碼由約10M減小到約5.1M的大小

 

⑤ 一些還未在《大天使之劍H5》中做的可取優化

 

· 靜態常量編譯爲JS後是把值寫在使用的地方,這不一定是最優的

· 方法裏使用的屬性賦值給局部變量再使用

· 使用(param)=>{}代替function(param){}

· 某類只有一個子類時可減少繼承鏈

· 包結構可以減化

 

四.使用分包

 

在上述的優化後,《大天使之劍H5》的主代碼還有5.1M,任然需要對這5.1M進行拆分,這5.1M中,有遊戲引擎的部分佔了0.7M,其他小文件佔了0.2M,剩餘的主程序還有4.2M,剩餘的4.2M可以通過分包處理。

 

怎樣分包我們可以在騰訊和layabox的官網上找到詳細的教程,下面是相關鏈接

騰訊官網關於分包加載的說明:

 

https://developers.weixin.qq.com/minigame/dev/tutorial/base/subpackages.html

 

Layabox官網關於微信小遊戲分包的示例:

 

https://ldc.layabox.com/doc/?nav=zh-js-5-0-6

 

關於在layabox下是如何分包的,在這裏簡單說一下:

 

 

在項目的根目錄下,創建一個module.def文件,這是一個文本文件,裏邊的內容如下,就可以在編譯後,生成主文件的JS和模塊.js兩個文件。如果要分爲多個模塊的,就把這個結構寫多個,都定義好模塊名稱和模塊對應的代碼所在的文件夾就可以了。

 

看起來是不是很簡單?但我們隨意的指定一個文件夾下的代碼被編譯爲一個模塊獨立出去後,在運行時,就會出錯上圖紅色部分的一個報錯。

 

出現這個報錯的原因是主文件會先運行,主文件裏引用了模塊裏的XXX,而運行到這裏的時候,模塊還沒有被加載,所以xxx沒有被定義,所以報錯了。

 

所以,要做好分模塊前,就需要對項目進行解偶。要解偶的話,那就得知道,我們分到模塊裏的是什麼功能,這個功能裏如果需要和主程序進行交互,就需要設計相應的中轉機制來進行解偶。

 

如果項目是新項目,我們可以在一開始設計遊戲的時候就做好這部分內容,在功能進行開發中,會知道這個功能是要分出去的模塊,要以怎麼樣的開發規則進行開發,就可以做到解偶進而做到分模塊。

 

但我們的遊戲已經上線快一年了,如果現在才加入這樣的機制相當於我們要對需要放到模塊裏的功能進行重構,這樣做工作量大,而且功能還要重新測試,開發週期開,還容易出BUG。後來我想了一個不需要解偶也可能分模塊的辦法。

 

 

我在說我們辦法前,我要說明一點,我這個辦法只是爲了解決在小遊戲裏做到分包小於4M而做的,與分模塊的設計思路是不太一樣的。分模塊的目的是什麼呢?是把還沒有使用到的功能放到模塊中去,需要使用到的時候,再去加載對應的模塊。而我的做法,是需要在進遊戲前,需要把所有模塊都加載進來,無論模塊的功能是否需要,也不管模塊裏到底是什麼功能。

 

爲了說清楚這點,我們先來看看JS的類。JS的類定義在書寫的時候,是否有先後順序?看看這段代碼,這裏定義了一個父類,然後再定義了一個子類。這裏我們是否能先寫定義一個子類,再寫定義一個父類嗎?大家注意下子類的定義裏,是需要將父類的定義傳入的,如果先寫子類的定義,那傳入的父類定義就是一個undefined,裏邊在調到到父類定義裏的屬性時,就會報錯。

 

所以父類必須要寫在子類前邊。換成分模塊的情況下是怎麼樣呢?假設我們現在有兩個文件,先被加載的叫模塊A,後被加載的叫模塊B。模塊A裏有一個子類的定義,在模塊B裏有其他類的定義,也包括這個模塊A裏的子類的父類的定義。在模塊A被加載完成後,運行到子類的定義時,就調用到了他的父類,因爲模塊B還未加載,所以必然報錯了。這裏我們要怎麼避免報錯呢?很簡單,把父類的定義,也放到模塊A裏,那就不會報錯了。如果父類還有父類,而且也在模塊B裏的,那記得也要把他的父類也拿到模塊A裏。

 

具體我們是怎麼操作把父類也放到模塊A裏的呢?我們只需要在調用Laya的編譯器前,把父類的as文件考到模塊A的文件夾裏就可以了。父類裏的包名什麼的,都不需要做修改。要知道包名在AS裏雖然是和文件存放的路徑相匹配的,但在用laya編譯時,是不檢測包名是否和路徑匹配的,最終生成到JS裏的,是文件裏寫的包名,路徑只做爲是放到哪個模塊的依據。

 

 

剛纔我們講的是父類是在另一個模塊的情況下引起的報錯。除了這個,還有沒有其他情況呢?有的,比如說我們在剛纔的模塊A裏的類,在未解偶的邏輯裏,是肯定有調用到模塊B的類。不過在初始化時,應該不會運行到業務邏輯裏,那爲什麼會報錯呢?我們來看看模塊A裏的代碼。模塊A裏的頭幾行一般是長這個樣子的,第二行,是將Laya引擎裏的一些公共方法定義了短名稱的變量,方便在邏輯裏調用。

 

第三行開始,就是把這個模塊裏引用到的類,都用類的名稱做變量名賦值,這樣就方便在使用的時候,不需要寫包括包名的類名稱。也就是我們直接寫在AS裏的代碼,不用做太多修改就可以在變成可運行的JS。要注意到,這幾行代碼,是在這個JS文件初始化的時候就會被運行的。注意看第四行,我們有一個類,假設這個類叫ClassName,這個類是定義在模塊B裏的,那這句賦值語句就會因爲模塊B還未加載而找不到ClassName的定義,然後報錯。而且這個類之所以出現在這裏,就是因爲在該模塊的某個類裏使用了它。

 

這裏我們就明白了,寫在類的方法裏的代碼,在初始化的時候是不會被運行的,所以寫了模塊B裏定義的類也不會在初始化時報錯,被導入的類會被寫到模塊的最開頭,會在初始化時運行到就會報錯。那我們這麼處理,所有模塊A裏的類,如果import的類是模塊B的類,那就把這個import刪除掉。並且把所有使用這個類的地方,都寫成用這個函數調用的字符串的包括包名的類名。

 

 

好像這樣改,需要改的地方會比較多,而且生成的代碼裏,也會有多處長名稱,我改成了這樣,在類里加一個靜態的變量,讓他等於這個函數,那代碼裏就不用修改,使用到這個類名的地方,其實調用的是這個定義的靜態變量。而且編譯爲JS後,靜態變量的定義會變成get函數來得到這個值也就是在使用的地方纔會調用,而不是初始化的時候。這樣就解決了模塊A的代碼裏調用到模塊B的類的引起在初始化的時候報錯的問題。

 

 

做好剛纔的兩個地方就完成了嗎?我們再回想一下兩個情況,都是模塊A裏的類,如果引用了模塊B裏的類,那就想辦法把他的引用去掉,讓他在首次運行時才調用。也就是說,在編譯爲JS的時間,模塊A裏的類是被當成沒有引用模塊B裏的那個類了,那如果模塊B裏的那個類,假設叫SimgleClass,只有唯一的一個引用就是模塊A裏的類引用了,現在把模塊A裏的引用去掉了,那SimgleClass就沒有類引用到它了,也就是編譯的時候,會把這個類不編譯到JS裏去。那運行的時候就會因爲找不到定義而報錯。所以要在SimageClass里加上強制編譯的標籤,這個是由LayaBox提供的標籤,當有這個標籤時,這個類就算沒有引用,也會被編譯到JS裏去。

 

最終的結果,如圖所示:

 

 

這4.2M的主程序文件,就被拆分爲了一個1.2M和一個3M,小的那個和引擎代碼還有其他一堆小文件一起打包成一個包,共2.1M,3M的那個文件就一個包。在程序運行的時候,會在進入遊戲的時候,先加載2.1M的包,完成後會立即加載3M的包。兩個包都加載完成後,纔會進入遊戲。

 

陳源:遊戲性能優化

 

遊戲性能問題,往往是我們遊戲程序員最關心的問題,對於這個問題,我在這裏總結一下我關於遊戲性能優化的八個理念:

理念一:善於從問題的表象上出發進行優化

 

遊戲出現問題時,最直接的表現就是卡,造成卡頓的問題又有很多不同的情況。在解決卡頓問題前,我們應該最先排除是否是外部問題造成的卡頓,外部問題:網絡差,硬件差,系統問題等。排除是外部問題導致的卡後,我們可以根據卡頓的現象來定位問題。

 

1.輕微的較頻繁卡頓

 

1)幀率

 

根據遊戲自身來設定幀率範圍,一般遊戲建議30幀就夠了

 

2)Drawcall

 

在瞭解什麼是drawcall後,我們知道,過高的drawcall會導致卡頓,這裏就介紹一些減少drawcall的方法:

 

A.查看drawcall

 

在layabox中,添加代碼DebugTool.showStatu = true;

 

B.連續渲染相同圖集裏的圖,只會執行一次drawcall

 

C.UI上drawcall優化

 

優化的思路就是儘可能讓相同圖集裏的圖一次性連續渲染完,舉例來說:在layabox中打開一個UI,層級窗體裏看到界面裏的子對象,如下圖:

 

 

我們可以看到看每一個子對象前邊,都有一個小圓點,這個圓點的顏色代表了他來自哪個圖集,相同顏色的圓點代表是同一個圖集。渲染UI時,UI上的每個子對象是按照自上而下的順序去渲染的。在不影響界面的情況下,把界面裏各元件的層級調整一下,可以達到減少drawcall的目的。調整後,如圖所示:

 

 

優化前:會有6次drawcall

 

優化後:只有3次drawcall

 

D.場景上的drawcall優化

 

拿場景中的人物來舉例:

 

假設一個人物的某套裝資源在一個圖集裏。

 

 

那麼場景裏連續渲染穿這個套裝的人物,只用1次drawcall。實際上在遊戲裏,穿着相同套裝的人不會理想的按順序出現。一個場景裏會有許多穿着不同套裝的人物,而每個套裝在一個圖集。也就是說,場景裏,渲染N個這樣的人物就需要無限接近於N次drawcall。

 

當每個人物還有影子。

 

 

影子的資源,肯定是在不同於各個套裝資源的圖集裏。

 

情況1:人物和影子是在同一個容器裏處理,每一個容器就是一個人物,這種情況就會出現,渲染單位A,會使用兩個圖集套裝圖集+影子圖集有兩次drawcall。當渲染N個人物,就有N*2次drawcall

 

情況2:把渲染影子拆出來,當渲染完所有人物後,再根據所有人物的位置,繪製影子。這種情況,渲染N個人物,只會有N+1次drawcall顯然情況2的處理方式更優。

 

當每個人物還有名字、稱號、血條等等元素,若這些元素是按照上面情況1的處理,那drawcall就有N*M次。把這些元素按照上面情況2的處理,顯然可以減少大量的drawcall

 

E.其他減少drawcall的方法

 

如:減少被遮擋的單位渲染

圖集的控制

 

3)有較高消耗運算,特別是enterframe和for循環裏面的

 

注意代碼邏輯優化,減少算法複雜度

 

4)資源加載緩存有問題/資源太零散,造成I/O過高

 

優化資源加載策略

 

5)日誌打印

 

減少日誌打印

 

6)限制底層繪製分辨率

 

2.突然大卡頓

 

· 某些不定時的操作,循環裏複雜度高

可能是執行到一些複雜度高的循環處,導致突然大卡頓

 

· 協議包過大

 

注意協議包優化,優化方式諸如:

 

① 刪除不用字段

② 整合小字段

 

如某協議中的字段

 

var a:int;

var b:int;

var c:int;

 

假如上述a,b,c的值只會是十位數以下時,可以整合成一個字段:

 

var d:int;

 

d的每一位爲ABCDEF

 

AB爲a的值,CD爲b的值,EF爲c的值

 

比如d = 123456

 

那麼a爲12,b爲34,c爲56

 

這樣做的好處,原來需要12個字節的數據,現在只用4個字節就可以了。

 

③ 字段類型選擇

 

如用int還是short

 

只有兩個值的情況選擇用boolean

 

④ 儘量避免使用字符串

 

協議中的字符串,儘量通過ID發送

 

ID對應的字符串在代碼裏做好映射或配置

 

· 垃圾回收負荷過重

單位是否是頻繁初始化,用完後就銷燬?

是否需要使用對象池

 

如果卡頓發生UI上:

 

· 面板內容初始化分幀處理

· 圖片和特效尺寸,圖集依賴規劃是否合理

· UI面板是否分頁籤

· UI內特效,3D模型等次要元素延遲初始化

· List優化,動態初始化,循環使用

 

3.持續地越來越嚴重卡頓

 

· 是否有對象/物件用完了隱藏掉,而沒有被刪除,越積越多

如:飄血、事件監聽等

· 內存泄漏,GC越來越頻繁

 

4.卡頓到閃退

 

· 內存過高,內存有泄漏

· 檢測是否有報錯或死循環等問題

 

理念二:內存爲主

 

很多能感知的較大卡頓都是垃圾回收引起的;

 

內存問題往往比CPU問題更難解決,甚至需要大規模重構;

 

降低內存會使得遊戲更加輕快,可以緩解其他較小問題。

 

理念三:內存使用策略比內存釋放策略更重要

 

關於內存使用的策略,往往在項目的初期就應該制定好,下面有幾點建議:

 

· new對象必須由Factory或者Manager進行統一管理,這點做好了,對後期排查內存問題尤爲重要;

 

· View和Model要完全分離;

· View的創建細化到每幀,根據性能情況創建;

· FpsManager按照幀率動態調整閥值;

· 合理地運用對象池:“頻繁創建和銷燬”的“小型”對象採用對象池。

 

理念四:重視資源壓縮

 

遊戲程序中,大量的內存來自於資源,合理的壓縮資源是減小內存行之有效的辦法。關於資源壓縮,這裏提一些建議:

 

· 重視制定製作流程,技術標準

資源的製作不能隨心所欲,必須擁有一定的標準,例如:

 

① 原始圖片資源使用jpg還是png

② 是否是可以用鏡像處理的圖片資源

③ 相似的資源是否可以統一

④ 避免程序上使用濾鏡和灰化等

⑤ 一些圖片資源上不起眼的光效或羽化效果等是否可以不用

⑥ 美術字體圖片中,相同文字的拆分與組合

⑦ 九宮格拉伸圖片的概念

 

· 合理優化動作、特效等資源序列幀

許多人物動作、特效等資源,美術給出的效果十分精細。在處理內存問題上,可以考慮對這些資源做如下處理:

 

① 抽幀

一些影響不大的關鍵幀是否可以刪除

 

② 縮小再放大

一些不起眼的部位,序列幀可以按比例縮小,程序使用時,再放大

 

③ 砍方向

人物向左和向右的資源是否可以通過旋轉其中一個動作實現?

 

是否所有方向的資源都要用到?

 

· 對稱的資源砍半/四分之一鏡像使用

· 序列幀特效用IDE製作替代

· UI全屏分辨率選擇 1024像素

· icon儘量使用 64*64 的格式

· 使用pvrtc etc ,png8格式

· UI背景使用最接近的某個2的次方的尺寸

· 背景圖不要打到圖集中,並且儘量用jpg

· 少用遮罩

· 音效使用單聲道

 

理念五:屏蔽策略

 

在遊戲出現性能瓶頸的時候,我們不得不考慮屏蔽一些遊戲內容的顯示,比如一些次要的場景單位、特效等。屏蔽必然會減弱玩家的遊戲體驗,但是,比起卡頓甚至是閃退,適當的屏蔽規則是必要的。

 

·加入屏蔽設置,性能差的時候自動開啓

·屏蔽策略要覆蓋各類顯示對象

·某些場景使用統一模型

 

理念六:不要忽略看起來小的地方

 

正所謂積少成多,一些看起來小的地方,卻用了不太合理的處理方式,往往也影響着遊戲的性能,在《大天使之劍H5》中,我們就對如下這些地方做了些優化:

 

· 配置表數據優化

· String數組方案

· 相似String的處理

· 版本號文件,採用樹形存儲減少URL字段的重複度

· 數值類型廣泛使用變長

· 解析戰報,分段解析

 

理念七:深入學習開發者工具的使用

 

在調試遊戲時,我們往往要依賴開發者工具來獲悉遊戲的內存變化、CPU的使用、遊戲內對象的整體情況、資源的應用情況、甚至是我們關注的變量值等等。善於使用各種開發者工具能讓我們事半功倍。

 

· 使用谷歌瀏覽器的開發者工具

 

layabox開發的H5遊戲默認是用谷歌瀏覽器調試的,這裏我們稍微講下谷歌瀏覽器的開發者工具的運用。遊戲運行在谷歌瀏覽器時,按F12可以打開開發者工具,他的一些常用功能有:

 

① console的使用

 

 

Console的界面如上圖所示,它主要顯示着我們遊戲運行時的日誌等信息。每條日誌的後面,有鏈接可以快速跳轉到輸出日誌的代碼處,在console的下方,有輸入框功能,我們可以通過這裏輸入代碼改變當前遊戲內的數值、用不同方式打印日誌等。

 

② 調試

 

我們可以在工具的Sources選項卡下找到我們要調試的JS,進行斷點或條件斷點調試。

 

③ NetWork

 

NetWork面板提供了有關已經下載和加載過的資源的詳細信息

 

在這裏我們可以很直觀的找到一些加載耗時較長的資源進行優化

 

④ TimeLine

 

TimeLine界面中,我們可以看到關於時間開銷的完整概述。

 

 

如圖所示,通常我們在遊戲性能表現差的情況下,在TimeLine界面點擊左上角的錄製按鈕,錄製一段時間後,點擊完成,它會幫我們蒐集到在錄製的這段時間裏,遊戲每一幀的運行狀況。

 

· 綠色的幀是在幀時間內處理完需求處理的事情的

 

· 紅色的幀是處理時間大於幀應該有的時間的,也就是卡了的幀

 

我們可以重點看下紅的幀,在下邊可以看到是哪個方法佔了多少時間,以及這個文件裏又是調用哪些方法,分別調多少時間

 

 

在圖中左下部分,我們還可以看到代碼邏輯和渲染的比重,可以用來判斷可以做什麼優化,怎麼優化。這個功能,對程序的參考決不止我提到這些,這裏有很多信息幫我們分析找到問題,可以對我們優化提供很多幫助。

 

⑤ Profiles

 

Profiles界面中,我們可以使用快照功能。最常用的還是Take Heap Snapshot功能。它可以記錄當前內存分佈的詳細信息,多張內存快照還可進行比對,分析在不同的時間點,內存具體有哪些變化。

 

理念八:抓大放小,先抗住再優化

 

沒有一個性能問題是由單一問題造成的;

 

單次單點修改,注意記錄便於前後對比找到關鍵問題;

 

調試崩潰的時候一定要注重日誌,一行一行看總會找到Keyword。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章