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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    rt;
    trail;
    player;
    tween;

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

    create ()
    {
        this.rt = this.add.renderTexture(0, 0, 800, 600).setOrigin(0, 0);

        this.trail = this.add.image(400, 300, 'bubble').setVisible(false);

        this.player = this.add.image(400, 300, 'bubble');

        this.tween = this.tweens.add({
            targets: this.trail,
            x: this.player.x,
            y: this.player.y,
            ease: 'Sine.easeInOut',
            duration: 50000,
            repeat: -1
        });
    }

    update ()
    {
        this.player.x = this.input.x;
        this.player.y = this.input.y;

        const dist = Phaser.Math.Distance.Between(this.trail.x, this.trail.y, this.player.x, this.player.y);

        this.tween.timeScale = dist / 100;

        this.tween.updateTo('x', this.player.x, true);
        this.tween.updateTo('y', this.player.y, true);

        this.trail.setAlpha(100 / (dist + 0.001));
        this.trail.setTint(dist | 0xff0000);

        this.rt.draw(this.trail).render();
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и создание ключевых объектов

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

В методе `create` инициализируются три ключевых объекта:
1.  **Render Texture (`this.rt`)**: Это холст в памяти, на котором мы будем последовательно отрисовывать кадры шлейфа. Он создается на весь размер игрового окна (800x600) и привязывается к его началу.
2.  **Невидимый след (`this.trail`)**: Это тот же спрайт `bubble`, но изначально скрытый. Он будет служить «кистью», которой мы рисуем на `RenderTexture`.
3.  **Игрок (`this.player`)**: Видимый спрайт, который следует за курсором мыши. Он представляет собой головную точку шлейфа.

Также создается бесконечный Tween для объекта trail. Его цель — плавно следовать за игроком, но с огромной задержкой (50 секунд). Это создает базовое отставание.

create ()
{
    this.rt = this.add.renderTexture(0, 0, 800, 600).setOrigin(0, 0);
    this.trail = this.add.image(400, 300, 'bubble').setVisible(false);
    this.player = this.add.image(400, 300, 'bubble');
    this.tween = this.tweens.add({
        targets: this.trail,
        x: this.player.x,
        y: this.player.y,
        ease: 'Sine.easeInOut',
        duration: 50000,
        repeat: -1
    });
}

Динамическое управление следом в update

Основная логика эффекта выполняется каждый кадр в методе update. Сначала позиция спрайта игрока обновляется в соответствии с координатами курсора.

Затем вычисляется критически важный параметр — расстояние (dist) между текущей позицией «кисти» (trail) и игроком.

update ()
{
    this.player.x = this.input.x;
    this.player.y = this.input.y;
    const dist = Phaser.Math.Distance.Between(this.trail.x, this.trail.y, this.player.x, this.player.y);
}

Это расстояние используется для динамической настройки Tween и внешнего вида следа: 1. **Скорость анимации (timeScale)**: Чем больше расстояние, тем быстрее trail будет пытаться догнать player. Это делает шлейф отзывчивым к резким движениям. 2. **Обновление цели Tween**: Целевые координаты Tween (`xиy) постоянно переназначаются на текущие координаты игрока, заставляяtrail` всегда стремиться к нему. 3. **Прозрачность (alpha)**: Прозрачность «кисти» обратно пропорциональна расстоянию. Когда trail далеко от игрока, он почти прозрачный. При сближении он становится ярче. Добавление малого числа (0.001) предотвращает деление на ноль. 4. **Цвет (tint)**: Цвет «кисти» напрямую зависит от расстояния. Побитовая операция dist | 0xff0000 использует расстояние для вычисления красной компоненты цвета, создавая плавный цветовой градиент.

this.tween.timeScale = dist / 100;
this.tween.updateTo('x', this.player.x, true);
this.tween.updateTo('y', this.player.y, true);
this.trail.setAlpha(100 / (dist + 0.001));
this.trail.setTint(dist | 0xff0000);

Визуализация шлейфа через Render Texture

Самая важная строка кода — финальный вызов в update. Метод draw объекта RenderTexture (this.rt) рисует на своем холсте текущее состояние спрайта trail (с его позицией, прозрачностью и цветом).

Затем вызывается .render(), чтобы зафиксировать это изображение. Поскольку RenderTexture не очищается автоматически каждый кадр, все нарисованные кадры trail накапливаются, создавая иллюзию непрерывного шлейфа. Резкие движения игрока увеличивают расстояние, что делает след ярче, цветнее и заставляет его быстрее «подтягиваться», визуально растягивая шлейф.

this.rt.draw(this.trail).render();

Таким образом, весь эффект достигается не созданием сотен частиц, а многократной отрисовкой одного изменяющегося объекта на персистентный холст.

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

Этот пример наглядно показывает, как комбинация простых инструментов Phaser — Tween, RenderTexture и базовых математических расчетов — позволяет создавать сложные и красивые визуальные эффекты с минимальными затратами ресурсов. Для экспериментов попробуйте изменить формулу для tint, чтобы получить другие цветовые схемы, добавьте размытие или изменение масштаба trail в зависимости от расстояния, или замените одно изображение на спрайт-лист для анимированного шлейфа.