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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('brush', 'assets/sprites/brush2.png');
    }

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

        this.emitter = this.add.particles(400, 100, 'brush', {
            speedX: 200,
            lifespan: 2000,
            tint: this.hsv[0].color,
        });

        this.tweens.add({
            targets: this.emitter,
            particleY: 400,
            speedX: -200,
            duration: 1500,
            ease: 'sine.inout',
            yoyo: true,
            repeat: -1
        });
    }

    update ()
    {
        this.i++;

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

        this.emitter.particleTint = this.hsv[this.i].color;
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и загрузка ассетов

В методе preload() мы загружаем единственный ассет — текстуру для частицы. Важно использовать изображение с прозрачным фоном (например, PNG), чтобы цветовой тон применялся корректно.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('brush', 'assets/sprites/brush2.png');
}

Здесь 'brush' — это ключ текстуры, а 'assets/sprites/brush2.png' — путь к файлу. Метод setBaseURL задает базовый URL для упрощения загрузки из удаленного репозитория.

Создание эмиттера частиц и цветового спектра

В методе create() происходит основная настройка. Сначала мы генерируем цветовое колесо HSV — массив из 360 объектов, каждый из которых содержит цвет в числовом формате для каждого градуса тона.

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

Затем создается эмиттер частиц с помощью this.add.particles(). Первые два аргумента (400, 100) — это начальные координаты X и Y эмиттера. Третий аргумент — ключ текстуры 'brush'. Четвертый аргумент — объект конфигурации частиц.

this.emitter = this.add.particles(400, 100, 'brush', {
    speedX: 200,
    lifespan: 2000,
    tint: this.hsv[0].color,
});

В конфигурации мы задаем начальную скорость по оси X (speedX: 200), время жизни частицы в миллисекундах (lifespan: 2000) и начальный цветовой тон (tint), взятый из первого элемента массива hsv.

Сразу после создания эмиттера мы добавляем твин для анимации его движения. Твин меняет свойство particleY (фиксированную Y-координату для новых частиц) и speedX (скорость по X), создавая колебательное движение.

this.tweens.add({
    targets: this.emitter,
    particleY: 400,
    speedX: -200,
    duration: 1500,
    ease: 'sine.inout',
    yoyo: true,
    repeat: -1
});

Параметр yoyo: true заставляет анимацию проигрываться в обратном порядке, а repeat: -1 делает ее бесконечной.

Динамическое обновление цвета в update()

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

this.i++;

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

Затем мы берем цвет из массива hsv по текущему индексу `iи присваиваем его свойствуparticleTint` эмиттера. Это свойство отвечает за цветовой тон всех вновь создаваемых частиц.

this.emitter.particleTint = this.hsv[this.i].color;

Важно понимать, что particleTint применяется только к новым частицам. Частицы, уже выпущенные эмиттером, сохраняют свой исходный цвет до конца своего времени жизни (lifespan). Это создает плавный градиентный эффект, когда новые частицы одного цвета смешиваются со старыми другого.

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

Пример завершается стандартной конфигурацией игры Phaser. Ключевые параметры: тип рендерера (type), размеры холста (width, height), цвет фона (backgroundColor) и указание основного класса сцены (scene).

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

const game = new Phaser.Game(config);

Экземпляр игры создается с этой конфигурацией, что инициирует выполнение методов preload(), create(), а затем циклический вызов update().

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

Вы освоили технику динамического изменения цвета частиц в Phaser 3, используя свойство particleTint и предварительно сгенерированную палитру HSV. Это мощный инструмент для добавления «живости» эффектам. **Идеи для экспериментов:** 1. Свяжите изменение цвета не со счетчиком кадров, а со временем игры, используя this.time.now для более плавного и независимого от FPS перехода. 2. Примените этот метод к нескольким эмиттерам с разной скоростью смены цвета для создания сложных композиций. 3. Используйте не линейный перебор цветового круга, а случайный выбор из массива или переход между двумя заданными цветами для конкретных игровых событий (например, урон — красный, лечение — зеленый). 4. Поэкспериментируйте с другими свойствами частиц в update(), например, динамически меняйте speedY, scale или alpha.