一 自定位圖文字
因為egret的位圖文字是texturemerger做的,需要多張單圖片導入tm,然后導出兩個文件來使用,過程比較麻煩。
而Laya的位圖文字則是一張整圖數字圖片,使用FontClip就能直接使用, 很方便。
所以現在弄個自定義的位圖文字類,也不用tm去導圖了。
二 決戰沙城的位圖文字代碼
先來看看別人的。據說這個框架里的位圖文字被用於很多大型H5 mmo項目。14年寫的工具類ε=(´ο`*)))唉。
主要原理就是1個字1個bitmap,然后並列排起來。“123“就是3個bitmap排起來。
/** * 素材需要提前加載好 * 素材命名規則:類型_數值(有類型是因為一般會同時有幾種數字圖片,比如大小號或不同顏色) * 點號素材命名:類型_dot * 創建BitmapNumber使用createNumPic返回DisplayObjectContainer * 創建好的BitmapNumber數值需要變化是調用changeNum * 回收使用desstroyNumPic * * Created by Saco on 2014/8/1. */ class BitmapNumber extends SingtonClass { private _imgPool:egret.Bitmap[]; private _containerPool:egret.DisplayObjectContainer[]; public constructor() { super(); this._imgPool = []; this._containerPool = []; } /* * 根據需要的數字和類型返回一個DisplayObjectContainer * num數字值,支持小數點 * type素材類型 * */ 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(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); } /* * 改變帶數字的DisplayObjectContainer數字值 * 提供這個方法是為了提高效率,直接更換之前創建對象的texture,避免多余的刪除和創建 * */ public changeNum(picContainer:egret.DisplayObjectContainer, num:number, type:string):void { var numStr:string = num.toString(); var tempBm:egret.Bitmap; //如果當前數字個數多於目標個數則把多余的回收 if (picContainer.numChildren > numStr.length) { while (picContainer.numChildren > numStr.length) { this.recycleBM(<egret.Bitmap>picContainer.getChildAt(picContainer.numChildren - 1)) } } var index:number = 0; var tempStr:string; for (index; index < numStr.length; index++) { //如果當前的Bitmap數量不夠則獲取新的Bitmap補齊 if (index >= picContainer.numChildren) picContainer.addChild(this.getBitmap()); tempStr = numStr.charAt(index); tempStr = tempStr == "." ? "dot" : tempStr; (<egret.Bitmap>picContainer.getChildAt(index)).texture = this.getTexture(tempStr, type); } 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:string, type:string):egret.Bitmap { if (num == ".") num = "dot"; var bm:egret.Bitmap = this.getBitmap(); bm.texture = this.getTexture(num, type); return bm; } private getTexture(num:string, type:string):egret.Texture { return RES.getRes(type + num); } private getBitmap():egret.Bitmap { if (this._imgPool.length) return this._imgPool.shift(); return new egret.Bitmap(); } }
順便看了下凡人修仙傳的位圖文字。可以看到文字是一整張合圖且無序排列,而且network里沒有fnt文件。推測應該也是使用支持單張數字的自定義位圖文字。
三 我自己寫了個
相對於決戰沙城的有些改動
1. 支持使用整圖和單張圖片。
2. 支持代碼創建和拖動到exml上擺放。
3. 文字圖片可以使用tm和其他圖片進行合並,減少drawcall和http請求,而不影響位圖文字的使用。
需要注意的是
1. 不要用cacheAsBitmap,對於變化數字非常卡。我試了下,卡爆了。
測試用整圖
測試用單張圖
具體代碼
/** * 位圖文字 * @deprecated 可以使用單張整圖或者多張散圖,制作位圖文字。 * 注意"."的圖片命名要改為dot,例如"1"是font_1.png,那么"."的圖片命名是font_dot.png。 * @author ck 2019.11.10 */ class BitmapFont extends eui.Component{ /**位圖緩存 */ private static bmCaches:Array<egret.Bitmap> = []; /**紋理緩存 */ private static textureCaches = {}; /**顯示的文字 */ private _text:string; /**圖片名 */ private pngName:string; public constructor() { super(); } /** * 文字在一張圖上 * @param pngName 圖片名 pngName = font_test (font_test.png) * @param txt 文字名 "0123456789.+-" */ public static registerByOne(pngName:string, txt:string){ let textureCache = this.getTextureCache(pngName); if(textureCache.length > 0){ console.log("位圖字體緩存已存在:",pngName); return; } let src:egret.Texture = new egret.Texture(); src = RES.getRes(pngName + "_png"); let len = txt.length; let fontWidth:number = src.textureWidth/len; let fontHeight:number= src.textureHeight; let texture:egret.Texture; let rect:egret.Rectangle = new egret.Rectangle(); for(let i=0;i<len;i++){ texture = new egret.Texture(); texture.disposeBitmapData = false; texture.$bitmapData = src.$bitmapData texture.$initData(i*fontWidth,0, fontWidth, fontHeight, 0, 0, fontWidth, fontHeight, src.textureWidth, src.textureHeight); textureCache[txt.charAt(i)] = texture; } } /** * 文字在不同的圖片上 * @param pngName 圖片名 pngName=font_test 多張圖片源文件名(font_test0.png font_test1.png .... font_testdot.png) * @param txt 文字名 "0123456789.+-" */ public static registerByMulti(pngName:string, txt:string){ let textureCache = this.getTextureCache(pngName); if(textureCache.length > 0){ console.log("位圖字體緩存已存在:",pngName); return; } let len = txt.length; let char:string; for(let i=0;i<len;i++){ char = txt.charAt(i); if(char == "."){ textureCache[char] = RES.getRes(pngName + "dot" + "_png"); }else{ textureCache[char] = RES.getRes(pngName + char + "_png"); } } } /** * 獲取紋理緩存 * @param pngName 圖片名 */ public static getTextureCache(pngName:string){ let textureCache = BitmapFont.textureCaches[pngName]; if(textureCache == null){ textureCache = []; BitmapFont.textureCaches[pngName] = textureCache; } return textureCache; } /** * 設置文字 * @param txt 文字 */ public set text(txt:string){ let bmCaches = BitmapFont.bmCaches; let textureCache = BitmapFont.getTextureCache(this.pngName); let curLen = this.numChildren; let targetLen = txt.length; this._text = txt; //文字存在,且大於顯示文字,則移除多余文字 if(curLen > targetLen){ let bm:egret.Bitmap; for(let i=curLen-1;i>=targetLen;i--){ bm = this.removeChildAt(i) as egret.Bitmap; bm.texture = null; bmCaches.push(bm); } } //顯示文字 let bm:egret.Bitmap; let tempX:number = 0; let char:string; for(let i=0;i<targetLen;i++){ //少於顯示文字,則增加文字 if(i >= curLen){ if(bmCaches.length > 0){ bm = bmCaches.pop(); }else{ bm = new egret.Bitmap(); } this.addChild(bm); } bm = this.getChildAt(i) as egret.Bitmap; bm.texture = textureCache[txt.charAt(i)]; bm.x = tempX; tempX = bm.x + bm.width; } } /** * 獲取文字 */ public get text(){ return this._text; } /** * 設置文字類型 * @value 字體類型 文字是font_test.png一張圖片,則value = "font_test""。 若文字是font_test0.png font1_test1.png..多張圖片,則value="font_test" */ public set font(value:string){ this.pngName = value; } /**銷毀 */ public destroy(){ //回收bitmap let len = this.numChildren; let bm:egret.Bitmap; let bmCaches = BitmapFont.bmCaches; for(let i=len-1;i>=0;i--){ bm = this.getChildAt(i) as egret.Bitmap; this.removeChild(bm); bm.texture = null; bmCaches.push(bm); } //從視圖移除 this.parent && this.parent.removeChild(this); } }
使用示例
//注冊 BitmapFont.registerByOne("font_test","02345"); //使用 let mc:BitmapFont = new BitmapFont(); mc.font = "font_test"; mc.text = "0222"; this.addChild(mc);
運行效果
四 測試下效率
新建n個位圖文字放到列表bmList,然后不停的切換數字顯示
private onEnterFrame(){ for(let i=0;i<this.bmList.length;i++){ //BitmapNumber.ins().changeNum(this.bmList[i],(Math.random()>0.5)?200000:200,"font" ); this.bmList[i].text = (Math.random()>0.5)?"200000":"200"; } }
200個 1000個 2000個 4000個
自定義字體 60FPS 58-60FPS 30FPS 8FPS
BtimapLabl 60FPS 59-60FPS 59-60FPS 30FPS
決戰沙城 60FPS 59-60FPS 24FPS 8FPS