-前言-
依照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