Phaser 3.60 BitmapText 增强详解:行距控制、显示尺寸、边界组件与字距(Kerning)修复
2026/9/19 4:36:36 网站建设 项目流程

Phaser 3.60 BitmapText 增强详解:行距控制、显示尺寸、边界组件与字距(Kerning)修复

【免费下载链接】phaserPhaser is a fun, free and fast 2D game framework for making HTML5 games for desktop and mobile web browsers, supporting Canvas and WebGL rendering.项目地址: https://gitcode.com/gh_mirrors/ph/phaser

本文以 Phaser 3.60.0 变更日志中 BitmapTextGameObject.md 的内容为骨架,系统讲解该版本为BitmapText游戏对象带来的四项核心变化:新增setLineSpacing行距控制、新增只读属性displayWidth/displayHeight并接入GetBounds组件、GetBitmapTextSize结果中新增字符索引idx,以及字距(kerning)偏移在 WebGL 与 Canvas 两种渲染器下的修复。读完本文,你将能够利用行距精确排版多行位图字体文本,正确获取 BitmapText 的尺寸与边界(包括放入Container之后的场景),并理解 kerning 从字体解析、尺寸计算到逐字符渲染的完整链路。

一、3.60 中 BitmapText 更新总览

在 changelog/v3/3.60/BitmapTextGameObject.md 中,Phaser 3.60.0 对BitmapText的变更可分为三类:

类别变更内容说明
新特性BitmapText.setLineSpacing方法及lineSpacing属性控制多行位图文本的垂直行距,可正可负,用法与Text对象一致
更新新增只读属性displayWidthdisplayHeight使 BitmapText 能正确配合GetBounds组件使用
更新BitmapText 接入GetBounds组件修复 #6237,可在Container中正确获取尺寸
更新GetBitmapTextSize结果新增idx字段字符在原始文本中的索引,不受自动换行影响
修复逐字符 kerning 偏移渲染时正确应用字距偏移
修复GetBitmapTextSize中的 kerning 计算WebGL 与 Canvas 渲染结果一致

下文逐一展开每个变更的用法、源码实现与验证方式。

二、行距控制:setLineSpacing 与 lineSpacing

2.1 API 形态

3.60 之前,多行 BitmapText 的行与行之间只能使用字体文件自身定义的lineHeight,间距不可调整。3.60 起可以通过两种方式控制:

// 方式一:链式方法(默认值 0) bitmapText.setLineSpacing(8); // 方式二:直接读写属性 bitmapText.lineSpacing = 8;

关键约定:

  • 该值会叠加到字体本身的 lineHeight 之上,用于计算整体行高;
  • 可正可负:正值加大行距,负值收紧行距;
  • 仅对多行文本(以\r\n\r\n分隔)生效,单行文本没有影响;
  • 传入undefined时默认重置为0

2.2 源码实现

方法定义位于 src/gameobjects/bitmaptext/static/BitmapText.js:

setLineSpacing: function (spacing) { if (spacing === undefined) { spacing = 0; } this.lineSpacing = spacing; return this; },

内部通过lineSpacing属性(同一文件)写入私有字段_lineSpacing,并将_dirty标记为true,从而触发后续尺寸重算:

lineSpacing: { set: function (value) { this._lineSpacing = value; this._dirty = true; }, get: function () { return this._lineSpacing; } },

_lineSpacing字段本身从 3.60.0 开始引入(src/gameobjects/bitmaptext/static/BitmapText.js)。在序列化方法toJSON中,lineSpacing也会随fonttextfontSizeletterSpacingalign一起被导出(同一文件),便于场景数据持久化。

2.3 行距在尺寸计算中的生效点

行距真正参与排版的位置在 src/gameobjects/bitmaptext/GetBitmapTextSize.js。当遍历到换行符(charCode 10)时,通过以下公式计算下一行文字的 y 偏移:

yAdvance = (lineHeight + lineSpacing) * currentLine;

也就是说,第 N 行文字相对顶部的偏移是(lineHeight + lineSpacing) × N。这也解释了为什么行距对单行文本无影响——只有出现第二个换行符时currentLine才会递增。

2.4 实战示例

