О чем этот пример
Анимация — это один из ключевых способов добавить динамику и привлекательность вашей игре. Пример с вращающимися кубами демонстрирует, как с помощью всего нескольких мощных инструментов 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 к любому другому действию, например, к изменению прозрачности или размера объектов, создавая уникальные волновые эффекты на ваших игровых полях.
