О чем этот пример

Статичный текст — это скучно. В игровых интерфейсах, титрах или эффектах хочется, чтобы буквы двигались, масштабировались и переливались. Класс `DynamicBitmapText` в Phaser и его метод `setDisplayCallback()` дают вам прямой доступ к отрисовке каждого символа, позволяя программировать сложные анимации на лету. Эта статья покажет, как использовать callback-функцию для создания волнообразного текста с минимальным кодом, открывая двери к кастомизации шрифтов и динамическим интерфейсам.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


const scale = { i: -64, x: 16, y: -16 };
//  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('desyrel', 'assets/fonts/bitmap/desyrel.png', 'assets/fonts/bitmap/desyrel.xml');

    }

    create ()
    {
        const text = this.add.dynamicBitmapText(60, 200, 'desyrel', 'Hello World!', 64);

        text.setDisplayCallback(this.textCallback);

        this.tweens.add({
            targets: scale,
            duration: 1000,
            i: 64,
            x: -16,
            y: 16,
            ease: 'Linear',
            repeat: -1,
            yoyo: true
        });
    }

    textCallback (data)
    {
        data.y += scale.y * data.index;

        // if (data.index % 2)
        // {
        //     data.y += scale.x;
        // }
        // else
        // {
        //     data.y += scale.y;
        // }

        return data;
    }
}
const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);


Загрузка и создание динамического текста

В отличие от обычного BitmapText, динамическая версия позволяет менять свойства каждого символа в реальном времени. Первым делом загружаем bitmap-шрифт, который состоит из изображения (PNG) и файла описания (XML).

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() создаём объект динамического текста. Ключевые параметры: начальные координаты (60, 200), ключ шрифта ('desyrel'), строка для отображения и размер.

create() {
    const text = this.add.dynamicBitmapText(60, 200, 'desyrel', 'Hello World!', 64);
}

Волшебный displayCallback

Сердце механики — метод setDisplayCallback(). Он назначает функцию, которая вызывается для **каждого символа** перед его отрисовкой на кадре. Функция получает объект data со свойствами символа и должна вернуть его обратно.

text.setDisplayCallback(this.textCallback);

Объект data содержит, среди прочего, индекс символа в строке (index), его текущие координаты (`x,y) и масштабы. В примере мы изменяем вертикальную позициюdata.y` каждого символа, создавая волну.

textCallback(data) {
    data.y += scale.y * data.index;
    return data;
}

Здесь scale.y — внешняя переменная, которая меняется со временем. Умножение на data.index обеспечивает смещение для каждого следующего символа, создавая ступенчатый эффект.

Анимируем параметры через Tweens

Чтобы волна двигалась, нужно анимировать значения, которые использует callback-функция. В примере для этого создаётся простой объект scale и анимируется с помощью системы твинов Phaser.

const scale = { i: -64, x: 16, y: -16 };

// В create():
this.tweens.add({
    targets: scale,
    duration: 1000,
    i: 64,
    x: -16,
    y: 16,
    ease: 'Linear',
    repeat: -1,
    yoyo: true
});

Твин плавно меняет свойства объекта scale от начальных значений к целевым за 1000 мс. Параметры repeat: -1 и yoyo: true заставляют анимацию бесконечно колебаться туда-обратно. Каждый кадр textCallback получает обновлённое значение scale.y, и текст начинает «дышать».

Эксперименты с data: комментируемый код

В исходном примере часть кода закомментирована. Это отличная площадка для экспериментов. Попробуйте раскомментировать блок и увидеть, как изменится поведение.

// if (data.index % 2)
// {
//     data.y += scale.x;
// }
// else
// {
//     data.y += scale.y;
// }

Этот код проверяет чётность индекса символа (data.index % 2). Чётные и нечётные символы будут смещаться на разные величины (scale.x или scale.y), создавая более рваный, «зубчатый» эффект. Поменяйте условие или используйте scale.i для других вариаций.

Помните: можно менять не только data.y, но и data.x, data.scaleX, data.scaleY и даже data.color (если шрифт поддерживает tint).

Что попробовать дальше

DynamicBitmapText с displayCallback — мощный инструмент для создания живой типографики в играх. Вы можете генерировать эффекты бегущей строки, дрожания, появления символов по одному или реакции текста на игровые события. Для экспериментов попробуйте: привязать смещение букв к синусу от времени, менять масштаб в зависимости от позиции мыши или использовать разные callback-функции для различных текстовых объектов в сцене.