О чем этот пример
Статичный текст — это скучно. В игровых интерфейсах, меню и титрах хочется динамики и индивидуальности. Класс `DynamicBitmapText` в Phaser 3 позволяет управлять отрисовкой каждого символа текста по отдельности. В этой статье мы разберем, как с помощью функции обратного вызова `displayCallback` создавать сложные анимационные эффекты для текста, такие как волны, вращение или хаотичное движение символов, превращая обычные надписи в яркий элемент геймдизайна. Этот подход особенно полезен для создания стилизованных заголовков, "дыхания" текста в RPG, дрожания надписей от страха или магического свечения. Вы получите полный контроль над позицией, масштабом и поворотом каждого символа в реальном времени, не прибегая к созданию сотен отдельных игровых объектов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
var scale = { r: -Math.PI };
// data = { index: index, charCode: charCode, x: x, y: y, scaleX: scaleX, scaleY: scaleY }
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.bitmapFont('ice', 'assets/fonts/bitmap/iceicebaby.png', 'assets/fonts/bitmap/iceicebaby.xml');
this.load.bitmapFont('atari', 'assets/fonts/bitmap/atari-smooth.png', 'assets/fonts/bitmap/atari-smooth.xml');
this.load.bitmapFont('gothic', 'assets/fonts/bitmap/gothic.png', 'assets/fonts/bitmap/gothic.xml');
this.load.bitmapFont('hyper', 'assets/fonts/bitmap/hyperdrive.png', 'assets/fonts/bitmap/hyperdrive.xml');
}
create ()
{
// var text = this.add.bitmapText(60, 200, 'hyper', 'Hello World!', 128);
// var text = this.add.bitmapText(60, 200, 'atari', 'Hello\nWorld!', 128);
const text = this.add.dynamicBitmapText(60, 60, 'gothic', 'Hello\nWorld!', 128);
text.setDisplayCallback(this.textCallback);
const text2 = this.add.dynamicBitmapText(560, 60, 'ice', 'Hello\nWorld!', 128);
text2.setDisplayCallback(this.textCallback);
const text3 = this.add.dynamicBitmapText(100, 460, 'hyper', 'Terminator', 200);
text3.setDisplayCallback(this.textCallback);
this.tweens.add({
targets: scale,
duration: 2000,
r: Math.PI,
ease: 'Linear',
repeat: -1
});
}
textCallback (data)
{
data.rotation = scale.r;
// data.rotation = Phaser.Math.Between(-0.02, 0.02);
// data.rotation = 1.570;
return data;
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое DynamicBitmapText и displayCallback?
В отличие от обычного BitmapText, который отрисовывается как единое целое, DynamicBitmapText позволяет задать функцию обратного вызова (displayCallback). Эта функция вызывается для **каждого символа** текста перед его отрисовкой на кадре, передавая объект с его параметрами. Вы можете модифицировать эти параметры, и символ будет отображен уже с изменениями.
Ключевой объект data, который получает функция, содержит следующие свойства для текущего символа:
* index: Порядковый номер символа в строке.
* charCode: Код символа.
* `x,y`: Исходные координаты для отрисовки.
* scaleX, scaleY: Масштаб по осям.
* rotation: Угол поворота (в радианах).
const text = this.add.dynamicBitmapText(60, 60, 'gothic', 'Hello\nWorld!', 128);
text.setDisplayCallback(this.textCallback);
Метод setDisplayCallback назначает вашу функцию модификации. Без нее текст ведет себя как обычный статичный BitmapText.
Анализ кода: вращение символов
В предоставленном примере создается простой, но эффектный анимированный текст, где все символы синхронно вращаются.
Сначала создается глобальный объект scale, который хранит одно значение — угол поворота `r`. Это значение будет анимировано и доступно для функции обратного вызова.
var scale = { r: -Math.PI };
В методе create создаются три текстовых объекта с разными шрифтами. Обратите внимание на использование add.dynamicBitmapText. Для каждого из них устанавливается один и тот же коллбэк this.textCallback.
const text = this.add.dynamicBitmapText(60, 60, 'gothic', 'Hello\nWorld!', 128);
text.setDisplayCallback(this.textCallback);
Затем создается бесконечная Tween-анимация для объекта scale. За 2 секунды значение `rплавно изменяется от-Math.PIдоMath.PI` (то есть, полный оборот на 360 градусов), и затем повторяется.
this.tweens.add({
targets: scale,
duration: 2000,
r: Math.PI,
ease: 'Linear',
repeat: -1
});
Сердце эффекта: функция textCallback
Эта функция — место, где происходит магия. Она вызывается для каждого символа в каждом кадре.
1. Phaser готовится нарисовать символ и передает в функцию его текущие параметры в объекте data.
2. Мы берем текущее значение угла scale.r (которое плавно меняется твином) и присваиваем его свойству data.rotation.
3. Возвращаем модифицированный объект data обратно в рендерер.
4. Phaser отрисовывает этот конкретный символ с новым углом поворота.
textCallback (data)
{
data.rotation = scale.r;
return data;
}
Поскольку все символы в этом примере ссылаются на одно и то же значение scale.r, они вращаются синхронно, как единое целое. Простота этого механизма открывает огромные возможности для кастомизации.
Практические идеи для ваших игр
Модифицируя data внутри коллбэка, можно создавать десятки эффектов. Вот несколько практических идей:
* **Волна:** Изменяйте data.y на основе Math.sin(data.index + time). Символы начнут "плыть" волной.
textCallback (data) {
// time - это глобальная переменная, увеличиваемая в update()
data.y += Math.sin(data.index * 0.5 + time) * 10;
return data;
}
* **Случайное дрожание:** Идеально для надписей "страха" или поврежденного интерфейса. Используйте Phaser.Math.Between для небольших случайных смещений `x,yилиrotation`.
textCallback (data) {
data.x += Phaser.Math.Between(-2, 2);
data.y += Phaser.Math.Between(-2, 2);
return data;
}
* **Появление символов (Typewriter с эффектом):** Меняйте scaleX и scaleY от 0 до 1 в зависимости от data.index и общего прошедшего времени, чтобы символы увеличивались один за другим.
* **Цветовая пульсация:** Хотя прямо в data нет цвета, можно управлять оттенком (tint) самого объекта DynamicBitmapText на основе среднего индекса или времени, создавая эффект бегущей волны цвета.
Важные нюансы и производительность
DynamicBitmapText — мощный, но более требовательный к ресурсам инструмент, чем статичный аналог.
* **Производительность:** Коллбэк выполняется для каждого символа каждый кадр. Для длинных текстов (параграфов) это может стать нагрузкой. Используйте эффекты для коротких, значимых надписей (заголовки, имена, реплики).
* **Координаты:** Помните, что data.x и data.y — это локальные координаты символа **относительно позиции всего текстового объекта**. Их изменение не влияет на позицию соседних символов.
* **Возвращаемое значение:** Функция **обязана** вернуть объект data. Если не вернуть его или вернуть null, символ отрисован не будет.
* **Использование this внутри коллбэка:** Если ваша функция-коллбэк является методом класса сцены (как в примере), и вам нужен доступ к свойствам сцены (this.time, this.tweens), убедитесь, что она правильно привязана. В примере используется стрелочная функция или метод класса, переданный по ссылке, что безопасно, так как внутри не требуется доступ к сцене. Для сложных случаев может потребоваться .bind(this).
Что попробовать дальше
DynamicBitmapText с displayCallback — это ваш ключ к созданию уникального, живого текста в играх на Phaser. Вы вышли за рамки простого вывода строк и получили инструмент для точечной анимации. Начните экспериментировать: создайте эффект "замораживания" текста, где символы постепенно поворачиваются и сдвигаются, имитируя лед. Или реализуйте "гравитацию", где каждый символ падает и отскакивает от воображаемой линии. Комбинируйте изменения rotation, scale и позиции на основе индекса символа или времени — ограничений только ваша фантазия.
