О чем этот пример
Динамический Bitmap Text в Phaser 3 — это мощный инструмент для создания стилизованных текстовых эффектов, которые были неотъемлемой частью классических игр. В отличие от обычного текста, он использует растровые изображения для отображения символов, что даёт полный контроль над пиксельным стилем и производительностью. Эта статья покажет, как создать эффект плавно прокручивающегося текста, имитирующий знаменитые титры из старых игр, используя `DynamicBitmapText` и группу объектов для управления множеством строк одновременно. Вы научитесь загружать и конфигурировать пользовательский ретро-шрифт, настраивать его параметры и анимировать с помощью простой физики.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('171', 'assets/fonts/retro/171.png');
this.load.image('rain', 'assets/pics/shadow-of-the-beast2-karamoon.png');
this.load.image('contra', 'assets/pics/contra2.png');
}
create ()
{
this.add.image(0, 0, 'rain').setOrigin(0).setScale(2);
const config = {
image: '171',
width: 16,
height: 18,
chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ| 0123456789*#!@:.,\\?-+=^$£()\'',
charsPerRow: 19,
spacing: { x: 0, y: 1 }
};
this.cache.bitmapFont.add('171', Phaser.GameObjects.RetroFont.Parse(this, config));
this.scrollers = this.add.group();
for (let i = 0; i < 20; i++)
{
const t = this.add.dynamicBitmapText(0, i * 40, '171', ' PHASER 3 IS IN THE HOUSE ... WELCOME TO THIS BITMAP TEXT SCROLLER DEMO, SHOWING OFF A NICE NEW FEATURE!!! AND WRAPP........');
t.setSize(640, 18);
t.setScale(2);
this.scrollers.add(t);
}
this.add.image(640, 800, 'contra').setOrigin(0.5, 1).setScale(2);
}
update (time, delta)
{
this.scrollers.children.forEach(function (child, index) {
child.scrollX += 2.5 + Math.sin((0.01 * index) * delta);
if (child.scrollX > 2800)
{
child.scrollX = -200;
}
});
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
pixelArt: true,
width: 640 * 2,
height: 400 * 2,
scene: Example
};
const game = new Phaser.Game(config);
Загрузка ресурсов и настройка шрифта
Перед созданием скроллера необходимо загрузить все необходимые ресурсы: фоновое изображение, спрайт для декора и, что самое важное, таблицу символов (spritesheet) для нашего растрового шрифта.
В методе preload мы загружаем три изображения. Ключевым является файл '171' — это PNG-изображение, содержащее все символы нашего шрифта в виде сетки.
this.load.image('171', 'assets/fonts/retro/171.png');
this.load.image('rain', 'assets/pics/shadow-of-the-beast2-karamoon.png');
this.load.image('contra', 'assets/pics/contra2.png');
Создание растрового шрифта происходит в create. Мы определяем конфигурационный объект, который описывает, как Phaser должен интерпретировать загруженную таблицу символов. Параметр chars задаёт строку, где каждый символ соответствует позиции в таблице.
const config = {
image: '171', // Ключ загруженного изображения
width: 16, // Ширина одного символа в пикселях
height: 18, // Высота одного символа в пикселях
chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ| 0123456789*#!@:.,\\?-+=^$£()\'', // Символы по порядку
charsPerRow: 19, // Количество символов в одной строке изображения
spacing: { x: 0, y: 1 } // Отступы между символами при рендеринге
};
Метод Phaser.GameObjects.RetroFont.Parse анализирует изображение на основе конфигурации и создаёт данные шрифта, которые затем сохраняются в кеш под ключом '171'. Теперь этот шрифт можно использовать для создания объектов BitmapText.
this.cache.bitmapFont.add('171', Phaser.GameObjects.RetroFont.Parse(this, config));
Создание группы текстовых объектов
Для создания эффекта множественных параллельных строк текста мы используем Group. Это позволяет легко управлять коллекцией похожих игровых объектов.
Сначала создаём группу scrollers, которая будет хранить все наши текстовые объекты.
this.scrollers = this.add.group();
Затем в цикле создаём 20 объектов DynamicBitmapText. Ключевые параметры конструктора:
- Координаты X и Y. Мы задаём Y как i * 40, чтобы строки располагались вертикально с отступом.
- Ключ шрифта '171' (тот, что мы добавили в кеш).
- Начальный текст. Для эффекта плавного появления используется строка с пробелами в начале.
DynamicBitmapText — это особый тип, который поддерживает свойство scrollX для прокрутки текста.
const t = this.add.dynamicBitmapText(0, i * 40, '171', ' PHASER 3 IS IN THE HOUSE ...');
После создания каждого объекта мы настраиваем его виртуальный размер (область, в которой текст будет виден) и масштаб. Увеличение масштаба в 2 раза соответствует пиксель-арт стилю игры.
t.setSize(640, 18);
t.setScale(2);
Наконец, каждый созданный текстовый объект добавляется в группу scrollers для последующего управления.
this.scrollers.add(t);
Анимация и логика прокрутки
Вся магия движения происходит в методе update, который вызывается на каждом кадре игры. Мы перебираем всех детей в группе scrollers с помощью forEach.
Логика проста: на каждом кадре мы увеличиваем свойство scrollX у каждого текстового объекта. Это смещает видимую область текста по горизонтали, создавая иллюзию движения справа налево.
child.scrollX += 2.5 + Math.sin((0.01 * index) * delta);
Здесь используется хитрый приём: к базовой скорости 2.5 добавляется значение синуса, которое зависит от индекса строки (index) и времени, прошедшего с последнего кадра (delta). Это создаёт волнообразный, нелинейный эффект движения для разных строк, что выглядит гораздо интереснее, чем равномерная прокрутка.
Когда текст полностью прокрутился и ушёл за пределы видимой области (значение scrollX превышает 2800), мы сбрасываем его позицию в начало, но с небольшим отрицательным отступом (-200), чтобы текст плавно появлялся из-за края.
if (child.scrollX > 2800)
{
child.scrollX = -200;
}
Использование delta в расчёте синуса гарантирует, что анимация будет плавной и независимой от частоты кадров.
Конфигурация игры и важные настройки
Для корректного отображения пиксель-арт графики и растровых шрифтов критически важна правильная конфигурация игры.
Ключевые параметры в объекте config:
- type: Phaser.WEBGL: Используется WebGL рендерер для лучшей производительности.
- pixelArt: true: Эта настройка отключает сглаживание текстуры при масштабировании, сохраняя чёткие, «квадратные» пиксели.
- width и height: Установлены в 640 * 2 и 400 * 2. Удвоенное разрешение позволяет масштабировать все игровые объекты в 2 раза (setScale(2)), сохраняя при этом чёткость из-за pixelArt: true.
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
pixelArt: true, // Важно для сохранения sharp пикселей!
width: 640 * 2,
height: 400 * 2,
scene: Example // Указываем наш класс сцены
};
Инициализация игры стандартна:
const game = new Phaser.Game(config);
Настройка pixelArt: true в сочетании с удвоенным разрешением холста — стандартный приём для создания ретро-игр с увеличенными, но чёткими спрайтами.
Что попробовать дальше
Вы освоили создание динамичного текстового скроллера с использованием DynamicBitmapText и групп объектов в Phaser 3. Этот подход открывает двери для множества экспериментов: попробуйте изменить формулу движения в update, чтобы текст двигался по синусоиде вертикально или по диагонали. Замените ретро-шрифт на свой собственный spritesheet, созданный в графическом редакторе. Добавьте взаимодействие — например, изменение скорости скролла при клике мыши или цвет текста в зависимости от позиции. Можно даже создать эффект «матрицы», генерируя случайные символы для каждой строки на лету. Главное — помните, что scrollX/scrollY и группы (Group) являются вашими основными инструментами для управления подобными массовыми эффектами.
