О чем этот пример
Добавление атмосферы — ключевой элемент в игровом дизайне. Один из эффективных способов погрузить игрока в мир вашей игры — использовать стилизованные текстовые эффекты. В этом руководстве мы разберем, как создать эффект бегущей строки в стиле ретро-консолей или киберпанк-неоновых вывесок, используя мощный, но часто упускаемый из виду объект `DynamicBitmapText`. Вы научитесь динамически управлять текстом, создавая живые, привлекающие внимание элементы интерфейса или нарратива.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
let i = 0;
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('neuromancer', 'assets/pics/case.jpg');
this.load.image('knighthawks', 'assets/fonts/retro/knighthawks-font.png');
}
create ()
{
var prose = [
"The sky above the port was the color of television, tuned to a dead channel.",
"'It's not like I'm using,' Case heard someone say, as he shouldered his way",
"through the crowd around the door of the Chat. 'It's like my body's developed",
"this massive drug deficiency.' It was a Sprawl voice and a Sprawl joke.",
"The Chatsubo was a bar for professional expatriates; you could drink there for",
"a week and never hear two words in Japanese.",
"Ratz was tending bar, his prosthetic arm jerking monotonously as he filled a tray",
"of glasses with draft Kirin. He saw Case and smiled, his teeth a webwork of",
"East European steel and brown decay. Case found a place at the bar, between the",
"unlikely tan on one of Lonny Zone's whores and the crisp naval uniform of a tall",
"African whose cheekbones were ridged with precise rows of tribal scars. 'Wage was",
"in here early, with two joeboys,' Ratz said, shoving a draft across the bar with",
"his good hand. 'Maybe some business with you, Case?'",
"Case shrugged. The girl to his right giggled and nudged him.",
"The bartender's smile widened. His ugliness was the stuff of legend. In an age of",
"affordable beauty, there was something heraldic about his lack of it. The antique",
"arm whined as he reached for another mug.",
" ----------- From Neuromancer by William Gibson "
];
this.content = prose.join(' ').toUpperCase();
var config = {
image: 'knighthawks',
width: 32,
height: 25,
chars: Phaser.GameObjects.RetroFont.TEXT_SET2,
charsPerRow: 10
};
this.cache.bitmapFont.add('knighthawks', Phaser.GameObjects.RetroFont.Parse(this, config));
this.add.image(400, 300, 'neuromancer').setAlpha(0.3);
this.dynamic = this.add.dynamicBitmapText(0, 190, 'knighthawks', ' --------------------- ');
this.dynamic.setScale(4);
}
update ()
{
this.dynamic.scrollX += 4;
if (this.dynamic.scrollX >= 32)
{
// Remove first character
var current = this.dynamic.text.substr(1);
// Add next character from the string
current = current.concat(this.content[i]);
i++;
if (i === this.content.length)
{
i = 0;
}
// Set it
this.dynamic.setText(current);
// Reset scroller
this.dynamic.scrollX = this.dynamic.scrollX % 32;
}
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
pixelArt: true,
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Загрузка ресурсов и подготовка данных
Вся магия начинается с подготовки. В методе preload мы загружаем фоновое изображение (neuromancer) и спрайт-лист с ретро-шрифтом (knighthawks).
Ключевой элемент — текстовый массив prose. Это отрывок из «Нейроманта» Уильяма Гибсона, который мы будем прокручивать. В create этот массив объединяется в одну строку и приводится к верхнему регистру для соответствия эстетике bitmap-шрифтов.
this.content = prose.join(' ').toUpperCase();
Создание и конфигурация Bitmap Font
Phaser не умеет напрямую загружать bitmap-шрифты из простого изображения. Вместо этого мы используем утилиту RetroFont.Parse, чтобы создать данные шрифта в нужном формате и поместить их в кеш.
Конфигурационный объект config описывает наше изображение со шрифтом: размер каждого символа (32x25), набор символов (TEXT_SET2) и их расположение на листе (10 символов в строке). После парсинга шрифт доступен в кеше под ключом 'knighthawks'.
var config = {
image: 'knighthawks',
width: 32,
height: 25,
chars: Phaser.GameObjects.RetroFont.TEXT_SET2,
charsPerRow: 10
};
this.cache.bitmapFont.add('knighthawks', Phaser.GameObjects.RetroFont.Parse(this, config));
Создание динамического текстового объекта
Объект DynamicBitmapText — это сердце нашего эффекта. В отличие от обычного BitmapText, он позволяет изменять текст и его свойства (такие как положение букв) в реальном времени, что идеально подходит для анимации.
Мы создаем объект, позиционируем его, устанавливаем начальный текст (дефисы как заполнитель) и увеличиваем масштаб в 4 раза для лучшей читаемости.
this.dynamic = this.add.dynamicBitmapText(0, 190, 'knighthawks', ' --------------------- ');
this.dynamic.setScale(4);
Механика прокрутки в методе Update
Анимация состоит из двух частей: плавного скролла всего текста и дискретной смены символов.
1. **Скролл:** Каждый кадр мы сдвигаем свойство scrollX объекта на 4 пикселя. Это заставляет весь текстовый блок плавно двигаться влево.
2. **Обновление текста:** Когда накопленный сдвиг достигает или превышает ширину одного символа (32 пикселя), пришло время заменить символы. Мы удаляем первый символ из строки, добавляем следующий символ из нашего подготовленного текста (this.content) и обновляем текст объекта с помощью setText. После этого сбрасываем scrollX, используя остаток от деления, чтобы сохранить плавность.
this.dynamic.scrollX += 4;
if (this.dynamic.scrollX >= 32) {
var current = this.dynamic.text.substr(1);
current = current.concat(this.content[i]);
i++;
// ... сброс индекса `i` при достижении конца строки
this.dynamic.setText(current);
this.dynamic.scrollX = this.dynamic.scrollX % 32;
}
Важные настройки конфигурации игры
Для корректного отображения pixel art и bitmap-шрифтов критически важна настройка pixelArt: true в конфиге игры. Она отключает сглаживание при масштабировании изображений, сохраняя четкие, пиксельные края.
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
pixelArt: true, // Важно для четкого шрифта!
width: 800,
height: 600,
scene: Example
};
Что попробовать дальше
Вы освоили технику создания динамической бегущей строки с помощью DynamicBitmapText и scrollX. Этот подход открывает множество возможностей: от создания титров и диалоговых систем до имитации терминалов и рекламных табло в играх. Для экспериментов попробуйте изменить скорость скролла, направление (используйте scrollY), добавлять несколько строк одновременно или менять цвет и тень текста (setTint, setDropShadow) в зависимости от контекста игры.