this.add.bitmapText(100, 100, 'desyrel', 'First Line\nSecond Line\nThird Line') .setLineSpacing(12); // 三行文本,行距加大 12 像素

将行距设为负值可让多行文本更加紧凑:

bitmapText.setLineSpacing(-4); // 行距收紧 4 像素

三、displayWidth / displayHeight 与 GetBounds 组件

3.1 背景:Issue #6237

3.60 之前,BitmapText 在放入Container后,Container无法正确计算其边界尺寸,因为 BitmapText 缺少GetBounds组件所依赖的displayWidth/displayHeight属性。3.60 为 BitmapText 补齐了这两个只读属性并接入GetBounds组件(src/gameobjects/bitmaptext/static/BitmapText.js 的 Mixins 列表包含Components.GetBounds)。

3.2 属性语义

displayWidth(源码)与displayHeight(源码)均为只读属性:

  • 返回考虑了缩放因子的显示尺寸(内部委托给width/height的取值器,它们基于getTextBounds计算出的global尺寸);
  • 允许赋值:setter 会先将 X/Y 方向缩放重置为 1,再根据目标尺寸反算scaleX/scaleY,等价于“以目标显示尺寸反向设定缩放”;
  • 文档注释明确标注@readonly,推荐通过setDisplaySize(3.61+)或直接设置scale来控制显示大小。

3.3 GetBounds 组件提供的能力

接入后,BitmapText 拥有了 src/gameobjects/components/GetBounds.js 提供的一整套边界查询方法:

  • getBounds():返回轴对齐包围矩形(AABB),会考虑旋转与父Container变换;
  • getTopLeft()/getTopRight()/getBottomLeft()/getBottomRight():四角坐标;
  • getCenter()/getTopCenter()/getLeftCenter()等:中心与边中点;
  • 所有方法都支持includeParent参数,以纳入父Container的变换矩阵。

这些方法在内部均依赖this.displayWidththis.displayHeight计算(参见 GetBounds.js 的getCenter实现),这正是新增两个属性的意义所在。

3.4 实战示例

const label = this.add.bitmapText(200, 150, 'desyrel', 'Score: 100'); // 直接读取显示尺寸(已包含 scale) console.log(label.displayWidth, label.displayHeight); // 获取边界矩形,用于碰撞检测或布局 const rect = label.getBounds(); console.log(rect.x, rect.y, rect.width, rect.height); // 放入 Container 后依然能正确计算 const group = this.add.container(0, 0, [label]); const boundsInGroup = label.getBounds(); // 已计入父容器变换

四、GetBitmapTextSize 新增字符索引 idx

4.1 背景

GetBitmapTextSize在计算尺寸时,会把超长文本按maxWidth自动换行(src/gameobjects/bitmaptext/GetBitmapTextSize.js)。换行会向文本中插入\n,导致“换行后的字符下标”与“原始文本中的字符下标”不一致。原有i字段表示的是换行处理后的索引,无法定位回原始字符串。

4.2 idx 字段

3.60 起,characters数组中的每个BitmapTextCharacter对象新增idx字段(src/gameobjects/bitmaptext/GetBitmapTextSize.js):

