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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
        this.i = 0;
    }

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

        //  Rainbow Text
        this.text1 = this.add.text(50, 100, 'Rainbow Text', { font: "74px Arial Black", fill: "#fff" });
        this.text1.setStroke('#00f', 16);
        this.text1.setShadow(2, 2, "#333333", 2, true, true);

        //  Rainbow Stroke
        this.text2 = this.add.text(50, 300, 'Rainbow Stroke', { font: "74px Arial Black", fill: "#000" });
        this.text2.setStroke('#fff', 16);
        this.text2.setShadow(2, 2, "#333333", 2, true, true);
    }

    update ()
    {
        const top = this.hsv[this.i].color;
        const bottom = this.hsv[359 - this.i].color;

        this.text1.setTint(top, top, bottom, bottom);
        this.text2.setTint(top, bottom, top, bottom);

        this.i++;

        if (this.i === 360)
        {
            this.i = 0;
        }
    }
}

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

const game = new Phaser.Game(config);

Подготовка цветовой палитры

В основе анимации лежит заранее сгенерированный массив из 360 цветов, который представляет собой полный цветовой круг. Phaser предоставляет для этого удобный статический метод.

this.hsv = Phaser.Display.Color.HSVColorWheel();

Вызов Phaser.Display.Color.HSVColorWheel() создает массив объектов. Каждый объект содержит цвет в формате RGB (color) и соответствующие значения HSV (оттенок, насыщенность, яркость). Индекс элемента в массиве (от 0 до 359) напрямую соответствует углу на цветовом круге. Это дает нам готовую палитру для плавного перебора всех оттенков радуги.

Создание текстовых объектов

В примере создаются два текстовых блока с разными стилями, чтобы продемонстрировать различные варианты окрашивания.

//  Rainbow Text
this.text1 = this.add.text(50, 100, 'Rainbow Text', { font: "74px Arial Black", fill: "#fff" });
this.text1.setStroke('#00f', 16);
this.text1.setShadow(2, 2, "#333333", 2, true, true);

//  Rainbow Stroke
this.text2 = this.add.text(50, 300, 'Rainbow Stroke', { font: "74px Arial Black", fill: "#000" });
this.text2.setStroke('#fff', 16);
this.text2.setShadow(2, 2, "#333333", 2, true, true);

Первый текст (text1) имеет белую заливку (fill: "#fff") и синюю обводку (setStroke('#00f', 16)). Второй (text2) — наоборот, черную заливку и белую обводку. Оба используют тень для объема. Исходные цвета (fill и stroke) будут перезаписаны в цикле анимации с помощью метода setTint().

Магия метода setTint()

Ключевой метод для анимации — setTint(topLeft, topRight, bottomLeft, bottomRight). Он применяет оттенок (тинт) к каждому из четырех углов игрового объекта (текста в нашем случае), создавая градиентную заливку. Цвета смешиваются между углами.

const top = this.hsv[this.i].color;
const bottom = this.hsv[359 - this.i].color;

this.text1.setTint(top, top, bottom, bottom);
this.text2.setTint(top, bottom, top, bottom);

В коде мы берем два цвета из нашей палитры: top — цвет по текущему индексу `i,bottom— цвет с противоположной стороны круга (индекс359 - i`). - Для text1 задается вертикальный градиент: оба верхних угла — цвет top, оба нижних — bottom. - Для text2 задается более сложный паттерн: верхний левый и нижний правый — top, верхний правый и нижний левый — bottom, что создает диагональный или крестообразный эффект.

Важно: setTint полностью заменяет исходные цвета текста (fill и stroke), игнорируя их.

Цикл анимации в update()

Анимация реализуется в методе update(), который вызывается на каждом кадре. Мы инкрементируем индекс `i`, чтобы перемещаться по цветовому кругу, и сбрасываем его, когда проходим полный цикл.

update ()
{
    // ... (расчет top, bottom и вызов setTint) ...

    this.i++;

    if (this.i === 360)
    {
        this.i = 0;
    }
}

Поскольку массив hsv содержит ровно 360 элементов, увеличение `iот 0 до 359 обеспечивает плавный переход через все оттенки. Сброс на 0 замыкает цикл, делая анимацию бесконечной. Использование противоположного цвета (359 - i`) гарантирует, что градиент всегда будет контрастным и динамичным.

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

Используя Phaser.Display.Color.HSVColorWheel() и setTint(), вы можете легко добавлять сложные цветовые переходы к любому игровому объекту, поддерживающему тинтирование — не только к тексту, но и к изображениям или спрайтам. Для экспериментов попробуйте изменить логику выбора цветов: например, использовать не противоположные, а соседние оттенки для более плавного градиента, или анимировать только обводку, оставив заливку static. Также можно привязать скорость изменения цвета (this.i++) к игровому времени для независимости от частоты кадров.