Egret開發《消滅方塊》後記(二)物件池的應用
對於效能的追求是永無止境的,此文只做拋磚引玉,歡迎討論。
用好物件池絕對能提高很多專案的執行效率,下面結合我在《消滅方塊》的開發介紹一下物件池的應用。
一、什麼是物件池?
頻繁地建立和銷燬物件會消耗很多效能,有需要頻繁建立物件的時候使用物件池絕對是不二的選擇。《消滅方塊》中的方塊是操作最頻繁的一個物件,一邊銷燬一邊建立,不加優化的話會嚴重拖累效能。
既然物件池這麼好,到底什麼才是物件池呢?聽起來很高深,簡單來講物件池就是個陣列,把不用的物件放進去,因為陣列還儲存了物件的引用,所以物件不會被回收,等需要用的時候再從陣列中取出來,把這個陣列當作一個”快取池“。簡單的例子:
回收不用的物件:
public recycleObject(noUse:any):void { this._pool.push(noUse); }
需要用這個物件的時候,先檢索物件池有沒有快取物件,有的話取出來以後初始化屬性,物件池是空的話再建立新物件:
public getObject():any
{
if(this._pool.length)
return this._pool.shift();
else
return new egret.Bitmap(); //用bitmap作示例
}
這就是一個簡單的物件池。
二、Egret中的物件池
在Egret的egret-game-library下面有個ObjectPool.ts,這邊是Egret官方的“物件池”,關鍵程式碼如下:
public createObject(classFactory:any):GameObject { var result; var key = classFactory.key; var arr = this._pool[key];
//先在快取中檢索是否有快取的目標物件
if (arr != null && arr.length) {
result = arr.shift();
}
<span style="font-family: Arial, Helvetica, sans-serif;"> //快取中沒有物件可用時,根據型別建立新的物件作為返回</span>
else {
result = new classFactory();
result.key = key;
}
//快取的物件再次使用應該執行初始化操作 result.onCreate(); this._list.push(result); return result; } public destroyObject(obj:GameObject) { var key = obj.key; if (this._pool[key] == null) { this._pool[key] = []; } this._pool[key].push(obj); obj.onDestroy(); var index = this._list.indexOf(obj); if (index != -1) { this._list.splice(index, 1); } }
Egret的ObjectPool更進一步,用的陣列+字典的組合來快取物件,這樣的好處就是可以快取不止一種物件。如果你的專案比較大,有很多地方需要用物件池的話,強烈建議使用Egret的ObjectPool。
三、《消滅方塊》中的物件池
如果專案本身比較小,只有一兩處需要用物件池的話,建議自己根據專案實際需要寫物件池比較好。一來用起來隨意,不用刻意繼承物件實現介面,二來可以做一些功能上的拓展,會“更好用”。
《消滅方塊》中一空有兩處使用了物件池,一處是方塊物件池,快取和建立方塊用的,程式碼很簡單就不再貼了。另一處是拼接數字的物件池,就是把0-9個數字的圖片素材根據實際需求拼接成需要的數字,使用的頻率比較高,在多處都有呼叫,於是寫了個通用的管理類處理這項需求。
效果圖:
實現原理示意圖:
不得不說,當DisplayObjectContainer不再像AS3一樣是抽象類的時候用起來有多方便。原理就是上圖,用多個單數字的Bitmap拼接成目標數字,存放到一個DisplayObjectContainer中返回給需要用的地方。那麼這一個數字物件確切來說包含了兩種物件(Bitmap和DisplayObjectContainer),所以回收快取這個物件就要分兩步:首先分解DisplayObjectContainer內容,清空並快取裡面的BItmap,最後快取外部DisplayObjectContainer,所以,實現這個功能就需要兩個快取池搭配使用。
原理講完上程式碼:
class ImgNumManager
{
private _imgPool:egret.Bitmap[];
private _containerPool:egret.DisplayObjectContainer[];
private static _instance:ImgNumManager;
public constructor()
{
this._imgPool = [];
this._containerPool = [];
}
public static i():ImgNumManager
{
if(!this._instance)
this._instance = new ImgNumManager();
return this._instance;
}
//根據需要的數字和型別返回一個<span style="font-family: Arial, Helvetica, sans-serif;">DisplayObjectContainer</span>
public createNumPic(num:number, type:string):egret.DisplayObjectContainer
{
var container:egret.DisplayObjectContainer = this.getContainer();
var numStr:string = num.toString();
var index:number = 0;
var tempBm:egret.Bitmap;
for(index; index < numStr.length; index ++)
{
tempBm = this.getSingleNumPic(parseInt(numStr.charAt(index)), type);
container.addChild(tempBm);
}
this.repositionNumPic(container);
return container;
}
//回收帶數字的DisplayObjectContainer
public desstroyNumPic(picContainer:egret.DisplayObjectContainer):void
{
this.clearContainer(picContainer);
if(picContainer.parent)
picContainer.parent.removeChild(picContainer);
this._containerPool.push(picContainer);
}
<pre name="code" class="java"> //改變帶數字的DisplayObjectContainer數字值
public changeNum(picContainer:egret.DisplayObjectContainer, num:number, type:string):void { var numStr:string = num.toString(); var tempBm:egret.Bitmap;
<span style="font-family: Arial, Helvetica, sans-serif;">//如果當前數字個數多於目標個數則把多餘的回收</span>
if(picContainer.numChildren > numStr.length)
{
while(picContainer.numChildren > numStr.length)
{
this.recycleBM(<egret.Bitmap>picContainer.getChildAt(picContainer.numChildren - 1))
}
}
var index:number = 0;
for(index; index < numStr.length; index ++)
{
//如果當前的Bitmap數量不夠則獲取新的Bitmap補齊
if(index >= picContainer.numChildren)
picContainer.addChild(this.getBitmap());
(<egret.Bitmap>picContainer.getChildAt(index)).texture = RES.getRes("imgs."+type+numStr.charAt(index));
}
this.repositionNumPic(picContainer);
}
//每個數字寬度不一樣,所以重新排列
private repositionNumPic(container:egret.DisplayObjectContainer):void
{
var index:number = 0;
var lastX:number = 0;
var temp:egret.DisplayObject;
for(index; index < container.numChildren; index++)
{
temp = container.getChildAt(index);
temp.x = lastX;
lastX = temp.x + temp.width;
}
}
//清理容器
private clearContainer(picContainer:egret.DisplayObjectContainer):void
{
while(picContainer.numChildren)
{
this.recycleBM(<egret.Bitmap>picContainer.removeChildAt(0));
}
}
//回收Bitmap
private recycleBM(bm:egret.Bitmap):void
{
if(bm && bm.parent)
{
bm.parent.removeChild(bm);
bm.texture = null;
this._imgPool.push(bm);
}
}
private getContainer():egret.DisplayObjectContainer
{
if(this._containerPool.length)
return this._containerPool.shift();
return new egret.DisplayObjectContainer();
}
//獲得單個數字Bitmap
private getSingleNumPic(num:number, type:string):egret.Bitmap
{
var bm:egret.Bitmap = this.getBitmap();
bm.texture = RES.getRes("imgs." + type + num);
return bm;
}
private getBitmap():egret.Bitmap
{
if(this._imgPool.length)
return this._imgPool.shift();
return new egret.Bitmap();
}
}
這個類使用了單例模式,全域性統一用這一個單例管理。提供了三個公共方法分別是建立、回收和改變值,已寫好註釋,如有不足或者錯誤歡迎跟帖討論。
相關推薦
Egret開發《消滅方塊》後記(二)物件池的應用
對於效能的追求是永無止境的,此文只做拋磚引玉,歡迎討論。 用好物件池絕對能提高很多專案的執行效率,下面結合我在《消滅方塊》的開發介紹一下物件池的應用。 一、什麼是物件池? 頻繁地建立和銷燬物件會消耗很多效能,有需要頻繁建立物件的時候使用物件池絕對是不二的選擇。《消滅方塊
it編程開發模式有哪些(二)
選擇 靜態 代碼生成 最有 組合 自己的 通用 工作流程 rapi IT編程的開發模式一共有10種,或許有更多,但是常見的和常用的是10種模式。前面有提到了也詳細的說明了前五種的開發模式,下面就來漸漸後面的五種開發模式。IT編程開發模式有哪些(二) 1、 it編程
開發工具之Git(二)
一次 別名 拉取 官網 wan img log 方法 用戶 目錄 四、Git安裝與配置 (一)安裝 (二)配置 (三)創建倉庫 五、Git基本命令 六、Git分支 上一篇講了Git的基本原理,建議沒看過的同學先看看,然後這次我們來講Git的具體操作和指令。 四、G
從頭開發一個Flutter外掛(二)高德地圖定位外掛
在上一篇文章從頭開發一個Flutter外掛(一)開發流程裡具體介紹了flutter外掛的具體開發流程,從建立專案到釋出。接下來將會為Flutter天氣專案開發一個基於高德定位sdk的flutter定位外掛。 完整程式碼在git倉庫裡 github.com/KinsomyJS/l… 申請key 首先先進入
如何使用ES6開發Three.js專案(二)
如何使用ES6開發Three.js專案(二) 之前寫過一篇文章如何使用ES6開發Three.js專案(一),這次再完善一下,並把程式碼放在GitHub上了。 three.js-es6-webpack 基於ES6開發的three.js演示專案 專案目錄: pub
嵌入式核心及驅動開發之學習筆記(二) 實現應用控制驅動
Linux系統根據驅動程式實現的模型框架將裝置驅動分成字元裝置驅動、塊裝置驅動、網路裝置驅動三大類。這裡簡單理解一下概念 字元裝置:裝置按位元組流處理資料,通常用的串列埠裝置、鍵盤裝置都是這種。 塊裝置:裝置按塊單位對資料處理,通常是儲存裝置。 網路裝置:顧名思義,建立在soc
二..linux開發之uboot移植(二)——網路命令ping開發搭建使用&tftp伺服器的安裝&nfs網路伺服器的安裝
2018/01/05 19:48 - 網路命令搭建開發板uboot和虛擬機器ubuntu互相ping通記錄 1. uboot可以通過網路來傳輸檔案到開發
用Electron開發企業網盤(二)--分片下載
書接上文,背景見:https://www.cnblogs.com/shawnyung/p/10060119.html HTTP請求頭 Range 請求資源的部分內容(不包括響應頭的大小),單位是byte,即位元組,從0開始。 如果伺服器能夠正常響應的話,伺服器會返
egret 釋出android原生專案(二)執行專案
一、編譯Demo專案 在終端定位到Demo目錄,執行如下命令 egret build -e (-e 編譯指定專案的同時編譯引擎目錄) 在瀏覽器訪問http://10.0.0.237/demo/index.html(10.0.0.237為本地ip地址) 二、Androi
阿里Java開發手冊學習筆記(二)----異常日誌、MySql規約
一、異常處理 不捕獲Java類庫中定義的繼承自RuntimeException的執行時異常類。此類異常應由程式設計師預檢查規避。 異常不要用來做流程控制,效率太低。 對非穩定程式碼的catch儘可能
Framework7 + cordova +AS 混合開發安卓app(二)
四、cordova建立專案 建立專案 cordova create hello com.example.hello HelloWorld 如果一切正常,本條命令將建立一名為hello的專案資料夾,com.example.hello是你的專案包名,它將生成一個
Unity3D開發之自制座標軸(二)
我在上篇部落格寫出瞭如何用程式碼建立一個我們可以任意控制頂點得圓柱體。本片部落格著重記錄如何在空間建立類似unity可以控制物體的座標軸。由於我們要控制圓柱體的長度,所以我們設計的座標軸是兩個方塊在圓柱體的兩端。效果圖如下:  
Android開發————簡易APP設計(二)
實驗內容 使用sqlite 給備忘錄app增加資料儲存功能 實驗步驟 活動1: Xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://sche
C# 微信開發-----微信會員卡(二)
主要說說如何使用微信的啟用會員卡 如圖: 點選啟用會員卡時,要跳轉到如下的圖片: 要實現這個功能,首先我們在建立會員卡後就操作如下程式碼 #region 新增啟用時的自定義欄位 string cus
手把手教你用nginx開發自己的伺服器------利用nginx開發一個helloWorld程式(二)
現在我們正式開始編寫nginx的helloWorld功能,該從哪下手呢?別急,我們在上一篇文章中提到了事件驅動對吧。nginx是怎麼樣事件驅動的呢?我們來看看ngx_worker_process_cycle()這個函式的一部分for ( ;; ) { if
django-web開發框架-模型類(二)
設計介紹 本示例完成“圖書-英雄”資訊的維護,需要儲存兩種資料:圖書、英雄 圖書表結構設計: 表名:BookInfo 圖書名稱:btitle 圖書釋出時間:bpub_date 英雄表結構設計: 表名:HeroIn
Docker 構建 Java Web 開發環境——使用Dockerfile(二)
上篇文章 Docker 構建 Java Web 開發環境(一)使用 docker commit 命令,直接在容器中操作(安裝軟體及配置檔案等),比較簡單直觀;這篇文章使用 docker build命令和 Dockerfile 檔案,模板化映象構建過程,推薦使用這
python開發電影查詢系統(二)—Django展示
上篇部落格講了python爬取電影資訊的後臺資料處理,現在我們將Django前端顯示。 如果對Django還不熟的朋友可以先找資料熟悉一下。 這裡我們直接講。 1.安裝好Django後,找到你的工作目錄,建立好一個專案find_film: dja
Go遊戲伺服器開發的一些思考(二):綜合考察(上)
對Go做體檢 Go語言在遊戲伺服器方向的開發,起步比較晚。有必要考察下使用Go來實現,該怎麼去做,會遇到什麼問題,以及怎麼克服。這裡把使用Go語言開發MMO RPG遊戲作為考察物件,對Go做一次體檢。 MMO RPG最小功能清單 首先,羅列下開發一個M
MFC對話方塊如何使用工具欄並修改工具欄的背景顏色與自繪對話方塊統一(二)
寫這篇文章只是為了解決我上一篇文章遺留的一個問題,本來覺得這個問題可以不用管,但是在我寫的程式中發現並不能置之不理,先來重現這個問題 工具欄有一部分沒有繪上 看紅色框包圍的地方,是背後 VS2010 的介面,因為程式擦成透明的啦,上一篇文章我錯誤