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

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

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

Живой запуск

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

Исходный код


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

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.spritesheet('diamonds', 'assets/sprites/diamonds32x24x5.png', { frameWidth: 32, frameHeight: 24 });
    }

    create ()
    {
        const config = {
            key: 'flash',
            frames: this.anims.generateFrameNumbers('diamonds', { start: 0, end: 4 }),
            frameRate: 1,
            repeat: -1
        };

        this.anims.create(config);

        const group = this.add.group();

        group.createMultiple({ key: 'diamonds', frame: 0, repeat: 279 });

        Phaser.Actions.GridAlign(group.getChildren(), {
            width: 20,
            height: 20,
            cellWidth: 38,
            x: 19,
            y: 25
        });

        this.anims.staggerPlay('flash', group.getChildren(), 60);
    }
}

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

const game = new Phaser.Game(config);

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

Перед использованием staggerPlay необходимо создать анимацию и группу объектов, которые будут её проигрывать. В методе preload загружается спрайтшит, содержащий кадры для анимации. В create сначала создается конфигурация анимации, а затем группа, заполненная множеством спрайтов.

const config = {
    key: 'flash',
    frames: this.anims.generateFrameNumbers('diamonds', { start: 0, end: 4 }),
    frameRate: 1,
    repeat: -1
};

this.anims.create(config);

const group = this.add.group();
group.createMultiple({ key: 'diamonds', frame: 0, repeat: 279 });

Ключевые моменты: - key: 'flash' — уникальное имя анимации для последующего вызова. - generateFrameNumbers — генерирует массив кадров из спрайтшита 'diamonds' с индексами от 0 до 4. - repeat: -1 — анимация будет зациклена бесконечно. - group.createMultiple — создает 280 спрайтов (1 начальный + 279 повторов) с начальным кадром frame: 0.

Выравнивание объектов в сетку

Чтобы объекты группы отображались упорядоченно, используется Phaser.Actions.GridAlign. Этот метод распределяет дочерние элементы группы по виртуальной сетке с заданными параметрами.

Phaser.Actions.GridAlign(group.getChildren(), {
    width: 20,
    height: 20,
    cellWidth: 38,
    x: 19,
    y: 25
});

Параметры конфигурации: - width: 20 и height: 20 — определяют размер сетки 20x20, что идеально подходит для 280 спрайтов (14 строк * 20 столбцов = 280). - cellWidth: 38 — задает горизонтальное расстояние между центрами спрайтов. Значение больше ширины спрайта (32px) предотвращает наложение. - x: 19 и y: 25 — смещение начальной позиции сетки от левого верхнего угла сцены. Метод group.getChildren() возвращает массив всех созданных спрайтов для операции выравнивания.

Запуск анимации с задержкой

Самый важный этап — вызов this.anims.staggerPlay. Этот метод запускает указанную анимацию на всех объектах массива, но не одновременно, а с заданным временным интервалом между запусками для каждого следующего объекта.

this.anims.staggerPlay('flash', group.getChildren(), 60);

Аргументы метода: 1. 'flash' — ключ анимации, которую нужно проигрывать. Должен совпадать с key в конфигурации, созданной ранее. 2. group.getChildren() — массив игровых объектов (спрайтов), которые должны проигрывать анимацию. 3. 60 — задержка в миллисекундах между запуском анимации на одном объекте и на следующем. Значение 60 создает плавную волну. В результате анимация 'flash' запустится на первом спрайте, через 60 мс — на втором, еще через 60 мс — на третьем и так далее, создавая эффект последовательного мерцания по сетке.

Как работает задержка и визуальный эффект

Задержка применяется не к кадрам самой анимации, а к моменту её старта на каждом объекте. Это означает, что все спрайты будут независимо проигрывать одну и ту же циклическую анимацию, но начало воспроизведения у них будет смещено во времени.

Поскольку в нашем примере анимация 'flash' имеет frameRate: 1 (один кадр в секунду) и состоит из 5 кадров, полный цикл на одном спрайте длится 5 секунд. Из-за staggerPlay с задержкой 60 мс, соседние спрайты в сетке будут находиться на разных фазах этого цикла. Это создает иллюзию бегущей волны или последовательного включения объектов, что гораздо интереснее, чем статичное синхронное мигание.

Важно: метод staggerPlay автоматически запускает анимацию на каждом переданном объекте. Вручную вызывать play для каждого спрайта не требуется.

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

Метод anims.staggerPlay — это мощный и простой инструмент для добавления динамики в сцены с множеством одинаковых анимированных объектов. Он избавляет от необходимости писать циклы с таймерами вручную. Для экспериментов попробуйте изменить параметр задержки на большее (например, 200) или меньшее (20) значение, чтобы увидеть, как меняется характер "волны". Также можно применить этот метод к объектам, выстроенным не в сетку, а, например, по окружности с помощью Phaser.Actions.PlaceOnCircle, чтобы создать спиральный или круговой эффект активации.