#<center>遊戲性能優化</center> ##一、遊戲性能——白鷺: ###1、性能糟糕原因:
1)幀頻很低(即FPS低,正常60),量化爲:在特定設備上的幀頻是XX幀,其中JavaScript邏輯開銷XX毫秒,渲染開銷YY毫秒。
2)設備發熱,量化方法:首先將設備充滿電,然後統計遊戲在XX分鐘後的剩餘電量。由於耗電量和發熱基本成正比,所以解決耗電問題,發熱問題也同步得到解決。
3)不定期卡頓:一定要記錄卡頓是否存在規律,比如①是播放動畫的瞬間,②打開UI面板的瞬間,③毫無規律
###2、幀頻低和發熱主要有如下原因: 1)渲染內容過多
2)渲染方式不當
3)計算開銷過大
4)大量創建對象
渲染內容和渲染方式不當最終影響是可以在引擎渲染層解決——理解WebGL底層的渲染原理
計算開銷過大和大量創建對象都是在用戶邏輯的JavaScript層去解決——瞭解JavaScript底層的一些原理
對應的優化: 1、渲染內容過多:
在屏幕之外的內容,可以設置爲隱藏,不要渲染。如:①打開UI彈窗時,可以吧遊戲背景隱藏,同樣可以節省大量的渲染開銷。
2、渲染方式不當:
底層原理:
統計遊戲所有顯示對———一次性提交所有的“形狀數據”———設置渲染模式———執行渲染批次———設置渲染模式———執行渲染批次———設置渲染模式———執行渲染批次 2D是一次性提交所有的數據,然後設置渲染模式,執行渲染批次,在設計渲染模式,在執行渲染批次。如果你能保證渲染模式這東西是沒有發生變化的,就可以一次儘可能多地渲染。 如: 重度遊戲的典型UI,DrawCall是30,這種遊戲可以做很多優化,就是把所有的圖片、文字合成一張紋理集。不要將動態變化的內容合拼。
做UI時儘可能把所有的動態內容放在最上層,把圖片放在下層,並將這些圖片合成紋理集。
3、計算開銷過大優化:
1)對骨骼動畫使用緩存,優化骨骼開銷
2)避免大量的數學計算與浮點數計算
3)邏輯幀與渲染幀分離;這個提升是比較明顯的,因爲很多遊戲都是做30幀的,但是現在有些是60幀,所以要做一些邏輯幀與渲染幀分離,邏輯上可以是15幀,然後渲染上做60幀,那麼邏輯的開銷就可以少很多。
邏輯幀與渲染幀分離:
4、大量創建對象:
1)JavaScript虛擬機有一個特,就是對象創建的開銷遠遠大於對象計算的開銷
2)大量的創建對象會導致更頻繁的GC(GC:垃圾回收),而GC會導致遊戲不定期卡頓
3)不要在主循環中創建任何對象,強烈建議遊戲中的人物、怪物、節能特效統統做成對象池,這樣可以大幅降低遊戲的不定期卡頓現象的出現。
**主循環指的是:**遊戲都是由更新狀態、處理數據、播放音樂、更換地圖和處理動畫來構成。而能引起這種的更新一般是由兩種行爲引起:
1)時間驅動(用戶輸入)
2)固定時間的FPS(每秒幀數)
而最能解析遊戲主循環的就是固定的FPS(每秒幀數),例如:每次刷幀,地圖向下移動0.1個單元格,人物向上移動0.2個單元格
###4、一個常用於測試每秒鐘創建了多少個對象的函數,數量:120以下最好
(function() {
var count = egret.$hashCount;
setInterval(() => {
var newCount = egret.$hashCount;
var diff = newCount - count;
count = newCount;
console.log(diff);
}, 1000)
})();
函數原理:
它就是實現了每秒去拿一個hasCount跟上一個它就是實現了每秒去拿一個hashCount跟上一個hashCount作對比,這個hashCount是由白鷺引擎呢內部API,用於統計引擎對象的創建數量。
如果hashCount>120,如何解決:
只需要在引擎的HasObject的構造函數這裏添加一個斷點,在運行時去檢查調用堆棧就可以了。
###5、白鷺的3D引擎的核心功能及內部優化技巧 1)egret3D內部的所有資源都採用GLTF文件格式。這是一種對OpenGL ES、WebGL非常友好的3D內容格式標準。
2)面向實時渲染,儘量提供可直接傳輸給圖形API的數據格式,而不再需要反序列化。
3)3D領域的JPEG。
在egert3D內測版本中,在3D引擎加載一個模型文件,過程:
首先加載模型文件———解析模型文件———生成WebGL所需要的數據格式———提交到GPU
缺點: 1、模型解析速度,加載速度都比直接是WebGL所需要的數據格式時的速度慢。
2、內存也比直接WebGL需要的數據格式時的佔用多。
在Egret3D正式版中,加載一個模型文件:
加載模型文件(與GPU想要的文件格式幾乎一樣———GLTF)——————提交到GPU
優化了:
1、減少了文件的解析與重新生成新的數據格式。
優點: 1、比內測的模型解析速度快。
2、比內測的加載速度快。
3、比內測的內存佔用低。
#二、遊戲性能優化——egret官網優化 ##一、簡介: ###1、通過幾個方面優化移動端遊戲:
1)少使用Alpha混合(即透明度)。
2)顯式停止計時器,讓它們準備好進行垃圾回收。
3)在不需要觸摸交互性時顯式禁用觸摸交互性。
4)使用事件偵聽器並在不需要時刪除這些偵聽器。
5)合理使用dispatchEvents函數(自定義事件)。
6)儘可能重用對象,建立對象池,而不再創建的對象對其執行垃圾回收。
7)多次調用類屬性時,避免直接使用this.att,要建立局部變量賦值。
8)Event.ENTER_FRAME(幀事件)數量控制
9)減少不必要的引用。
10)減少顯示對象的旋轉、縮放。
11)使用SpriteSheet合併的圖片尺寸要優於單張圖片的總尺寸,尤其是帶透明通道的。
12)在Http請求中,加載單個文件速度要優於加載多個文件。
改變性能的常規方法:
1)降低內存的使用量。
2)降低CPU的使用量。
3)是否可以調用GPU。
###2、運行時代碼執行基本原理: 在引擎的設計中,Egret底層使用了"彈性跑道模型",即各種操作都是針對每"幀"發生的。 例如:我們指定幀速度30幀/秒,則運行時會嘗試使每個幀的執行時間隔1/30秒。
每個幀循環包括兩個階段,三部分:enterFrame事件
、事件
和呈現
。
第一階段:包括兩部分centerFrame事件
和事件
,第一部分是派發一次enterFrame事件
,第二部分是調度運行時的事件
。
第二階段:是第一階段所有事件調度完後會執行clear後,幀循環的呈現
階段開始。此時,運行時將計算屏幕上所有可見元素的狀態並將其繪製到屏幕上。
然後,此進程重複,就像賽跑者圍繞跑道奔跑。
①有些情況,一半時間運行幀循環事件處理函數和應用程序代碼,另一半事件發生呈現。
②有些情況,應用程序代碼會通過增加雲運行事件並減少用於呈現的事件來佔用幀中多一半的可用時間(即應用程序的時間超過1/2幀)。
③有些情況,特別是對於混合模式等複雜的可見內容,呈現需要的時間會多於1/2幀的時間。
由於各階段需要的實際時間是靈活的,所以幀循環常稱爲彈性跑道
。
如果一個幀循環需要的時間超過1/30秒,則運行時不能以每秒30幀的速度更新屏幕,如果幀速率減慢,將影響體驗效果,最樂觀情況是動畫斷斷續續,如果情況更糟糕,可能畫面停止,或程序崩潰。
###3、體驗反饋與實際性能:
根據操作的運行時與創建對象實例數來度量遊戲性能:
1)動畫是流暢還是斷斷續續?
2)音頻是連續播放還是暫停再恢復播放?
3)鍵入時,文本輸入保持同步還是有些延遲?
4)如果單擊,會立即發生某些情況還是存在延遲?
5)PC應用程序運行時,CPU風扇聲音是否會變大?
6)在便攜式計算機或移動設備上運行應用程序時,電池是否會很快好盡?
7)開啓性能調試面板
##二、內存優化
###1、顯示對象:
Egret包含多種顯示對象,要限制內存使用量,需要選擇合適的顯示對象:
1)對於非交互的簡單形狀,使用Shape。
2)對於沒有重繪需求但有子節點,使用DisplayObjectContainer。
3)對於有重繪需求而且有子節點的,使用Sprite。
###2、重用對象:
重用對性能與內存非常重要。
需要密集的創建對象,要引用對象池;例如:做一款打飛機類型遊戲,進入戰鬥前,飛機、怪物、掉血特效等對象提前初始化,在過程中實時提取,而不是實時創建。
儘管使用對象池具有性能優勢,但它的主要好處是讓內存管理工作更容易。如果內存利用率無限制地增長,對象池可預防該問題。此技術通常可提高性能和減少內存的使用。
###3、釋放內存:
JavaScript中,GC在回收內存時,首先判斷該對象是否被其他對象引用。在確定沒有其他引用,GC在特定條件下進行回收。
1)刪除對象的所有引用確保被垃圾回收器回收。
2)刪除資源RES.destroyRes(“”)。
3)暫停清除計時器clearInterval()
、clearTimeout()
、Timer.stop()
。
###4、使用位圖(即:Bitmap):
位圖的創建過程中,需要考慮時機,創建是一個消耗內存與CPU的過程。大量的位圖創建會使你內存佔用快速增長,導致性能下降,可以使用對象池優化創建銷燬。
針對分辨率製作相關素材要比適配最大分辨率節省內存,並且減少由程序自適應帶來性能下降。(是否一張張拼成要比一幅大的性能要優)。
寬高<=1024*1024,這不是針對內存佔用,但是它影響兼容性。
###5、文本: TextField減少對於描邊的使用(stroke)。
TextField中使用cacheAsBitmap,可以減少重繪次數。
固定文字段落應當使用位圖避免文字重繪產生開銷(即:BitmapText)。
###6、事件模型與回調: Egret是基於事件模型的。這裏主要指出事件模型值得注意的事項。
dispatchEvent()
方法循環訪問註冊列表,並對各個註冊的對象調用事件處理函數方法,以下代碼說明過程:
//觸發
this.dispatchEvent(new egret.event(egret.event.ADDED_TO_STAGE));
//監聽
this.addEventListener(egret.Event.ADDED_TO_STAGE, this.test, this);
在每次觸發時,會實例化Event
對象,這樣會佔用更多內存,當偵聽Event.ENTER_FRAME事件時,將在各個幀上爲事件處理函數創建一個Event對象。在捕獲和冒泡階段(如果顯示列表很複雜,此成本會很高),顯示對象的性能可能會特別低。
##三、CPU優化: ###1、觸控交互:
禁用不必要顯示對象的觸摸交互。
使用交互對象(例如MovieClip或Sprite對象時),尤其是屏幕上顯示許多交互對象,當他們重疊時,檢測觸摸交互可能會佔用大量的CPU資源。避免這種情況的一種簡便方法是對不需要任何交互的對象禁用交互行爲。以下說明禁用觸摸交互:
var sp:egret.Sprite = new egret.Sprite();
this.addChild(sp);
sp.touchChildren = false; //確保子孫是否接受觸摸事件,默認true
sp.touchEnabled = false; //此對象是否接受觸摸事件,默認true
還有一種方式禁用對象的touchEnabled
,父容器創建如:遮罩容器並偵聽點擊事件,尋找對象座標標點——要額外寫邏輯,就是判斷觸摸的座標點是對應哪個對象的座標點。
###2、JavaScript: javaScript是一種解釋型語言,執行速度要比編譯型語言慢得多,隨着作用域中的作用域數量的增加,訪問當前作用域以外的變量的時間也在增加。所以,訪問全局變量總是比訪問局部變量要慢,因爲需要遍歷作用域鏈。只要能減少花費在作用域鏈上的事件,就能增加腳本的整體性能,對於寫法的技巧非常重要,下面是一些簡單常用技巧。
對於v8虛擬機的優化方法是:避免動態添加屬性與修改變量類型,好處是減少創建新類的開銷,v8中當試圖修改動態變量或屬性時,虛擬機會把function
類型緩存爲一個固定的C++類並觸發虛擬機的重新編譯。當你使用TypeScript
顯而易見的好處是類型的聲明以及語法規範會使你避免這種情況發生。 1)類方法中,將this賦值給另一個臨時變量。
2)在循環中,嘗試改進寫法,減少讀取次數。
var len:int = array.length;
for (var i:int = 0; i < len; i++) {
}
3)避免雙重解釋,如:eval函數
會使javaScript創建解釋器,產生額外的性能消耗。
如:
eval("alert('hello worid')"); //避免
var sayHi= new Function(“alert('hello world')”); //避免
setTimeout(“aler(‘hello world’)”); //避免
4)推薦使用正則表達式處理字符串遍歷。
5)避免使用[“property”]訪問對象屬性,改爲Object.property。
6)創建對象var obj:Object = {"key":"value"} > var obj:Object = {} > var obj:Object = new Object()。
7)字符串類型 “” > String() > .toString() > new String()。
8)類聲明屬性不宜過多(< 64),少繼承,多引用。
引用:
var obj:Object = {};
var obj2:Object = obj;
//即把obj複製到obj2,但兩個變量是指向同一個地址,所以其中一個變量屬性值改變了另一變量也會跟着改變(值是number類型除外)。
繼承:
var obj:Object = {};
obj.name = 'hang';
var obj2:Object = new obj();
console.log(obj2.name); //hang
//即obj2把obj的所有可繼承的屬性都複製了並且obj2和obj的地址是不一樣的,改變其中一個不會影響另一個變量。
9)代碼中Getter
、Setter
、Try-catch
會使性能下降。
10)請保持數組中類型的一致。
###3、計時器與enterFrame事件(幀事件): 顯式停止定時器,移除enterFrame偵聽。
將程序中Timer對象與enterFrame註冊數量降至最少,事件中儘量減少對顯示對象外觀的更改。
每幀在運行時將爲顯示對象在內部註冊enterFrame事件,但這樣做將使每幀執行更多代碼,如果超出處理範圍,程序會出現卡頓。可以考慮使用一個集中的enterFrame處理函數,通過集中同類代碼,更容易管理所有頻繁運行的代碼。
如果使用Timer對象,也將產生與多個Timer對象創建與調度事件相關的開銷。減少或合理設置觸發事件,對於性能提升很有幫助。停止未使用的Timer。
###4、後臺對象: 移除顯示列表中的對象,而不是visible = fals, 對象仍在父級顯示列表,某些功能依然在遍歷這個列表。
避免一些後臺對象參與邏輯,例如一些已經移出顯示列表的對象,是否需要碰撞檢測,距離運算等。
適當延長檢測時間,例如:碰撞間隔,非需要特別精確的時候使用簡單的矩形碰撞。
###5、動畫: 對於簡單的動畫,序列幀的性能更佳。
使用StalingSwf製作動畫時,導出文件之前刪除合併fla中多餘幀,可以減少json體積。
處理動畫移動,使用幀時間差計算而不要使用頻率,不要相信頻率,它在各種環境中時不穩定的。
##四、重繪優化:
###1、渲染對象: 要改進渲染,務必考慮顯示列表的嵌套。每個顯示對象都是獨立的,當對象放入顯示列表後參與整個渲染過程。
繪製過程:由內而外
, spr > 文檔類 > 舞臺。
Egret版本大於2.5,引擎提供了自動的髒矩形,極大提高渲染能力,無需手動設置。
髒矩形:
設置了髒矩形的區域引擎不會每幀都對顯示列表中的顯示對象進行重繪,大大的提高了渲染能力。
//關閉自動髒矩形
顯示對象.stage.dirtyRegionpolicy = egret.DirtyRegionPolicy.OFF;
//開啓自動髒矩形
顯示對象.stage.dirtyRegionpolicy = egret.DirtyRegionPolicy.ON;
###2、顯示優化: 涉及頻繁在Stage添加移除對象並且不關心ADDEDTOSTAGE(對象本身被添加到顯示列表時觸發)與REMOVEfRMOSTAGE(對象本身從顯示列表中移除時觸發)事件時,可以進行addChild和removeChild優化,減少邏輯判定。
###3、Alpha混合: 使用alpha實行時避免使用要求alpha混合的效果,例如:淡化效果。當顯示對象使用alpha值混合處理時,運行時必須將每個堆疊顯示對象的顏色值與背景色混合起來,以確定最終的顏色。因此,alpha值混合處理可能比繪製不透明顏色佔用更多的處理器資源。這種額外的計算可能影響慢速設備上的性能,儘可能避免使用alpha屬性。
###4、幀頻速率: 穩定的幀率是遊戲性能最重要的表現,非動作遊戲降低遊戲幀率可以大幅提升性能。
this.stage.frameRate = 30; //能被60整除的數
使用egret.callLater,egret.serTimeout自定義分幀管理器等來實現功能的分幀延時處理。
例如:
①切換界面時,界面顯示與數據填充進行一個分幀或者延時處理來保證UI切換時的流暢性。
②當同一幀創建多個顯示對象時,可進行分幀處理,保證幀率穩定。
偵聽休眠與激活狀態改變幀率與動畫:
this.stage.addEventListener(egret.Event.ACTIVATE, this,onActive, this);
this.stage.addEventListener(egret.Event.DEACTIVATE, this.onDeactive, this);
休眠中停止動畫與呈現相關內容,在程序被激活時重新啓動。
###5、位圖緩存 在適當的時候對多位圖容器使用位圖緩存功能。
選擇cacheAsBitmap可實現良好的優化。此功能對漸變、多段落文字、多位圖、9宮格等有顯著提高呈現的性能,但是需要佔用大量的內存。
當顯示對象內部是實時改變的,啓動位圖緩存或獲得相反的效果。當每幀運行時必須更新緩存位圖,然後屏幕重繪該位圖,這一過程需要消耗許多CPU。
僅當緩存的位圖可以一次生成,且隨後無需要更新時,適合使用位圖緩存功能。
對Sprite顯示對象開啓位圖緩存後,縮放、移動、修改XY屬性不會導致重新生成,但是修改Sprite內部子項將會導致重新生成緩存。
合理使用位圖緩存,可以極大程度提高渲染性能。
##五、網絡優化
###1、資源劃分 減少界面層次,多層組合爲獨立位圖是目前高性能的方法。界面有時會分爲單色背景,紋理,邊框,子項邊框等這種設定對於網絡與程序性能沒有提高,合併以上圖層會對你應用帶來質的飛躍。
合理的根據遊戲進程去加載所需要資源,常見的方法有:
1)每次場景變換隻加載所需資源。
2)每次點擊界面入口按鈕加載資源並緩存。
3)初始化應用值只加載可能曲劇全局使用與當前可能需要的。
4)分塊分組資源加載。
###2、加載通信
當每次發起的HTTP請求或有協議頭,確認過程,返回數據,優化合並文件減少請求數可以顯著提高網絡性能。
資源服務器開啓GZIP壓縮,提高載入速率。
對於一些有透明通道的png圖片格式,可以使用壓縮軟件進行一定比例壓縮,會有非常大的提升空間。對於沒有透明通道的資源推薦使用jpg已達到提升網絡加載。
對於MP3,儘可能的減少其採樣率與碼率。
使用egret compress_json
命令壓縮json文件,使體積減少。
加載中的顯示對象,給予顯示對象預設位圖,這也許並不能帶來網絡性能提升甚至有所下降,但是這樣的修改對於用戶體驗是極佳的。
如果不能做預加載處理,用戶並不瞭解屏幕中呈現的最終效果,也許認爲遊戲有問題,給與用戶感知思考是非常好的體驗。
#三、遊戲開發性能優化:
##一、遊戲優化的四個考慮方向:
1、網絡和IO:
1)異步加載資源。
2)分幀處理網絡包進行處理。
3)限制發包頻率。
4)適當拆包或者合包。
2、CPU:
1)緩存數據。
2)異步計算數據(分幀或者多線程)。
3)採取合理算法和數據結構。
3、內存:
1)動態加載和卸載資源。
2)合理規劃美術資源。
4、GPU:
1)優化美術資源。
2)優化Shader。
3)對不同平臺使用不同的格式或處理方案(如:IOS和安卓)。
###1、CPU:
引發的問題:
1)由於短時間內計算量太大,導致畫面流暢性降低,俗稱跳幀。
2)發熱嚴重,耗電量高。
常見優化手段:
1)將計算分到多個邏輯中進行計算,避免短時間內的性能超過負荷,俗稱分幀(time-slice)
。
2)將可以緩存的數據儘可能的緩存起來,避免重複計算和重複分配內存,常見的示例爲內存池
。
3)使用合理的算法和數據結構,比如:冒泡排序和直接插入排序在整體數組比較有序的情況下效率大大好於快速排序,把快速排序替換成是優化程序排序效率的一個常見的思路。
###2、GPU: 引發的問題:
1)發熱嚴重,耗電量高。
2)FPS降低。
常見的優化手段:
1)優化美術資源,比如合理規劃圖集,約定好模型的最大三角形面數,製作合理的粒子效果規範。這個可以說是遊戲優化中最重要的一個,因此,技術美術在遊戲開發中作用巨大。
模型的最大三角形面數:
在3D遊戲中才有。
2)簡化或者優化着色器(shader),如在遊戲開始前就對shader進行編譯和加載。
3)使用Batching,儘量減少DrawCall。
4)使用引擎推薦的壓縮格式。
###3、IO和網絡: 引發的問題:
1)網絡延遲甚至掉線。
2)加載資源導致的跳幀。
3)加載時間過長。
常見的優化手段:
1)使用獨立的線程進行加載,有些引擎還能利用協程(如:Unity)。
2)減少網絡包裏面的冗餘數據。
3)合併小包,減少請求數據的次數。
4)分幀對回包進行處理。
5)限制一定事件內的發包率。
###4、內存: 引發的問題:
1)閃退和卡死。
常見的優化手段:
1)動態加載和卸載資源,比如在遊戲內的時候,我們可以把遊戲外的一些UI圖集卸載掉。
2)降低資源質量或屏幕分辨率,這是有損優化,一般作爲最後的手段。
#四、Egret性能優化之優化渲染 ###1、Egret在內核中是如何來處理渲染部分的? MainLoop ——> EnterFrame(幀事件) ——> clear ——> stageUpdateTransForm ——> stageDraw。
Egret每刷新一幀的時候,會執行四步操作。
1)執行一次EnterFrame,此時,引擎會執行遊戲中的邏輯。並且拋出EnterFrame事件。如果在這裏編寫了大量消耗性能的代碼,那麼遊戲的幀頻就會開始下降。
2)引擎會執行一個clear,將上一幀的畫面全部擦除。
3)Egret內核會遍歷遊戲中所有的DisplayObject,並重新計算所有顯示對象的transform——visible = false的顯示對象也會參加計算。
4)將所有的圖像全部draw到畫布中。
###2、瞭解了egret的渲染機制,優化遊戲: 1)不想要的DisplayObject,請removeChild掉,如果是設置了它的visible屬性的false,確實這個顯示對象不會被渲染出來,但是,它還是會參與到第三步的計算過程。所以也無形中增加了性能的開銷。如果某一個圖像被其他圖像遮蔽,那麼你就需要移除被遮蓋的對象。
2)太多的顯示對象不僅會在第三步會消耗性能,更重要的是,在第四步的時候,會嚴重影響性能,讓幀頻下降。
①可以將畫面中的元素進行合併,合併不是將兩個Bitmap塞到一個Sprite中,這樣病不起作用,無論是嵌套好事並列,都會消耗大量性能。如果可以,最好調整遊戲元素圖片的拆分方式,儘量減少DisplayObject數量。
②使用cacheAsBitmap,讓你的矢量圖在運行時以位圖形式進行計算。這回大大減少你的矢量圖運算。
3)儘量不要在EnterFrame事件中做過多的操作,EnterFrame事件派發太頻繁了。
4)善用髒矩形是一種非常高效的優化手段,但它是把雙刃劍。用的好,性能飆升,用不好,自取滅亡。