使用Laya开发微信小游戏时无法使用位图字体解决方案

-前言-

依照Laya官方提供的位图字体使用方案时,在本地是可以正常使用的。当发布到微信小游戏上就没法使用。经过查找是缺少解析xml的库。

-正文-

方案1:引入xml解析库

缺少什么我们就引入什么,我们引入官方提供的js库

//修改bin目录下game.js文件
if ((typeof swan !== 'undefined') && (typeof swanGlobal !== 'undefined')) {
	require("swan-game-adapter.js");
	require("libs/laya.bdmini.js");
} else if (typeof wx!=="undefined") {
	require("weapp-adapter.js");
	require("libs/laya.wxmini.js");
}
window.loadLib = require;
require("index.js");
//添加解析库
window.Parser = require("./js/dom_parser.js");

具体的解析库就执行去找了。本文不是着重讲解这个方案,这个方案有下面几个缺点:

  • Parser可能会遭到全局变量污染,导致无法调用到我们想要的函数。
  • 我们始终都是拉取xml文件再进行解析,在浏览器层面数据传输xml的效率并不如json
  • 为了解析xml我们会额外引入js文件,js文件需要被解析,增加了游戏进入时间。

因此我们可以将Laya源代码修改成使用json文件作为数据配置文件。

方案2 修改laya.core.js实现json文件作为fnt配置文件

1.xml到json的转换

生成fnt及png

前面的步骤我们还是保持不变,依旧使用Laya官方提供的方式,使用bmfont这个软件制作fnt位图字体文件。制作好后我们将.fnt(实际内容是xml)及.png文件放到laya/assets/font文件夹下。

转换.fnt文件到.json文件

首先这一步需要node环境及安装gulp,如果没有安装环境首先安装环境。这里默认安装了node环境。

//1.安装gulp
npm install gulp --save-dev
//2.安装转档所需依赖
npm install iconv-lite --save-dev
npm install xml-js

安装好gulp后我们在项目根目录新建一个gulpfile.js文件来写我们的转换代码。

const fs = require("fs");
const x2j = require("xml-js");
const path = require("path");
const iconv = require("iconv-lite");
function xml2json(cb) {
    let allPath = [];
    readAll("./laya/assets/font/", allPath);
    for (let i = 0; i < allPath.length; i++) {
        let srcPath = allPath[i];
        if (srcPath.indexOf(".fnt") != -1) {
            //xml配置中可能存在中文,因此利用iconv-lite转换为GBK在进行转档
            let xmlData = fs.readFileSync(srcPath, {encoding:"binary"});
            let buf = new Buffer(xmlData,'binary');
            xmlData = iconv.decode(buf,'GBK');
            let jsonData = x2j.xml2json(xmlData,{
                compact:true,
                ignoreDeclaration:true,
                ignoreCdata:true,
                ignoreDoctype:true,
                ignoreComment:true,
                nativeType:true
            })
            let destPath = srcPath.replace(".fnt",".json");
            fs.writeFileSync(destPath,jsonData,"utf-8");
        }
    }
    cb();
}

/**
 * 读取指定路径下的所有文件路径并赋值到out中
 * @param {string} parentPath 
 * @param {Array<string>} out 
 */
function readAll(parentPath, out) {
    try {
        let files = fs.readdirSync(parentPath);
        files.forEach(function (item) {
            let tempPath = path.join(parentPath, item);
            let stats = fs.statSync(tempPath);
            if (stats.isDirectory()) {
                readAll(tempPath, out);
            } else {
                out.push(tempPath);
            }
        });
    } catch (e) {
        console.warn("Path Error:" + e);
        return out;
    }
}
module.exports.xml2json = xml2json;

在gulpfile.js写好之后我们在终端直接执行gulp xml2json就可转档。我们上面使用的是xml-js这个库来实现的xml到json的转换,上面传给xml-js对象的参数不能变,否则后面解析json的代码也要做适当修改,不然是读取不到对应属性的。

2.修改laya.core.js

