О чем этот пример
Визуальные эффекты для текста — отличный способ привлечь внимание игрока, выделить важное сообщение или просто добавить игре стиля. В Phaser для работы с растровыми шрифтами есть мощный инструмент — DynamicBitmapText. В этой статье мы разберем, как с помощью `setDisplayCallback` создавать кастомные анимации для каждого символа текста, от простого дрожания до плавной радужной волны. Вы научитесь управлять позицией, цветом и другими свойствами символов в реальном времени, что откроет двери для создания уникальных типографских эффектов в ваших проектах.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
const rainbowColor = [0xFF5757, 0xE8A241, 0x97FF7F, 0x52BFFF, 0x995DE8];
let rainbowColorIdx = 0;
let rainbowColorOffset = 0;
let delay = 0;
let rainbowWave = 0;
let jiggleText;
let rainbowText;
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.bitmapFont('desyrel', 'assets/fonts/bitmap/desyrel.png', 'assets/fonts/bitmap/desyrel.xml');
}
create ()
{
jiggleText = this.add.dynamicBitmapText(32, 100, 'desyrel', 'It\'s cold outside,\nthere\'s no kind of atmosphere', 64);
rainbowText = this.add.dynamicBitmapText(32, 400, 'desyrel', 'HELLO WORLD', 96);
jiggleText.setDisplayCallback(this.textCallback);
rainbowText.setDisplayCallback(this.rainbowCallback);
}
update()
{
rainbowColorIdx = 0;
if (delay++ === 6)
{
rainbowColorOffset = (rainbowColorOffset + 1) % (rainbowColor.length);
delay = 0;
}
}
rainbowCallback(data)
{
data.color = rainbowColor[(rainbowColorOffset + rainbowColorIdx) % rainbowColor.length];
rainbowColorIdx = (rainbowColorIdx + 1) % (rainbowColor.length);
data.y = Math.cos(rainbowWave + rainbowColorIdx) * 10;
rainbowWave += 0.01;
return data;
}
// data = { color: color, index: index, charCode: charCode, x: x, y: y, scaleX: scaleX, scaleY: scaleY }
textCallback (data)
{
if (data.index >= 5 && data.index <= 8)
{
data.x = Phaser.Math.Between(data.x - 2, data.x + 2);
data.y = Phaser.Math.Between(data.y - 4, data.y + 4);
}
return data;
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое Display Callback?
Класс DynamicBitmapText в Phaser позволяет отображать текст, используя растровые шрифты (bitmap fonts). Его ключевая особенность — метод setDisplayCallback(callback). Этот метод назначает функцию обратного вызова (callback), которая выполняется для **каждого отображаемого символа** текста на каждом кадре.
Функция получает объект data с параметрами текущего символа и должна вернуть его обратно, возможно, с изменениями. Это и есть основа для персональной анимации каждого символа.
rainbowText.setDisplayCallback(this.rainbowCallback);
Структура данных для callback
Функция обратного вызова получает один аргумент — объект data. В примере этот объект подробно описан в комментарии к textCallback. Давайте разберем его поля.
* color: Цвет символа в числовом формате (как 0xFF5757).
* index: Индекс (порядковый номер) символа в строке.
* charCode: Код символа.
* `x,y`: Текущие координаты символа.
* scaleX, scaleY: Масштаб по осям.
Изменяя эти свойства внутри callback-функции, мы управляем отображением.
// data = { color: color, index: index, charCode: charCode, x: x, y: y, scaleX: scaleX, scaleY: scaleY }
textCallback (data) {
// Логика изменения данных символа
return data;
}
Важно всегда возвращать измененный объект data из функции.
Эффект "Дрожания" (Jiggle)
Первый пример в коде (textCallback) создает эффект легкого дрожания для конкретного диапазона символов. Логика проста: мы определяем, попадает ли индекс текущего символа в нужный интервал, и если да — случайным образом смещаем его координаты.
В данном случае эффект применяется к символам с индексами от 5 до 8 (то есть к слову "cold"). Функция Phaser.Math.Between(min, max) каждый кадр генерирует случайное смещение относительно исходной позиции.
textCallback (data) {
// Применяем эффект только к символам с 5-го по 8-й индекс
if (data.index >= 5 && data.index <= 8) {
data.x = Phaser.Math.Between(data.x - 2, data.x + 2);
data.y = Phaser.Math.Between(data.y - 4, data.y + 4);
}
return data;
}
Эффект "Радужной волны" (Rainbow Wave)
Второй, более сложный пример (rainbowCallback), совмещает два эффекта: плавное изменение цвета по заданной палитре и волнообразное движение по вертикали.
**Цвет:** Глобальная переменная rainbowColorOffset медленно меняется в методе update(), создавая эффект "прокрутки" палитры. Для каждого символа мы берем цвет из массива rainbowColor, используя сумму этого смещения и локального индекса rainbowColorIdx. rainbowColorIdx увеличивается для каждого следующего символа в строке, создавая градиент.
**Движение:** Вертикальная позиция data.y вычисляется с помощью косинуса, что дает плавную волну. Переменная rainbowWave постоянно увеличивается, заставляя волну "бежать" вдоль текста.
rainbowCallback(data) {
// Меняем цвет, используя смещение и индекс
data.color = rainbowColor[(rainbowColorOffset + rainbowColorIdx) % rainbowColor.length];
rainbowColorIdx = (rainbowColorIdx + 1) % (rainbowColor.length);
// Задаем вертикальное движение по волне
data.y = Math.cos(rainbowWave + rainbowColorIdx) * 10;
rainbowWave += 0.01;
return data;
}
Важные детали реализации
1. **Глобальные переменные:** Обратите внимание, что для работы эффектов используются глобальные переменные (rainbowColorIdx, rainbowWave и др.). Это нужно, чтобы сохранять состояние между вызовами callback для разных символов и кадров. В более сложном проекте лучше вынести эту логику в свойства сцены или отдельный класс.
2. **Сброс индекса:** В методе update() перед каждым новым кадром отрисовки сбрасывается rainbowColorIdx = 0. Это важно, потому что rainbowCallback вызывается для каждого символа заново, и отсчет индекса цвета должен начинаться с первого символа строки.
3. **Задержка (delay):** Переменная delay используется, чтобы замедлить изменение глобального смещения цвета (rainbowColorOffset), делая анимацию более плавной и не слишком быстрой.
update() {
rainbowColorIdx = 0; // Сбрасываем для нового кадра
if (delay++ === 6) { // Обновляем смещение цвета раз в 6 кадров
rainbowColorOffset = (rainbowColorOffset + 1) % (rainbowColor.length);
delay = 0;
}
}
Что попробовать дальше
Метод setDisplayCallback для DynamicBitmapText — это мощный и гибкий инструмент для создания живого, динамического текста в Phaser. Вы можете управлять не только цветом и позицией, но и масштабом, прозрачностью (добавив свойство alpha в объект data) и другими параметрами.
**Идеи для экспериментов:**
* Создайте эффект "появления" текста, анимируя scale или alpha символов в зависимости от индекса или времени.
* Реагируйте на ввод игрока: пусть текст "отскакивает" или меняет цвет при наведении курсора.
* Скомбинируйте несколько callback-функций для разных строк или частей интерфейса.
* Используйте физическое тело (тело Arcade Physics) для расчета позиции символов, создавая текст, который "падает" или сталкивается.