characters.push({ i: charIndex, // 换行后的索引(原有) idx: i, // 原始文本中的索引(3.60 新增) char: text[i], code: charCode, // ... });

类型定义位于 src/gameobjects/bitmaptext/typedefs/BitmapTextCharacter.js:

  • i:该字符在换行后文本中的索引;
  • idx:该字符在原始文本(不包含自动换行)中的索引。

4.3 使用场景

maxWidth触发自动换行时,idx可用来把字符级信息(如逐字染色、命中检测)映射回用户输入的原字符串:

const bounds = bitmapText.getTextBounds(); bounds.characters.forEach((char) => { // char.idx 指向原始文本下标,不受换行影响 console.log(char.idx, char.char, char.x, char.y); });

注意:BitmapTextCharacterx/y已按字号缩放,但尚未换算为游戏对象本地坐标,w/h为字形尺寸,t/r/b分别表示所在行的顶部、字符右沿与行底部(typedefs 说明)。

五、字距(Kerning)修复:从解析到渲染

5.1 问题

3.60 修复了两个 kerning 相关问题:

  1. 渲染阶段:BitmapText 渲染时没有应用逐字符的 kerning 偏移;
  2. 尺寸计算阶段GetBitmapTextSize中的 kerning 计算有误,导致文本尺寸与实际绘制不一致。

修复后,WebGL 与 Canvas 两种渲染器的表现统一。

5.2 字体解析:kerning 数据的来源

kerning 数据来自位图字体 XML 的<kerning>节点,解析逻辑在 src/gameobjects/bitmaptext/ParseXMLBitmapFont.js:

var kernings = xml.getElementsByTagName('kerning'); for (i = 0; i < kernings.length; i++) { var kern = kernings[i]; var first = getValue(kern, 'first'); var second = getValue(kern, 'second'); var amount = getValue(kern, 'amount'); data.chars[second].kerning[first] = amount; }

每个字形对象持有kerning映射表(键为前一个字符的 charCode),在构造字形时初始化为空对象(ParseXMLBitmapFont.js)。

5.3 尺寸计算:kerning 的正确应用

GetBitmapTextSize遍历字符时,会查询当前字形相对上一个字符的 kerning 偏移(src/gameobjects/bitmaptext/GetBitmapTextSize.js):

if (lastGlyph !== null) { var kerningOffset = glyph.kerning[lastCharCode]; x += (kerningOffset !== undefined) ? kerningOffset : 0; }

该偏移同时参与字符宽度计算与 x 方向推进(GetBitmapTextSize.js):

var charWidth = glyph.xOffset + glyph.xAdvance + ((kerningOffset !== undefined) ? kerningOffset : 0); // ... xAdvance += glyph.xAdvance + letterSpacing + ((kerningOffset !== undefined) ? kerningOffset : 0);

5.4 渲染:BatchChar 逐字符提交

WebGL 渲染路径中,每个字符通过 src/gameobjects/bitmaptext/BatchChar.js 提交为四边形:

var x = (char.x - src.displayOriginX) + offsetX; var y = (char.y - src.displayOriginY) + offsetY; var xw = x + char.w; var yh = y + char.h;

由于char.x已在尺寸计算阶段(5.3 节)计入 kerning 偏移,渲染时直接使用该位置即可,从而保证“量出来”的尺寸与“画出来”的结果一致。Canvas 渲染器同理,统一消费GetBitmapTextSize的输出。

5.5 验证:测试用例

仓库测试 tests/gameobjects/bitmaptext/GetBitmapTextSize.test.js 中包含对 kerning 的精确断言(describe('kerning')分组):

  • 字形 B 声明相对 A(charCode 65)的 kerning 偏移为-2
  • 期望'AB'的整体宽度为18(而非无 kerning 时的20),且字符 B 的x8

同一测试文件还覆盖了 3.60 新增特性的相关行为:

  • lineSpacing生效:'A\nB'lineSpacing=4时,第二行 y 偏移为(16 + 4) × 1 = 20,整体高度为36describe('multi-line text'));
  • maxWidth自动换行写入wrappedTextdescribe('word wrap'));
  • roundupdateOriginletterSpacing、对齐等既有行为回归(describe('letter spacing')describe('alignment')等)。

六、升级与使用建议

  • 行距排版:3.60 起,多行 BitmapText 的垂直节奏可以像Text一样自由控制,优先使用setLineSpacing而不是手动拼接带空行的字符串;
  • 布局与碰撞:新接入的GetBounds组件让 BitmapText 在Container中也能得到正确边界,涉及 UI 面板、对话框、按钮文字对齐时可直接使用getBounds()/getCenter()等方法;
  • 字符级操作:如果需要把字符坐标映射回原始输入字符串(例如逐字描边、点击判字),优先使用characters[].idx而非i
  • 字体文件:kerning 修复后,携带<kerning>节点的 BMFont 类字体在两种渲染器下表现一致;若发现个别字符间距异常,可从字体 XML 的first/second/amount节点数据入手排查。

完整的 3.60.0 变更汇总可查阅 changelog/v3/3.60/CHANGELOG-v3.60.md。

【免费下载链接】phaserPhaser is a fun, free and fast 2D game framework for making HTML5 games for desktop and mobile web browsers, supporting Canvas and WebGL rendering.项目地址: https://gitcode.com/gh_mirrors/ph/phaser

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询