О чем этот пример
Создание сложных визуальных эффектов — ключ к запоминающимся играм. В этом примере мы разберем, как с помощью всего четырех спрайтов и контейнеров Phaser построить гипнотическую спиральную анимацию. Вы научитесь эффективно управлять множеством объектов, применяя твины к целым группам и настраивая задержки для каждого элемента. Этот подход экономит ресурсы и открывает двери для создания фоновых анимаций, эффектов заклинаний или психоделических переходов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('raster', 'assets/demoscene/rastercarpet32.png');
}
create ()
{
const sprite1 = this.add.sprite(0, 100, 'raster');
const sprite2 = this.add.sprite(-100, 0, 'raster').setAngle(90);
const sprite3 = this.add.sprite(0, -100, 'raster').setAngle(180);
const sprite4 = this.add.sprite(100, 0, 'raster').setAngle(270);
const containers = [];
for (let i = 0; i < 128; i++)
{
const container = this.add.container(400, 300);
if (i > 0)
{
container.setExclusive(false);
}
container.add([ sprite1, sprite2, sprite3, sprite4 ]);
containers.push(container);
}
this.tweens.add({
targets: sprite1,
y: -200,
ease: 'Sine.easeInOut',
duration: 4000,
repeat: -1,
yoyo: true
});
this.tweens.add({
targets: sprite2,
x: 300,
ease: 'Sine.easeInOut',
duration: 4000,
repeat: -1,
yoyo: true
});
this.tweens.add({
targets: sprite3,
y: 200,
ease: 'Sine.easeInOut',
duration: 4000,
repeat: -1,
yoyo: true
});
this.tweens.add({
targets: sprite4,
x: -300,
ease: 'Sine.easeInOut',
duration: 4000,
repeat: -1,
yoyo: true
});
this.tweens.add({
targets: containers,
angle: { value: 360, duration: 6000 },
scaleX: { value: 2, duration: 3000, yoyo: true, ease: 'Quad.easeInOut' },
scaleY: { value: 4, duration: 3000, yoyo: true, ease: 'Cubic.easeInOut' },
repeat: -1,
delay: function (target, key, value, index, total, tween)
{
return index * 32;
}
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ассетов
Вся логика примера содержится в классе сцены Example. В методе preload мы загружаем единственное изображение — текстуру 'raster'. Важно отметить использование setBaseURL, которое задает базовый путь для всех последующих загрузок, что удобно для работы с удаленными ресурсами.
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('raster', 'assets/demoscene/rastercarpet32.png');
Создание базовых спрайтов и их позиционирование
В методе create создаются четыре спрайта из одной текстуры. Они размещаются по четырем сторонам от условного центра (точки с координатами 0,0) и поворачиваются на 90 градусов друг относительно друга с помощью метода setAngle. Эти спрайты станут «кирпичиками» для нашего сложного составного объекта.
const sprite1 = this.add.sprite(0, 100, 'raster');
const sprite2 = this.add.sprite(-100, 0, 'raster').setAngle(90);
const sprite3 = this.add.sprite(0, -100, 'raster').setAngle(180);
const sprite4 = this.add.sprite(100, 0, 'raster').setAngle(270);
Формирование массива контейнеров
Здесь происходит основная магия. В цикле создается 128 контейнеров (this.add.container). Каждый контейнер размещается в центре экрана (400, 300). Метод setExclusive(false) вызывается для всех контейнеров, кроме первого. Это ключевой момент: он позволяет спрайтам принадлежать нескольким контейнерам одновременно, что и делает анимацию такой легковесной. В каждый контейнер добавляется один и тот же набор из четырех спрайтов.
const containers = [];
for (let i = 0; i < 128; i++) {
const container = this.add.container(400, 300);
if (i > 0) {
container.setExclusive(false);
}
container.add([ sprite1, sprite2, sprite3, sprite4 ]);
containers.push(container);
}
Анимация исходных спрайтов
Чтобы создать динамику внутри каждого контейнера, к каждому из четырех базовых спрайтов применяется отдельный твин. Спрайты движутся по оси X или Y, используя плавную Sine.easeInOut анимацию с бесконечным повторением (repeat: -1) и возвратом (yoyo: true). Это заставляет «лопасти» внутри контейнеров постоянно колебаться.
this.tweens.add({
targets: sprite2,
x: 300,
ease: 'Sine.easeInOut',
duration: 4000,
repeat: -1,
yoyo: true
});
// ... аналогичные твины для sprite1, sprite3, sprite4
Массовая анимация всех контейнеров
Самый мощный твин применяется ко всему массиву containers. Он одновременно вращает контейнеры на 360 градусов и меняет их масштаб по осям X и Y с разной динамикой (разные длительности и функции плавности ease). Параметр delay — это функция, которая вычисляет задержку старта анимации для каждого контейнера в зависимости от его индекса в массиве. Это создает эффект волны или спирали.
this.tweens.add({
targets: containers,
angle: { value: 360, duration: 6000 },
scaleX: { value: 2, duration: 3000, yoyo: true, ease: 'Quad.easeInOut' },
scaleY: { value: 4, duration: 3000, yoyo: true, ease: 'Cubic.easeInOut' },
repeat: -1,
delay: function (target, key, value, index, total, tween) {
return index * 32;
}
});
Что попробовать дальше
Комбинируя контейнеры, общие спрайты и твины с задержкой, можно генерировать невероятно сложные на вид эффекты с минимальным количеством графических объектов. Для экспериментов попробуйте изменить количество контейнеров, формулу задержки в функции delay, параметры масштабирования или добавьте твин для изменения прозрачности (alpha). Это отличная основа для создания вращающихся ловушек, порталов или гипнотических фоновых узоров.
