mcpField
T
text-and-bitmaptext

text-and-bitmaptext

github.com/phaserjs开发40.6k841 次安装10 周前更新
代码仓库

关于 text-and-bitmaptext

Phaser 4 文本与 BitmapText 使用指南,面向制作游戏界面的开发者。说明文本样式、换行和对齐,以及静态与动态位图文本的适用方式。

主要功能

  1. 01配置 Canvas 文本样式
  2. 02设置换行、对齐与边距
  3. 03使用静态和动态 BitmapText
  4. 04查询文本边界与字符信息

适用场景

  1. 01制作带样式的游戏标签和标题
  2. 02显示频繁更新的分数或计时器
  3. 03为字符添加逐字动画效果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/phaserjs/phaser --skill text-and-bitmaptext

安装后在 Claude Code 中输入/text-and-bitmaptext确认已加载。

详细介绍

文本与 BitmapText

在 Phaser 4 中显示文本 —— 基于 Canvas 的 Text 游戏对象、TextStyle 配置、自动换行、BitmapText(静态与动态)、复古字体、文本对齐、文本边界以及内边距。

关键源码路径: src/gameobjects/text/、src/gameobjects/bitmaptext/static/、src/gameobjects/bitmaptext/dynamic/ 相关技能: ../loading-assets/SKILL.md、../sprites-and-images/SKILL.md、../game-object-components/SKILL.md

快速开始

// Canvas-based Text (flexible styling, uses browser fonts)
const title = this.add.text(400, 50, 'Hello World', {
    fontFamily: 'Arial',
    fontSize: '32px',
    color: '#ffffff'
});

// BitmapText (pre-rendered font texture, faster rendering)
// Font must be loaded first: this.load.bitmapFont('myFont', 'font.png', 'font.xml')
const score = this.add.bitmapText(400, 100, 'myFont', 'Score: 0', 32);

// DynamicBitmapText (per-character manipulation via callback)
const fancy = this.add.dynamicBitmapText(400, 200, 'myFont', 'Wavy!', 32);
fancy.setDisplayCallback(function (data) {
    data.y += Math.sin(data.index * 0.5 + fancy.scene.time.now * 0.005) * 10;
    return data;
});

核心概念

Text 与 BitmapText 对比
特性TextBitmapTextDynamicBitmapText
渲染方式Canvas 2D API,作为纹理上传预渲染字体纹理预渲染字体纹理
Web/CSS 字体是否否
阴影、渐变、描边是(Canvas API)仅投影(WebGL)否
自动换行内置(width、callback、高级)setMaxWidthsetMaxWidth
逐字符效果否setCharacterTint、setWordTint(WebGL)setDisplayCallback
性能较慢(变更时重新创建 canvas 纹理)快(批量渲染)中等(逐字符回调)
对齐left、right、center、justifyALIGN_LEFT(0)、ALIGN_CENTER(1)、ALIGN_RIGHT(2)与 BitmapText 相同
RTL 支持是(rtl 样式选项)否否

经验法则: 对于需要灵活样式、Web 字体或更新不频繁的 UI 元素,使用 Text。对于分数、计时器以及任何更新频繁或大量渲染的内容,使用 BitmapText。仅当需要逐字符动画效果时,才使用 DynamicBitmapText。

TextStyle

Text 游戏对象将所有样式委托给其 TextStyle 实例,可通过 text.style 访问。创建 Text 时可传入样式配置对象,之后也可用 setter 方法更新。样式对象内部使用 Canvas 2D 属性。

fontFamily 默认为 'Courier',fontSize 默认为 '16px',color 默认为 '#fff'。font 简写属性(例如 'bold 24px Arial')在提供时会覆盖 fontFamily、fontSize 和 fontStyle。

常见模式

创建基础文本
// Simple text with inline style
const label = this.add.text(100, 100, 'Player 1', {
    fontFamily: 'Georgia',
    fontSize: '24px',
    color: '#00ff00'
});

// Using the font shorthand (sets fontStyle, fontSize, fontFamily)
const bold = this.add.text(100, 150, 'Bold Text', {
    font: 'bold 28px Arial'
});

// Array of strings creates multi-line text
const multi = this.add.text(100, 200, ['Line 1', 'Line 2', 'Line 3'], {
    fontFamily: 'Arial',
    fontSize: '18px',
    color: '#ffffff'
});

