Egret游戲性能優化


游戲性能優化

一、游戲性能——白鷺:

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)代碼中GetterSetterTry-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)善用臟矩形是一種非常高效的優化手段,但它是把雙刃劍。用的好,性能飆升,用不好,自取滅亡。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM