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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();

        this.rope;
        this.count = 0;
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('bg', 'assets/rope/background-red.png');
        this.load.image('banner', 'assets/rope/fade-one.png');
    }

    create ()
    {
        this.add.image(512, 300, 'bg').setAlpha(0.5);

        this.rope = this.add.rope(400, 300, 'banner', null, 20);

        const fx = this.rope.enableFilters().filters.external.addGlow(0xffffff, 4, 0, 1, false, 10, 32);

        this.tweens.add({
            targets: fx,
            outerStrength: 10,
            yoyo: true,
            loop: -1,
            ease: 'sine.inout'
        });
    }

    update ()
    {
        this.count += 0.1;

        let points = this.rope.points;

        for (let i = 0; i < points.length; i++)
        {
            points[i].y = Math.sin(i * 0.5 + this.count) * 16;
        }

        this.rope.setDirty();
    }
}

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

const game = new Phaser.Game(config);

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

Ключевой объект в этом примере — Rope. Это особый игровой объект (Game Object), представляющий собой растяжимую ленту, состоящую из сегментов (точек). Его форма задаётся массивом точек и может динамически меняться, что мы и используем для анимации.

В методе preload загружаются два изображения: фон и текстура для самой верёвки.

В create мы сначала добавляем фоновое изображение, а затем создаём объект Rope. Конструктор принимает начальные координаты, ключ текстуры, опорную точку (здесь null, что означает центр) и количество сегментов.

this.rope = this.add.rope(400, 300, 'banner', null, 20);

После создания объект rope содержит массив points из 20 элементов, определяющих его форму.

Применение и настройка фильтра свечения

Phaser 3 предоставляет систему пост-обработки — фильтры. Чтобы применить их к объекту, нужно сначала активировать слот для фильтров с помощью метода enableFilters(), а затем добавить нужный фильтр.

Фильтр GlowFilter создаёт эффект внешнего или внутреннего свечения. Мы добавляем его через менеджер внешних фильтров filters.external.

const fx = this.rope.enableFilters().filters.external.addGlow(0xffffff, 4, 0, 1, false, 10, 32);

Разберём параметры: 1. 0xffffff — цвет свечения (белый). 2. `4` — расстояние размытия (в пикселях). 3. `0` — качество размытия. 4. `1` — сила внутреннего свечения. 5. false — отключение режима knockout (когда объект становится невидимым, остаётся только свечение). 6. 10 — сила внешнего свечения. 7. 32 — количество шагов для отрисовки градиента свечения (качество).

Сразу после создания мы запускаем твин для параметра outerStrength, чтобы сила внешнего свечения пульсировала.

this.tweens.add({
    targets: fx,
    outerStrength: 10,
    yoyo: true,
    loop: -1,
    ease: 'sine.inout'
});

Динамическое обновление геометрии в update

Вся магия движения верёвки происходит в методе update, который вызывается на каждом кадре. Мы изменяем координату `yкаждой точки в массивеthis.rope.points` по синусоидальному закону.

Переменная this.count увеличивается каждый кадр, создавая постоянное смещение фазы волны.

this.count += 0.1;

let points = this.rope.points;

for (let i = 0; i < points.length; i++)
{
    points[i].y = Math.sin(i * 0.5 + this.count) * 16;
}

Здесь i * 0.5 определяет частоту волны вдоль верёвки, а * 16 — её амплитуду.

После изменения координат точек необходимо вручную сообщить системе рендеринга, что объект изменился. Для этого вызывается метод setDirty().

this.rope.setDirty();

Без этого вызова визуальная форма Rope не обновится, хотя данные в массиве точек изменятся.

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

Пример завершается стандартной для Phaser 3 конфигурацией игры. В объекте config мы задаём тип рендерера, размеры холста, цвет фона, ID родительского HTML-элемента и класс нашей сцены Example.

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

const game = new Phaser.Game(config);

Инициализация игры с этой конфигурацией автоматически создаст экземпляр нашей сцены и запустит игровой цикл.

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

Комбинирование динамической геометрии объекта Rope с анимированными фильтрами пост-обработки открывает широкие возможности для создания сложных визуальных эффектов с минимальным кодом. Для экспериментов попробуйте: 1. Изменить форму волны в update, используя Math.cos или комбинацию функций. 2. Анимировать другие параметры фильтра, например, innerStrength или color. 3. Привязать изменение параметров свечения к положению мыши или игровым событиям. 4. Использовать текстуру с альфа-каналом для более сложных форм верёвки.