// Text origin defaults to (0, 0) -- top-left corner
label.setOrigin(0.5); // center the text on its position
设置文本样式
const text = this.add.text(400, 300, 'Styled', {
    fontFamily: 'Verdana',
    fontSize: '48px',
    color: '#ff0000',
    stroke: '#000000',
    strokeThickness: 4,
    backgroundColor: '#333333',
    shadow: { offsetX: 2, offsetY: 2, color: '#000', blur: 4, stroke: false, fill: true }
});

// Modify style after creation (all return `this` for chaining)
text.setColor('#00ffff');
text.setFontSize(64);
text.setFontFamily('Courier');
text.setFontStyle('italic');
text.setStroke('#ff00ff', 6);
text.setShadow(3, 3, '#000', 5, false, true);
text.setBackgroundColor('#222');

// Replace entire style at once
text.setStyle({ fontSize: '64px', fontFamily: 'Arial', color: '#ffffff', align: 'center' });

// Canvas gradients and patterns work as fill/stroke
const gradient = text.context.createLinearGradient(0, 0, 0, text.height);
gradient.addColorStop(0, '#ff0000');
gradient.addColorStop(1, '#0000ff');
text.setFill(gradient);
自动换行
// Basic word wrap by pixel width
const wrapped = this.add.text(50, 50, 'Long text here...', {
    fontFamily: 'Arial',
    fontSize: '20px',
    color: '#fff',
    wordWrap: { width: 300 }
});

// Or set after creation
wrapped.setWordWrapWidth(300);

// Advanced wrap (collapses spaces, trims whitespace)
wrapped.setWordWrapWidth(300, true);

// Custom word wrap callback
wrapped.setWordWrapCallback(function (text, textObject) {
    // Return wrapped text as string with \n or array of lines
    return text.split(' ').join('\n');
});

// Get wrapped lines as array
const lines = wrapped.getWrappedText();
文本对齐
// Alignment only affects multi-line text: 'left' (default), 'right', 'center', 'justify'
const aligned = this.add.text(400, 100, 'Line 1\nLonger Line 2\nLine 3', {
    fontFamily: 'Arial', fontSize: '24px', color: '#fff', align: 'center'
});

aligned.setAlign('right');

// For center alignment to look correct, combine with fixedWidth:
aligned.setFixedSize(300, 0);
aligned.setAlign('center');
动态更新文本
const scoreText = this.add.text(10, 10, 'Score: 0', {
    fontFamily: 'Arial',
    fontSize: '24px',
    color: '#fff'
});

// setText replaces content (accepts string or string[])
scoreText.setText('Score: 100');
scoreText.setText(['Score: 100', 'Lives: 3']); // joins with \n

// appendText adds to existing content
scoreText.appendText('Extra line');          // prepends \n by default
scoreText.appendText(' more', false);        // no carriage return

// Read current text
const current = scoreText.text;
文本内边距
// Padding adds space around text inside the canvas
const padded = this.add.text(100, 100, 'Padded', {
    fontFamily: 'Arial', fontSize: '24px', color: '#fff',
    backgroundColor: '#333',
    padding: { left: 10, right: 10, top: 5, bottom: 5 }
});

padded.setPadding({ x: 10, y: 5 });       // shorthand: x=left+right, y=top+bottom
padded.setPadding(10, 5, 10, 5);           // left, top, right, bottom
padded.setPadding(10);                     // single value = all sides
固定尺寸、最大行数与间距
// Fixed dimensions create a text canvas of exact size
const fixed = this.add.text(100, 100, 'Fixed box', {
    fontFamily: 'Arial', fontSize: '18px', color: '#fff',
    fixedWidth: 200, fixedHeight: 100,
    wordWrap: { width: 200 }, align: 'center'
});
fixed.setMaxLines(3); // limit displayed lines

// Line and letter spacing (in style config or via setters)
const spaced = this.add.text(100, 200, 'Spaced\nLines', {
    fontFamily: 'Arial', fontSize: '24px', color: '#fff',
    lineSpacing: 10, letterSpacing: 2
});
spaced.setLineSpacing(20);
spaced.setLetterSpacing(5);
创建 BitmapText
// Load in preload: this.load.bitmapFont('pixelFont', 'font.png', 'font.xml');

// Static BitmapText -- fast, batched rendering
const bmpText = this.add.bitmapText(100, 100, 'pixelFont', 'Hello', 32);

