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

Визуальные эффекты для текста — простой способ повысить привлекательность игры. Статичные надписи скучны, а плавные цветовые переходы оживляют интерфейс, меню или титры. В этом примере показано, как создать динамически окрашенный текст с градиентом, используя встроенные возможности Phaser 3 для работы с Bitmap-шрифтами и кастомным рендерингом. Вы научитесь применять callback-функцию для тонирования каждого символа в реальном времени, что открывает путь к созданию мерцающих, переливающихся или реагирующих на игровые события текстовых элементов.

Версия 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.bitmapFont('ice', 'assets/fonts/bitmap/iceicebaby.png', 'assets/fonts/bitmap/iceicebaby.xml');
    }

    create ()
    {
        hsv = Phaser.Display.Color.HSVColorWheel();

        const tintedText = this.add.dynamicBitmapText(32, 64, 'ice', '- Phaser III -', 128);
        tintedText.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.05;

        if (i >= hsv.length)
        {
            i = 0;
        }

        return data;
    }
}

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    backgroundColor: '#2d2d2d',
    scene: Example
};

const game = new Phaser.Game(config);

Подготовка: загрузка Bitmap-шрифта

Bitmap-шрифты в Phaser — это предварительно отрендеренные изображения символов. Они обеспечивают чёткое отображение и высокую производительность, так как не требуют загрузки системных шрифтов. В методе preload мы указываем базовый URL для ресурсов и загружаем файлы шрифта: изображение (PNG) и файл описания (XML), который содержит данные о расположении каждого символа на изображении.

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');
}

Создание динамического текстового объекта

В методе create мы инициализируем наш текст. Ключевой объект здесь — DynamicBitmapText. В отличие от статичного, он позволяет модифицировать свойства каждого символа (глифа) на лету через callback-функцию. Мы создаём объект, указываем позицию, ключ загруженного шрифта, строку для отображения и размер.

create ()
{
    hsv = Phaser.Display.Color.HSVColorWheel();

    const tintedText = this.add.dynamicBitmapText(32, 64, 'ice', '- Phaser III -', 128);
    tintedText.setDisplayCallback(this.textCallback);
}

Перед этим мы генерируем палитру цветов с помощью Phaser.Display.Color.HSVColorWheel(). Этот метод возвращает массив из 360 объектов цвета, представляющих полный цветовой круг в модели HSV (оттенок, насыщенность, значение). Это даёт нам плавный градиент для анимации.

Магия callback-функции: тонирование символов

Сердце эффекта — функция textCallback. Она вызывается для каждого символа текста при каждом обновлении кадра (рендере). В параметр data передаётся объект с информацией о символе: его индекс, позиция на холсте и, что важно для нас, объект tint.

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.05;

    if (i >= hsv.length)
    {
        i = 0;
    }

    return data;
}

Объект tint содержит четыре свойства, соответствующие углам символа: topLeft, topRight, bottomLeft, bottomRight. Каждому из них мы присваиваем цвет из нашей HSV-палитры. Используя глобальную переменную `iи её зеркальное отражение359 - Math.floor(i), мы задаём разные цвета для противоположных углов, создавая диагональный градиент. Плавное увеличениеi` на каждом вызове функции анимирует этот градиент, заставляя цвета «перетекать» по тексту.

Конфигурация игры и запуск

Сцена готова, остаётся собрать конфигурацию игры и запустить её. В этом примере используется WEBGL-рендерер для лучшей производительности графических эффектов.

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    backgroundColor: '#2d2d2d',
    scene: Example
};

const game = new Phaser.Game(config);

Указав тёмно-серый фон, мы обеспечиваем хороший контраст для яркого переливающегося текста.

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

Динамические Bitmap-тексты в Phaser 3 — мощный инструмент для кастомного рендеринга. Вы освоили создание анимированного цветового градиента с помощью setDisplayCallback. Для экспериментов попробуйте изменить логику в callback-функции: привяжите цвет не к глобальному счётчику, а к позиции символа data.index для статичного радужного текста; используйте Math.sin() для пульсирующего эффекта; или меняйте не только tint, но и alpha (прозрачность) для мерцания. Это открывает двери к созданию уникальных визуальных подписей для ваших игровых проектов.