О чем этот пример
Работа с текстом — важная часть создания атмосферы в играх, особенно ретро-стилистики. Phaser предлагает мощный инструмент `RetroFont` для работы с растровыми шрифтами, но его возможности по динамическому изменению внешнего вида текста часто остаются в тени. В этой статье мы разберем пример, который показывает, как можно оживить надпись, программно меняя цвет каждого символа в реальном времени. Этот прием отлично подойдет для создания эффектных титров, анимированных меню или выделения важных сообщений в игре.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
let i = 0;
let hsv = [];
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');
}
create ()
{
hsv = Phaser.Display.Color.HSVColorWheel();
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));
const text = this.add.dynamicBitmapText(0, 300, 'knighthawks', 'PHASER 3').setScale(4);
text.setDisplayCallback(this.textCallback);
}
textCallback (data)
{
data.tint.topLeft = hsv[Math.floor(i)].color;
data.tint.topRight = hsv[359 - Math.floor(i)].color;
data.tint.bottomLeft = hsv[359 - Math.floor(i)].color;
data.tint.bottomRight = hsv[Math.floor(i)].color;
i += 0.1;
if (i >= hsv.length)
{
i = 0;
}
return data;
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
pixelArt: true,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка: загрузка и создание растрового шрифта
Прежде чем выводить текст, необходимо подготовить ресурсы. В методе preload() загружается изображение, содержащее набор символов растрового шрифта.
this.load.image('knighthawks', 'assets/fonts/retro/knight3.png');
Ключевой этап происходит в create(). Сначала генерируется цветовая палитра HSV (цветовой круг), которая будет использоваться для окрашивания. Затем создается конфигурационный объект для парсинга растрового шрифта. В нем указывается ключ загруженного изображения, размеры одного символа, набор используемых символов (в данном случае TEXT_SET6), количество символов в строке изображения и отступы.
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 этот конфиг преобразуется в данные, которые Phaser понимает как шрифт, и добавляется в кеш под ключом 'knighthawks'.
this.cache.bitmapFont.add('knighthawks', Phaser.GameObjects.RetroFont.Parse(this, config));
Создание динамического текста
Теперь можно создать сам текстовый объект. Используется add.dynamicBitmapText. Важное отличие от обычного bitmapText — возможность применять callback-функцию к каждому символу для изменения его свойств перед отрисовкой.
const text = this.add.dynamicBitmapText(0, 300, 'knighthawks', 'PHASER 3').setScale(4);
Связь между текстовым объектом и функцией обратного вызова устанавливается методом setDisplayCallback. Именно эта функция, textCallback, будет управлять внешним видом каждого символа.
text.setDisplayCallback(this.textCallback);
Магия в деталях: функция обратного вызова
Функция textCallback — сердце анимации. Она вызывается для каждого символа в кадре. Ей передается объект data, содержащий свойства символа, такие как его индекс, код, положение и, что важно для нас, — объект tint.
Объект tint позволяет задать цвет для каждого из четырех углов символа, создавая градиентную заливку. В примере цвета берутся из заранее созданного массива hsv (цветового круга).
data.tint.topLeft = hsv[Math.floor(i)].color;
data.tint.topRight = hsv[359 - Math.floor(i)].color;
data.tint.bottomLeft = hsv[359 - Math.floor(i)].color;
data.tint.bottomRight = hsv[Math.floor(i)].color;
Глобальная переменная `i` увеличивается с каждым вызовом, что приводит к плавному переходу цветов по кругу. Условие сбрасывает счетчик, когда проход по всей палитре завершен.
i += 0.1;
if (i >= hsv.length) {
i = 0;
}
Функция обязательно должна возвращать измененный объект data.
return data;
Именно этот механизм позволяет создавать сложные пошаговые анимации, где цвет каждого символа зависит от его позиции или глобального состояния игры.
Что попробовать дальше
Использование DynamicBitmapText с callback-функцией открывает широкие возможности для кастомизации текста в Phaser. Вы можете не только менять цвет, но и управлять прозрачностью, смещением, масштабом каждого символа на лету. Для экспериментов попробуйте привязать изменение цвета не к глобальному счетчику, а к позиции символа (data.index) или скорости движения игрового объекта. Это можно использовать для создания эффекта "волны", пробегающей по тексту, или для визуальной обратной связи, когда текст реагирует на действия игрока.