// With alignment (for multi-line)
const aligned = this.add.bitmapText(100, 200, 'pixelFont', 'Line 1\nLine 2', 24, 1);
// align: 0 = left, 1 = center, 2 = right

// Convenience alignment methods
bmpText.setLeftAlign();
bmpText.setCenterAlign();
bmpText.setRightAlign();

// Modify text
bmpText.setText('Updated!');
bmpText.text = 'Also works';

// Font size
bmpText.setFontSize(48);
bmpText.fontSize = 48;

// Spacing
bmpText.setLetterSpacing(2);
bmpText.setLineSpacing(5);

// Word wrap by max pixel width
bmpText.setMaxWidth(200);
BitmapText 投影与着色
// Drop shadow (WebGL only, static BitmapText only)
bmpText.setDropShadow(2, 2, 0x000000, 0.5);
// Clear shadow
bmpText.setDropShadow();

// Tint individual characters (WebGL only)
bmpText.setCharacterTint(0, 5, Phaser.TintModes.MULTIPLY, 0xff0000);
// start index, length (-1 for all from start), tintMode, color

// Tint by word (string match or word index)
bmpText.setWordTint('Score', -1, Phaser.TintModes.MULTIPLY, 0x00ff00);
DynamicBitmapText
const dynamic = this.add.dynamicBitmapText(100, 100, 'pixelFont', 'Dynamic!', 32);

// Per-character display callback -- invoked each render frame per character
dynamic.setDisplayCallback(function (data) {
    // data properties: parent, tint, index, charCode, x, y, scale, rotation, data
    data.x += Math.sin(data.index + dynamic.scene.time.now * 0.01) * 5;
    data.y += Math.cos(data.index + dynamic.scene.time.now * 0.01) * 5;
    return data;
});

// Scrolling text window
dynamic.setSize(200, 50);      // crop region in pixels
dynamic.setScrollX(10);
dynamic.setScrollY(0);
复古字体

复古字体使用标准图像中的固定宽度字符网格(无需 XML/JSON 字体文件)。

// In preload -- load the image containing the font characters
this.load.image('retroFont', 'retroFont.png');

// In create -- parse the retro font configuration
const config = {
    image: 'retroFont',
    width: 8,           // character width in pixels
    height: 8,          // character height in pixels
    chars: Phaser.GameObjects.RetroFont.TEXT_SET1,
    charsPerRow: 16,
    offset: { x: 0, y: 0 },
    spacing: { x: 0, y: 0 },
    lineSpacing: 0
};

// Parse adds it to the BitmapFont cache
this.cache.bitmapFont.add('retroFont', { data: Phaser.GameObjects.RetroFont.Parse(this, config) });

// Now use it like any BitmapText
const retro = this.add.bitmapText(100, 100, 'retroFont', 'RETRO TEXT', 8);

可用的 Phaser.GameObjects.RetroFont TEXT_SET 常量:TEXT_SET1(完整 ASCII 可打印字符)、TEXT_SET2(空格到 Z)、TEXT_SET3-TEXT_SET11(大写字母、数字、标点的各种子集)。TEXT_SET10 仅包含大写字母。

文本边界(BitmapText)
// getTextBounds returns local, global, line, word, and character data
const bounds = bmpText.getTextBounds();
// bounds.local       -- { x, y, width, height } at origin 0,0
// bounds.global      -- { x, y, width, height } with scale + world position
// bounds.lines       -- per-line { x, y, width, height }
// bounds.words       -- word objects with positions
// bounds.characters  -- character objects with positions

// width/height are read-only properties (computed from global bounds)
console.log(bmpText.width, bmpText.height);

// Get character at world position (useful for click detection)
const char = bmpText.getCharacterAt(pointer.worldX, pointer.worldY);
分辨率(高 DPI)
// Higher resolution for crisp text on HiDPI displays (costs more memory)
const crisp = this.add.text(100, 100, 'Sharp!', {
    fontFamily: 'Arial', fontSize: '24px', color: '#fff', resolution: 2
});
crisp.setResolution(window.devicePixelRatio); // change after creation

配置参考

