О чем этот пример
Создание атмосферных ретро-интерфейсов — важная часть геймдева. 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('knighthawks', 'assets/fonts/retro/knight3.png');
this.load.image('rain', 'assets/pics/thalion-rain.png');
this.load.image('contra', 'assets/pics/contra1.png');
}
create ()
{
this.add.image(0, 0, 'rain').setOrigin(0).setScale(4);
const config = {
image: 'knighthawks',
width: 31,
height: 25,
chars: Phaser.GameObjects.RetroFont.TEXT_SET6,
charsPerRow: 10,
spacing: { x: 1, y: 1 }
};
this.cache.bitmapFont.add('knighthawks', Phaser.GameObjects.RetroFont.Parse(this, config));
this.dynamic = this.add.dynamicBitmapText(0, 0, 'knighthawks', ' PHASER 3 IS IN THE HOUSE');
this.dynamic.setScale(4);
this.tweens.add({
targets: this.dynamic,
duration: 4000,
y: 175*4,
ease: 'Sine.easeInOut',
repeat: -1,
yoyo: true
});
this.add.image(1280, 800, 'contra').setOrigin(1).setScale(4);
}
update (time, delta)
{
this.dynamic.scrollX += 0.15 * delta;
if (this.dynamic.scrollX > 1300)
{
this.dynamic.scrollX = 0;
}
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
pixelArt: true,
width: 1280,
height: 800,
scene: Example
};
const game = new Phaser.Game(config);
Загрузка ресурсов и настройка сцены
В методе preload загружаются все необходимые ресурсы: фоновое изображение, картинка для шрифта и дополнительный графический элемент. Ключевой момент — использование this.load.setBaseURL для указания базового URL, что позволяет использовать относительные пути.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('knighthawks', 'assets/fonts/retro/knight3.png');
this.load.image('rain', 'assets/pics/thalion-rain.png');
this.load.image('contra', 'assets/pics/contra1.png');
}
Создание и кеширование растрового шрифта
В методе create сначала выводится фоновое изображение. Затем определяется конфигурация для создания растрового шрифта из изображения knighthawks. Конфиг включает размер одного символа, набор символов, их расположение на изображении и отступы.
const config = {
image: 'knighthawks',
width: 31,
height: 25,
chars: Phaser.GameObjects.RetroFont.TEXT_SET6,
charsPerRow: 10,
spacing: { x: 1, y: 1 }
};
Статический метод Phaser.GameObjects.RetroFont.Parse анализирует изображение на основе конфига и создаёт данные шрифта. Эти данные добавляются в кеш под ключом 'knighthawks', чтобы в дальнейшем использовать их как идентификатор шрифта.
this.cache.bitmapFont.add('knighthawks', Phaser.GameObjects.RetroFont.Parse(this, config));
Работа с DynamicBitmapText и анимация
После подготовки шрифта создаётся объект DynamicBitmapText. Его ключевое отличие от обычного BitmapText — наличие свойств scrollX и scrollY, позволяющих сдвигать текст внутри своей области отображения.
this.dynamic = this.add.dynamicBitmapText(0, 0, 'knighthawks', ' PHASER 3 IS IN THE HOUSE');
this.dynamic.setScale(4);
Обратите внимание на пробелы в начале строки — это простой способ задать начальный отступ для прокрутки. Для объекта также создаётся твин, который плавно перемещает его по вертикали, создавая дополнительный эффект движения.
this.tweens.add({
targets: this.dynamic,
duration: 4000,
y: 175*4,
ease: 'Sine.easeInOut',
repeat: -1,
yoyo: true
});
Основная логика прокрутки в update
Сердце эффекта бегущей строки находится в методе update. Каждый кадр мы увеличиваем свойство scrollX динамического текста. Умножение на delta (время, прошедшее с предыдущего кадра) делает скорость прокрутки независимой от частоты кадров.
update (time, delta)
{
this.dynamic.scrollX += 0.15 * delta;
if (this.dynamic.scrollX > 1300)
{
this.dynamic.scrollX = 0;
}
}
Когда прокрутка превышает определённое значение (в данном случае 1300), она сбрасывается в ноль, создавая впечатление бесконечно зацикленной строки. Значение подобрано эмпирически под длину текста и масштаб.
Что попробовать дальше
Использование DynamicBitmapText открывает простой путь к созданию плавно движущегося текста в стиле старых игр. Для экспериментов попробуйте изменить скорость прокрутки, направление (scrollY), добавить эффект мерцания через setAlpha в твине или использовать несколько строк с разными задержками. Также можно поиграть с другими наборами символов TEXT_SET из RetroFont или создать собственное изображение шрифта.
