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

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

Версия 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.image('bg', 'assets/tweens/space.png');
        this.load.image('block', 'assets/sprites/50x50.png');
    }

    create ()
    {
        this.add.image(400, 300, 'bg');

        const blocks = this.add.group({ key: 'block', repeat: 107, setScale: { x: 0.1, y: 0.1 } });

        Phaser.Actions.GridAlign(blocks.getChildren(), {
            width: 12,
            height: 9,
            cellWidth: 60,
            cellHeight: 60,
            x: 40,
            y: 30
        });

        let i = 0;

        blocks.children.forEach(child => {

            this.tweens.add({
                targets: child,
                scale: 1,
                angle: 180,
                ease: 'Power2',
                duration: 1000,
                delay: i * 50,
                repeat: -1,
                yoyo: true,
                hold: 1000,
                repeatDelay: 1000
            });

            i++;

            if (i % 12 === 0)
            {
                i = 0;
            }

        });
    }
}

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

const game = new Phaser.Game(config);

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

Класс Example расширяет Phaser.Scene и содержит стандартные методы жизненного цикла сцены.

В методе preload() мы задаём базовый URL для загрузки и загружаем два изображения: фоновую картинку 'bg' и спрайт блока 'block'. Эти ресурсы будут использоваться для визуализации.

this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/tweens/space.png');
this.load.image('block', 'assets/sprites/50x50.png');

Создание группы объектов и их выравнивание

В методе create() сначала добавляется фоновое изображение.

Затем создаётся группа (Group) под названием blocks. Группа — это удобный контейнер для управления множеством однотипных объектов. Параметр key указывает текстуру для всех объектов группы. Параметр repeat: 107 говорит о том, что всего объектов будет 108 (исходный + 107 повторений). setScale задаёт начальный масштаб каждому создаваемому спрайту.

const blocks = this.add.group({ key: 'block', repeat: 107, setScale: { x: 0.1, y: 0.1 } });

Далее используется статический метод Phaser.Actions.GridAlign для автоматического расположения всех детей группы в сетку 12x9. Параметры cellWidth и cellHeight задают размер ячейки, а `xиy` — начальное смещение сетки.

Phaser.Actions.GridAlign(blocks.getChildren(), {
    width: 12,
    height: 9,
    cellWidth: 60,
    cellHeight: 60,
    x: 40,
    y: 30
});

Настройка анимации для каждого спрайта

После размещения объектов в сетку мы проходимся по всем детям группы с помощью forEach.

Для каждого ребёнка (child) создаётся твин (tween) — объект, управляющий плавной анимацией свойств. Твин создаётся через this.tweens.add().

this.tweens.add({
    targets: child,
    scale: 1,
    angle: 180,
    ease: 'Power2',
    duration: 1000,
    delay: i * 50,
    repeat: -1,
    yoyo: true,
    hold: 1000,
    repeatDelay: 1000
});

Ключевые параметры твина: - targets: объект, который будет анимироваться. - scale и angle: целевые значения масштаба (до 1) и угла поворота (180 градусов). - ease: функция плавности анимации. - duration: длительность одного цикла анимации (в миллисекундах). - delay: задержка перед стартом анимации для данного объекта. Она рассчитывается как i * 50, что создаёт эффект волны или шахматного порядка. - repeat: -1: анимация повторяется бесконечно. - yoyo: true: после завершения цикла анимация проигрывается в обратном порядке. - hold: пауза в конце прямого цикла перед началом обратного. - repeatDelay: пауза между полными циклами (прямой + обратный).

Создание шахматного порядка задержек

Переменная `iиспользуется для расчёта задержки (delay) для каждого спрайта. После обработки каждого спрайтаi` увеличивается на единицу.

Условие if (i % 12 === 0) сбрасывает счётчик `i` обратно в 0, когда обработано 12 спрайтов (ширина нашей сетки). Это означает, что задержки будут повторяться для каждого нового ряда, создавая характерный волнообразный или шахматный паттерн анимации.

let i = 0;

blocks.children.forEach(child => {
    // ... создание твина с delay: i * 50 ...
    i++;
    if (i % 12 === 0)
    {
        i = 0;
    }
});

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

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

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

const game = new Phaser.Game(config);

После создания экземпляра game Phaser автоматически начнёт выполнение жизненного цикла сцены.

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

Мы разобрали пример, который демонстрирует мощь комбинации Group, Phaser.Actions для управления множеством объектов и Tweens для создания сложных повторяющихся анимаций. Для экспериментов попробуйте изменить параметры сетки (например, width и height), поиграть со значениями delay, hold и repeatDelay в твинах, или заменить анимируемые свойства — например, анимируйте alpha (прозрачность) или `x,y` (позицию).