TextStyle 属性
属性类型默认值描述
fontFamilystring'Courier'CSS 字体族
fontSizestring/number'16px'字体大小(数字会自动追加 'px')
fontStylestring''CSS 字体样式('bold'、'italic'、'bold italic')
fontstring-简写:'bold 24px Arial'(覆盖字体族/大小/样式)
colorstring/CanvasGradient/CanvasPattern'#fff'填充颜色
strokestring/CanvasGradient/CanvasPattern'#fff'描边颜色
strokeThicknessnumber0描边宽度(0 = 不描边)
backgroundColorstringnull纯色背景颜色
alignstring'left'多行对齐方式:'left'、'right'、'center'、'justify'
maxLinesnumber0最大渲染行数(0 = 不限)
fixedWidthnumber0固定画布宽度(0 = 自动)
fixedHeightnumber0固定画布高度(0 = 自动)
resolutionnumber0画布分辨率(0 = 使用游戏配置)
rtlbooleanfalse从右到左渲染
baselineXnumber1.2字体度量的水平内边距
baselineYnumber1.4字体度量的垂直内边距
testStringstring'|MEqgy'用于测量字体高度的字符串
wordWrapobject-{ width, callback, callbackScope, useAdvancedWrap }
shadowobject-{ offsetX, offsetY, color, blur, stroke, fill }
paddingobject-{ left, right, top, bottom } 或 { x, y }
lineSpacingnumber0行与行之间的额外垂直间距
letterSpacingnumber0字符与字符之间的额外水平间距
metricsobject-预先计算的 { ascent, descent, fontSize },用于跳过测量

工厂方法

// Text -- Canvas-based
this.add.text(x, y, text, style);
// x, y: number; text: string | string[]; style: TextStyle (optional)
// Returns: Phaser.GameObjects.Text

// BitmapText -- static, fast
this.add.bitmapText(x, y, font, text, size, align);
// x, y: number; font: string (cache key); text: string | string[] (optional)
// size: number (optional, defaults to font data size); align: number (optional, 0)
// Returns: Phaser.GameObjects.BitmapText

// DynamicBitmapText -- per-character callback
this.add.dynamicBitmapText(x, y, font, text, size);
// Same as bitmapText but returns DynamicBitmapText
// Returns: Phaser.GameObjects.DynamicBitmapText

API 快速参考

Text 方法

setText(value)、appendText(value, addCR?)、setStyle(style)、setFont(font)、setFontFamily(family)、setFontSize(size)、setFontStyle(style)、setColor(color)、setFill(color)、setStroke(color, thickness)、setShadow(x, y, color, blur, stroke, fill)、setBackgroundColor(color)、setWordWrapWidth(width, useAdvanced?)、setWordWrapCallback(callback, scope?)、setAlign(align)、setPadding(left, top?, right?, bottom?)、setFixedSize(width, height)、setMaxLines(max)、setLineSpacing(value)、setLetterSpacing(value)、setResolution(value)、setRTL(rtl?)、getWrappedText(text?)、updateText()

所有 setter 均返回 this 以支持链式调用。属性:text(可读/可写)、style(TextStyle 实例)、padding、lineSpacing、letterSpacing、autoRound、splitRegExp。

BitmapText 方法

setText(value)、setFont(font, size?, align?)、setFontSize(size)、setLetterSpacing(spacing?)、setLineSpacing(spacing?)、setMaxWidth(value, wordWrapCharCode?)、setLeftAlign()、setCenterAlign()、setRightAlign()、setDropShadow(x?, y?, color?, alpha?)、setCharacterTint(start?, length?, tintMode?, tl?, tr?, bl?, br?)、setWordTint(word, count?, tintMode?, tl?, tr?, bl?, br?)、getTextBounds(round?)、getCharacterAt(x, y, camera?)

属性(可读/可写):text、fontSize、letterSpacing、lineSpacing、align(数字)、maxWidth。只读:width、height、font、fontData。

对齐常量:BitmapText.ALIGN_LEFT(0)、BitmapText.ALIGN_CENTER(1)、BitmapText.ALIGN_RIGHT(2)。

DynamicBitmapText 方法(额外)

setDisplayCallback(callback)、setSize(width, height)、setScrollX(value)、setScrollY(value)

属性:scrollX、scrollY、cropWidth、cropHeight、displayCallback、callbackData。

