一 自定位图文字

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

 

posted on 2019-11-10 22:01  gamedaybyday  阅读(2024)  评论(0编辑  收藏  举报