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

Анимация — это один из ключевых способов добавить динамику и привлекательность вашей игре. Пример с вращающимися кубами демонстрирует, как с помощью всего нескольких мощных инструментов Phaser можно создать сложный визуальный эффект. В этой статье мы разберем, как использовать группы объектов (`Group`), систему анимаций и `Tweens` для управления камерой, чтобы создать завораживающую гипнотическую сцену, которую легко адаптировать под свои нужды.

Версия 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.atlas('cube', 'assets/animations/cube.png', 'assets/animations/cube.json');
    }

    create ()
    {
        this.anims.create({
            key: 'spin',
            frames: this.anims.generateFrameNames('cube', { prefix: 'frame', start: 1, end: 23 }),
            frameRate: 50,
            repeat: -1
        });

        const group = this.add.group({ key: 'cube', frame: 'frame1', repeat: 107, setScale: { x: 0.55, y: 0.55 } });

        Phaser.Actions.GridAlign(group.getChildren(), { width: 12, cellWidth: 70, cellHeight: 70, x: -20, y: 0 });

        let i = 1;
        let ci = 0;
        let colors = [ 0xef658c, 0xff9a52, 0xffdf00, 0x31ef8c, 0x21dfff, 0x31aade, 0x5275de, 0x9c55ad, 0xbd208c ];

        group.children.forEach(function (child) {

            child.tint = colors[ci];

            if (i % 12 === 0)
            {
                i = 1;
                ci++;
            }
            else
            {
                i++;
            }

        });

        this.anims.staggerPlay('spin', group.getChildren(), 0.03);

        this.cameras.main.zoom = 0.8;

        this.tweens.add({
            targets: this.cameras.main,
            props: {
                zoom: { value: 2.5, duration: 4000, ease: 'Sine.easeInOut' },
                rotation: { value: 2.3, duration: 8000, ease: 'Cubic.easeInOut' }
            },
            delay: 2000,
            yoyo: true,
            repeat: -1
        });
    }
}

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

const game = new Phaser.Game(config);

Подготовка и загрузка анимации

В основе эффекта лежит спрайт-лист (atlas) с кадрами вращающегося куба. В методе preload мы загружаем этот atlas.

this.load.atlas('cube', 'assets/animations/cube.png', 'assets/animations/cube.json');

Далее, в create, мы создаем саму анимацию. Ключевой метод this.anims.generateFrameNames автоматически сгенерирует массив кадров для анимации, используя соглашение об именах файлов в JSON atlas'е. Это избавляет от необходимости указывать каждый кадр вручную.

this.anims.create({
    key: 'spin',
    frames: this.anims.generateFrameNames('cube', { prefix: 'frame', start: 1, end: 23 }),
    frameRate: 50,
    repeat: -1
});

Параметры анимации: - key: 'spin' — уникальное имя для последующего воспроизведения. - frames — массив кадров, сгенерированный для имен с frame1 по frame23. - frameRate: 50 — высокая скорость смены кадров для плавного вращения. - repeat: -1 — анимация будет повторяться бесконечно.

Создание и раскраска сетки объектов

Вместо создания 108 кубов вручную мы используем мощный инструмент — Group. Группа позволяет управлять множеством игровых объектов как единым целым.

const group = this.add.group({ key: 'cube', frame: 'frame1', repeat: 107, setScale: { x: 0.55, y: 0.55 } });

Эта строка создает группу, которая автоматически добавит в сцену 108 спрайтов (1 исходный + 107 повторений) с текстурой 'cube' и начальным кадром 'frame1'. Каждому спрайту сразу применяется масштаб 0.55.

Теперь нужно расположить объекты в аккуратную сетку 12x9. Для этого используется Phaser.Actions.GridAlign.

Phaser.Actions.GridAlign(group.getChildren(), { width: 12, cellWidth: 70, cellHeight: 70, x: -20, y: 0 });

Метод принимает массив детей группы и параметры сетки: количество объектов в строке (width), размер ячейки и смещение всей сетки.

Следующий шаг — раскрасить строки сетки в разные цвета. Мы проходим по всем детям группы с помощью forEach, применяя цвет (tint) из массива colors. Логика с переменными `iиci` обеспечивает смену цвета каждые 12 объектов (после завершения строки).

group.children.forEach(function (child) {
    child.tint = colors[ci];
    if (i % 12 === 0) {
        i = 1;
        ci++;
    } else {
        i++;
    }
});

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

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

this.anims.staggerPlay('spin', group.getChildren(), 0.03);

Здесь анимация 'spin' запускается на всех детях группы с задержкой в 0.03 секунды между соседними объектами.

Финальный штрих — анимированная камера. Сначала мы немного отдаляем вид, устанавливая zoom.

this.cameras.main.zoom = 0.8;

Затем создается Tween — плавный переход для свойств камеры. Tween будет бесконечно (repeat: -1) двигать камеру вперед-назад (yoyo: true), изменяя зум и вращение с заданными длительностями и функциями плавности (ease). Задержка (delay) в 2 секунды дает игроку время рассмотреть первоначальную композицию.

this.tweens.add({
    targets: this.cameras.main,
    props: {
        zoom: { value: 2.5, duration: 4000, ease: 'Sine.easeInOut' },
        rotation: { value: 2.3, duration: 8000, ease: 'Cubic.easeInOut' }
    },
    delay: 2000,
    yoyo: true,
    repeat: -1
});

Именно это сочетание — вращающиеся объекты и двигающаяся камера — создает глубокий гипнотический эффект.

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

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