注意事项

  1. 每次更改都会重新渲染文本。 每次调用 setText、setStyle、setColor 等都会重新创建内部 canvas,并且(在 WebGL 中)重新上传纹理。请批量进行样式更改,并只调用一次 updateText(),或者直接设置样式对象的属性,最后再调用 updateText()。

  2. 字体必须在使用前加载。 Text 对象使用 Canvas 的 fillText API,因此字体必须在浏览器中可用(通过 CSS、Web 字体加载器加载,或已安装)。Phaser 不会加载 Web 字体——请使用第三方加载器或 CSS @font-face。

  3. 带有特殊字符的字体名称需要加引号。 包含数字或特殊字符的字体名称必须在字符串内使用双引号:fontFamily: '"Press Start 2P"'。

  4. 文本原点默认为 (0, 0)。 与默认原点为 (0.5, 0.5) 的 Sprite 不同,Text 对象默认原点为左上角。调用 setOrigin(0.5) 可居中。

  5. 对齐只影响多行文本。 align: 'center' 对单行文本没有任何作用。要居中单行文本,请使用 setOrigin(0.5) 或手动定位。

  6. BitmapText 字体必须在缓存中。 传递给 this.add.bitmapText() 的字体键必须与通过 this.load.bitmapFont() 加载的键匹配。如果缺失,你会看到控制台警告和未定义行为。

  7. BitmapText 的 setMaxWidth 只会在空白字符处换行。 如果未找到空白字符(默认字符编码 32 = 空格),则不会发生换行。可以通过 setMaxWidth 的第二个参数更改换行字符。

  8. DynamicBitmapText 开销更大。 显示回调会每帧对每个字符运行。仅在你需要逐字符操作时使用它。

  9. setCharacterTint 和 setWordTint 仅适用于 WebGL。 这些在 Canvas 渲染器中没有任何效果。

  10. setDropShadow 仅适用于 WebGL 和静态 BitmapText。 它不适用于 DynamicBitmapText 或 Canvas 渲染器。

  11. Text 上的 letterSpacing 开销很大。 当它非零时,Phaser 会使用 fillText 逐个渲染每个字符,而不是一次渲染整行。对于带有自定义字母间距的大量文本,请使用 BitmapText。

  12. setFill 和 setColor 是等价的。 两者都会设置 TextStyle 上的 color 属性。样式配置也接受 fill 作为 color 的别名。

  13. 必须在 setText 之前调用 setRTL。 从右到左模式会改变内部 canvas 的构建方式。如果你在设置文本之后调用 setRTL(true),文本可能无法正确渲染。请在样式配置中设置 RTL,或在任何 setText() 调用之前调用 setRTL()。

  14. 对于频繁更新的内容,BitmapText 要快得多。 Text 会在每次更改时重新渲染整个 Canvas 并重新上传 GPU 纹理。BitmapText 只是更新现有纹理图集中的顶点数据。对于分数、计时器、伤害数字,或任何每帧都会变化的文本,请始终优先使用 BitmapText。

源文件映射

文件描述
src/gameobjects/text/Text.jsText 类(基于 Canvas 的渲染)
src/gameobjects/text/TextFactory.jsthis.add.text() 工厂方法
src/gameobjects/text/TextStyle.jsTextStyle 类(所有样式属性 + setter)
src/gameobjects/text/GetTextSize.js内部文本测量
src/gameobjects/text/MeasureText.js字体度量测量
src/gameobjects/text/typedefs/TextStyle、TextWordWrap、TextPadding、TextShadow 类型定义
src/gameobjects/bitmaptext/static/BitmapText.js静态 BitmapText 类
src/gameobjects/bitmaptext/static/BitmapTextFactory.jsthis.add.bitmapText() 工厂方法
src/gameobjects/bitmaptext/dynamic/DynamicBitmapText.jsDynamicBitmapText(继承自 BitmapText)
src/gameobjects/bitmaptext/dynamic/DynamicBitmapTextFactory.jsthis.add.dynamicBitmapText() 工厂方法
src/gameobjects/bitmaptext/ParseRetroFont.js复古字体解析器(RetroFont.Parse)
src/gameobjects/bitmaptext/RetroFont.jsRetroFont 命名空间(Parse + TEXT_SET 常量)
src/gameobjects/bitmaptext/const.jsTEXT_SET1 至 TEXT_SET11 常量
src/gameobjects/bitmaptext/GetBitmapTextSize.jsBitmapText 边界计算
src/gameobjects/bitmaptext/typedefs/RetroFontConfig、DisplayCallbackConfig、BitmapTextSize 类型定义

评论

支持 Markdown