О чем этот пример
Визуальные эффекты движения по сложным траекториям — ключевой элемент многих игр, от следования врагов за игроком до создания гипнотических фоновых анимаций. В этом примере мы разберем, как создать волнообразный путь с помощью кривых 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('eyes', 'assets/sprites/eyes.png');
this.load.image('orb', 'assets/particles/green-orb.png');
}
create ()
{
const graphics = this.add.graphics();
const path = new Phaser.Curves.Path(0, 300);
for (let i = 0; i < 8; i++)
{
// xRadius, yRadius, startAngle, endAngle, clockwise, rotation
if (i % 2 === 0)
{
path.ellipseTo(50, 80, 180, 360, true, 0);
}
else
{
path.ellipseTo(50, 80, 180, 360, false, 0);
}
}
graphics.lineStyle(1, 0xffffff, 1);
path.draw(graphics);
for (let i = 0; i < 20; i++)
{
let follower;
if (i === 0)
{
follower = this.add.follower(path, 100, 100 + (30 * i), 'eyes').setDepth(50);
}
else
{
follower = this.add.follower(path, 100, 100 + (30 * i), 'orb');
follower.setBlendMode(Phaser.BlendModes.ADD);
follower.setScale(0.5);
}
follower.startFollow({
duration: 4000,
positionOnPath: true,
repeat: -1,
ease: 'Linear',
delay: i * 70
});
}
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Создание сложного пути с помощью Path и ellipseTo
В Phaser 3 класс Phaser.Curves.Path позволяет создавать составные пути из различных сегментов: линий, сплайнов, дуг и эллипсов. В нашем примере мы создаем волнообразный путь, последовательно добавляя к нему полуэллипсы.
const path = new Phaser.Curves.Path(0, 300);
for (let i = 0; i < 8; i++)
{
if (i % 2 === 0)
{
path.ellipseTo(50, 80, 180, 360, true, 0);
}
else
{
path.ellipseTo(50, 80, 180, 360, false, 0);
}
}
Метод ellipseTo добавляет к пути эллиптическую дугу. Его ключевые параметры:
- xRadius и yRadius (50, 80) — определяют ширину и высоту эллипса.
- startAngle, endAngle (180, 360) — задают диапазон дуги от 180 до 360 градусов, то есть нижнюю половину эллипса.
- clockwise (true/false) — направление обхода. Чередуя его на каждой итерации, мы получаем волну: один полуэллипс рисуется по часовой стрелке, следующий — против.
Путь начинается в точке (0, 300). Каждый вызов ellipseTo продолжает путь от его текущей конечной точки.
Визуализация пути и создание последователей
Чтобы увидеть созданный путь на экране, мы используем объект Graphics.
const graphics = this.add.graphics();
graphics.lineStyle(1, 0xffffff, 1);
path.draw(graphics);
Метод draw отрисовывает траекторию пути с заданным стилем линии.
Далее мы создаем группу объектов, которые будут следовать по этому пути — последователей (Followers). Важно, что для каждого последователя нужно создать отдельный экземпляр.
for (let i = 0; i < 20; i++)
{
let follower;
if (i === 0)
{
follower = this.add.follower(path, 100, 100 + (30 * i), 'eyes').setDepth(50);
}
else
{
follower = this.add.follower(path, 100, 100 + (30 * i), 'orb');
follower.setBlendMode(Phaser.BlendModes.ADD);
follower.setScale(0.5);
}
}
Фабричный метод this.add.follower принимает путь, начальные координаты (x, y) и ключ текстуры. Начальные координаты важны только до старта анимации. Первый объект (глаза) отрисовывается поверх остальных (setDepth(50)). Остальные объекты (орбы) уменьшены и используют аддитивное смешивание (BLEND_ADD), что делает их ярче при наложении.
Запуск анимации следования с задержкой
Сердце эффекта — метод startFollow. Он запускает движение спрайта вдоль заданного пути с определенными параметрами.
follower.startFollow({
duration: 4000,
positionOnPath: true,
repeat: -1,
ease: 'Linear',
delay: i * 70
});
Разберем конфигурационный объект:
- duration: 4000 — время в миллисекундах, за которое объект пройдет весь путь от начала до конца.
- positionOnPath: true — критически важный параметр. Он гарантирует, что объект будет точно размещен *на* кривой пути, а не просто перемещаться между его опорными точками. Без этого объекты двигались бы по ломаной линии.
- repeat: -1 — анимация будет повторяться бесконечно.
- ease: 'Linear' — линейная интерполяция обеспечивает постоянную скорость движения по всей траектории.
- delay: i * 70 — здесь создается волновой эффект. Каждый последующий последователь начинает движение на 70 мс позже предыдущего. Это создает запаздывающее движение, визуально похожее на бегущую волну.
Что попробовать дальше
Комбинируя Path для создания сложных траекторий и Follower с параметром delay, вы можете легко анимировать целые группы объектов, создавая сложные и визуально привлекательные паттерны движения. Для экспериментов попробуйте изменить форму пути, используя lineTo, splineTo или arcTo. Измените параметры duration и delay для управления плотностью и скоростью волны. Добавьте обработчик событий onComplete для создания цепочек действий или кастомизируйте визуал частиц в процессе движения с помощью tween-анимации свойств последователя.
