Egret自定義位圖文字(自定義+BitmapLabel)


 

一 自定位圖文字

因為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

 


免責聲明!

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



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