我们找到BitmapFont类,在loadFont方法下面添加一个loadFontByJson的方法。

//loadFontByJson
__proto.loadFontByJson=function(path,complete){
    this._path=path;
    this._complete=complete;
    this._isFntType = false;
    if (!path || path.indexOf(".json")===-1){
        console.warn('Bitmap font configuration information must be a ".json" file');
        return;
    }
    Laya.loader.load([{url:path,type:"json"},
    {url:path.replace(".json",".png"),
    type:/*laya.net.Loader.IMAGE*/"image"}],
    Handler.create(this,this._onLoaded));
}
/**
*@private
*/
__proto._onLoaded=function(){
    if(this._isFntType){
        this.parseFont(Loader.getRes(this._path),Loader.getRes(this._path.replace(".fnt",".png")));
    }else{
        this.parseFontByJson(Loader.getRes(this._path),Loader.getRes(this._path.replace(".json",".png")));
    }
    this._complete && this._complete.run();
}

加载完成后依旧回调到_onLoaded方法,通过_isFntType标志位来判断我们改BitmapFont实例是通过哪种方式加载的。接下来就创建parseFontByJson方法来解析数据即可。

/**
 * 通过json解析字体
 */
__proto.parseFontByJson=function(jsonData,texture){
    if(jsonData==null || texture == null)return;
    this._texture=texture;
    var data = jsonData.font;
    var tX=0;
    var tScale=1;
    var tInfo = data.info._attributes;
    this.fontSize=parseInt(tInfo.size);
    var tPadding=tInfo.padding;
    var tPaddingArray=tPadding.split(",");
    this._padding=[parseInt(tPaddingArray[0]),parseInt(tPaddingArray[1]),parseInt(tPaddingArray[2]),parseInt(tPaddingArray[3])];
    var chars;
    chars=data.chars.char;
    var i=0;
    for (i=0;i < chars.length;i++){
        var tAttribute=chars[i]._attributes;
        var tId = tAttribute.id;
        var xOffset=parseInt(tAttribute.xoffset) / tScale;			
        var yOffset=parseInt(tAttribute.yoffset) / tScale;
        var xAdvance=parseInt(tAttribute.xadvance) / tScale;
        var region=new Rectangle();
        region.x=parseInt(tAttribute.x);
        region.y=parseInt(tAttribute.y);
        region.width=parseInt(tAttribute.width);
        region.height=parseInt(tAttribute.height);
        var tTexture=Texture.create((texture),region.x,region.y,region.width,region.height,xOffset,yOffset);
        this._maxWidth=Math.max(this._maxWidth,xAdvance+this.letterSpacing);
        this._fontCharDic[tId]=tTexture;
        this._fontWidthMap[tId]=xAdvance;
    }
}

修改完JS代码之后,我们需要在.d.ts中添加这个类的声明,不然Ts调用的时候编辑器会报错。
在LayaAir.d.ts文件中找到BitmapFont类声明,在下面添加方法接口

/**
 * 通过指定位图字体文件路径,加载位图字体文件,加载完成后会自动解析。
 * @param path json路径
 * @param complete 完成回调
 */
loadFontByJson(path: string, complete: Handler): void;

至此我们所有需要修改的代码都修改完成了。外部调用直接调用loadFontByJson即可。

-结语-

在修改完下来,其实我们可以发现,Laya使用位图字体其实也是根据一张大纹理去索引uv去获取具体字的纹理数据。这跟图集实现基本是一样的,只是在外部调用设置Text会更加方便而已。
另外位图字体不能修改颜色(只能通过滤镜),改变字体大小(通过Scale修改大小)。追根溯源是因为这时候我们的字体已经不是渲染后的文字,是无法修改其属性,正常字体是拿着我们要修改的字体属性到Canvas绘图生成的,因此不能修改也是情有可原。因此想要使用位图字体的朋友们想清楚再使用。

任何一个小东西都蕴藏的无数的坑~~
原文链接:http://dengxuhui.cn/#html/blog/f02dea819604943226c8c7a247a8eb1a

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章