О чем этот пример
Работа со шрифтами — ключевой аспект создания визуально привлекательного интерфейса в играх. Phaser 3 предлагает мощную систему Bitmap Fonts, которая позволяет использовать предварительно отрисованные шрифты с полным контролем над внешним видом и производительностью. Этот подход особенно полезен для стилизованных заголовков, диалоговых окон и любого текста, который должен выглядеть идентично на всех устройствах. В этой статье мы разберем пример загрузки и отображения нескольких bitmap-шрифтов, а также коснемся важной темы кернинга — расстояния между символами, которое часто требует ручной настройки в таких шрифтах.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.bitmapFont('title', 'https://raw.githubusercontent.com/monteiz/phaser-3-example/main/title.png', 'https://raw.githubusercontent.com/monteiz/phaser-3-example/main/title.fnt');
this.load.bitmapFont('shortStack', 'assets/fonts/bitmap/shortStack.png', 'assets/fonts/bitmap/shortStack.fnt');
this.fonts = ['iceicebaby', 'atari-smooth', 'azo-fire', 'carrier_command', 'vermin', 'chiller', 'clarendon', 'desyrel-pink', 'gem', 'gothic', 'hyperdrive', 'topaz-fill']
for (let i = 0; i < this.fonts.length; i++)
{
this.load.bitmapFont(this.fonts[i], 'assets/fonts/bitmap/' + this.fonts[i] + '.png', 'assets/fonts/bitmap/' + this.fonts[i] + '.xml');
}
}
create ()
{
const fontSize = 40;
this.add.dynamicBitmapText(10, 10, 'title', 'YOU CAN WAIT!').setFontSize(fontSize);
this.add.dynamicBitmapText(10, 10 + fontSize, 'shortStack', 'YOU CAN WIN!').setFontSize(fontSize);
for (let i = 0; i < this.fonts.length; i++)
{
this.add.dynamicBitmapText(10, 50 + ((i+1) * fontSize), this.fonts[i], 'YOU CAN WAIT!').setFontSize(fontSize);
}
}
}
const config = {
// type: Phaser.CANVAS,
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Загрузка Bitmap Fonts: ключевой метод `load.bitmapFont`
Первый шаг — загрузить ресурсы шрифта в сцену. Phaser требует два файла: изображение (PNG), содержащее все символы (глифы), и файл с данными (обычно .fnt или .xml), описывающий положение каждого символа на этом изображении.
В методе preload() примера используется this.load.bitmapFont(). Он принимает три аргумента: ключ (уникальное имя для обращения к шрифту), URL изображения и URL файла с данными.
this.load.bitmapFont('title', 'https://raw.githubusercontent.com/monteiz/phaser-3-example/main/title.png', 'https://raw.githubusercontent.com/monteiz/phaser-3-example/main/title.fnt');
Для загрузки набора шрифтов из локальных ресурсов используется цикл. Массив this.fonts содержит ключи, а пути к файлам формируются по шаблону.
for (let i = 0; i < this.fonts.length; i++)
{
this.load.bitmapFont(this.fonts[i], 'assets/fonts/bitmap/' + this.fonts[i] + '.png', 'assets/fonts/bitmap/' + this.fonts[i] + '.xml');
}
Важно отметить, что Phaser корректно работает с обоими форматами данных (.fnt и .xml).
Создание и отображение текста: `add.dynamicBitmapText`
После загрузки шрифты можно использовать для создания текстовых объектов. В методе create() примера для этого применяется фабричный метод this.add.dynamicBitmapText().
Он создает динамический bitmap-текст, который, в отличие от статического, поддерживает изменение размера в рантайме через setFontSize(). Метод принимает четыре основных аргумента: координаты X и Y, ключ загруженного шрифта и строку для отображения.
this.add.dynamicBitmapText(10, 10, 'title', 'YOU CAN WAIT!').setFontSize(fontSize);
В примере используется константа fontSize для единообразного задания размера. Обратите внимание: изменение размера через setFontSize() масштабирует весь текстовый объект, а не перерисовывает глифы, что может привести к потере четкости при сильном увеличении.
Для отображения коллекции шрифтов координата Y каждого следующего объекта рассчитывается динамически, чтобы они не накладывались друг на друга.
this.add.dynamicBitmapText(10, 50 + ((i+1) * fontSize), this.fonts[i], 'YOU CAN WAIT!').setFontSize(fontSize);
Проблема кернинга и настройка расстояний
Bitmap-шрифты, особенно стилизованные, часто создаются вручную. Автоматический кернинг (оптическое расстояние между парами символов, например, 'AV' или 'To') в них может отсутствовать или работать некорректно. Это приводит к тому, что текст выглядит "рваным" — расстояния между буквами кажутся неравномерными.
В исходном коде примера (bugs/6631 bitmapfont kerning.js) как раз исследуется эта проблема. Phaser 3 пытается применить данные о кернинге, если они есть в файле описания шрифта (.fnt/.xml). Однако не все генераторы bitmap-шрифтов корректно их экспортируют, или данные могут быть неполными.
Проверить, загрузился ли кернинг для шрифта, можно, отобразив одну и ту же фразу (например, 'AVAST' или 'WAIT') разными шрифтами. Если расстояние между определенными буквами выглядит слишком большим или маленьким, проблема в кернинге.
Для тонкой настройки можно использовать метод setLetterSpacing() у объекта DynamicBitmapText, чтобы глобально увеличить или уменьшить расстояние между всеми символами.
const text = this.add.dynamicBitmapText(10, 10, 'title', 'YOU CAN WAIT!');
text.setFontSize(fontSize);
text.setLetterSpacing(2); // Добавляет 2 пикселя между всеми символами
Для более точной работы с проблемными парами символов может потребоваться редактирование исходного XML/FNT-файла шрифта в специализированном редакторе.
Конфигурация рендерера: WebGL vs Canvas
В конце примера находится конфигурационный объект игры. Обратите внимание на закомментированную строку // type: Phaser.CANVAS, и активную type: Phaser.WEBGL.
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
Использование WebGL-рендерера (Phaser.WEBGL) является предпочтительным для большинства современных проектов, так как он обеспечивает высокую производительность при отрисовке большого количества bitmap-объектов, включая текст. Рендеринг через Canvas (Phaser.CANVAS) может быть полезен для целевых платформ с ограниченной поддержкой WebGL или для упрощения отладки.
Bitmap-текст, созданный через add.dynamicBitmapText, корректно отображается в обоих режимах. Однако производительность анимации или трансформации множества таких объектов будет значительно выше в режиме WebGL.
Что попробовать дальше
Bitmap Fonts в Phaser 3 — это надежный инструмент для работы с нестандартной типографикой в играх. Они гарантируют идентичное отображение на всех платформах и не зависят от наличия системных шрифтов. Главные моменты для успешного использования: корректная загрузка пар файлов (изображение + данные), понимание разницы между статическим и динамическим текстом и внимательная проверка кернинга.
Для экспериментов попробуйте:
1. Анимировать размер шрифта с помощью setFontSize() в цикле обновления.
2. Применить setLetterSpacing к разным шрифтам из примера и оценить визуальный эффект.
3. Создать собственный bitmap-шрифт с помощью бесплатных онлайн-инструментов (например, Littera) и загрузить его в свой проект, специально настроив кернинг для часто используемых в игре слов.
