О чем этот пример
В игровом движке Phaser 3 управление движением объектов часто требует большего, чем просто перемещение по прямой. Использование кривых и путей (Paths) позволяет создавать сложные, плавные и визуально интересные траектории для персонажей, снарядов или камеры. Эта статья на практическом примере покажет, как создать объект, движущийся по заранее заданному многоугольному пути, используя классы `Path` и `Curves`. Вы научитесь определять путь, визуализировать его и анимировать по нему любой спрайт или графический объект с помощью твинов, что откроет возможности для создания патрулирующих врагов, летающих снарядов или сложных кат-сцен.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
graphics;
path;
follower;
create ()
{
this.graphics = this.add.graphics();
this.follower = { t: 0, vec: new Phaser.Math.Vector2() };
// Path starts at 100x100
this.path = new Phaser.Curves.Path(100, 100);
this.path.lineTo(500, 200);
this.path.lineTo(200, 300);
this.path.lineTo(400, 500);
this.tweens.add({
targets: this.follower,
t: 1,
ease: 'Sine.easeInOut',
duration: 4000,
yoyo: true,
repeat: -1
});
}
update ()
{
this.graphics.clear();
this.graphics.lineStyle(2, 0xffffff, 1);
this.path.draw(this.graphics);
this.path.getPoint(this.follower.t, this.follower.vec);
this.graphics.fillStyle(0xff0000, 1);
this.graphics.fillCircle(this.follower.vec.x, this.follower.vec.y, 12);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и создание пути
В методе create() сцены мы подготавливаем все необходимые компоненты. Сначала создается объект Graphics для отрисовки самой траектории. Затем определяется объект-последователь (follower), который будет хранить текущую позицию на пути (от 0 до 1) и вектор для вычисления координат.
Ключевой элемент — создание объекта Path. Он представляет собой последовательность соединенных кривых (в данном случае — прямых линий). Путь начинается в точке (100, 100). Метод lineTo() добавляет к пути отрезок прямой от текущей конечной точки до указанных координат, становясь новым концом пути. Таким образом, мы строим ломаную линию.
this.graphics = this.add.graphics();
this.follower = { t: 0, vec: new Phaser.Math.Vector2() };
this.path = new Phaser.Curves.Path(100, 100);
this.path.lineTo(500, 200);
this.path.lineTo(200, 300);
this.path.lineTo(400, 500);
Анимация движения по пути
Чтобы заставить точку двигаться по созданному пути, мы используем систему твинов Phaser. Твин будет анимировать свойство `t` объекта-последователя от 0 до 1, где 0 — начало пути, а 1 — его конец.
Конфигурация твина включает плавную анимацию (Sine.easeInOut), длительность в 4 секунды, а также параметры yoyo: true и repeat: -1. Это означает, что после достижения конца пути анимация автоматически пойдет в обратном направлении (от 1 к 0), и этот цикл будет повторяться бесконечно. Именно изменение значения this.follower.t в каждом кадре заставляет объект перемещаться.
this.tweens.add({
targets: this.follower,
t: 1,
ease: 'Sine.easeInOut',
duration: 4000,
yoyo: true,
repeat: -1
});
Визуализация пути и объекта в реальном времени
В методе update(), который вызывается каждый кадр, происходит отрисовка. Сначала холст Graphics очищается вызовом clear(), чтобы стереть рисунок предыдущего кадра. Затем задается стиль линии и отрисовывается сам путь с помощью метода draw().
Сердцевина логики — получение текущих координат на пути. Метод getPoint(this.follower.t, this.follower.vec) вычисляет точку на кривой, соответствующую текущему прогрессу `t, и записывает её координаты в переданный векторthis.follower.vec`.
Наконец, в этих вычисленных координатах (vec.x, vec.y) рисуется красный кружок, который и представляет собой движущийся объект.
this.graphics.clear();
this.graphics.lineStyle(2, 0xffffff, 1);
this.path.draw(this.graphics);
this.path.getPoint(this.follower.t, this.follower.vec);
this.graphics.fillStyle(0xff0000, 1);
this.graphics.fillCircle(this.follower.vec.x, this.follower.vec.y, 12);
Расширение примера: от точки к спрайту
В рассмотренном примере по пути движется графический примитив (круг). Однако эту технику легко адаптировать для движения спрайта, игрового объекта с физикой или камеры. Вместо отрисовки круга в update() нужно обновлять позицию нужного объекта.
1. **Для обычного спрайта:** Создайте его в `create()`, например, `this.ship = this.add.sprite(0, 0, 'texture');`. Затем в `update()` после `getPoint()` установите его позицию: `this.ship.setPosition(this.follower.vec.x, this.follower.vec.y);`.
2. **Для объекта с физикой:** Используйте `this.physics.add.sprite()` и устанавливайте `body.position` или скорость (`body.setVelocity()`), направленную к следующей точке на пути для более реалистичного движения.
Главное — продолжать использовать один и тот же механизм с Path, follower.t и getPoint() для расчета позиции.
Что попробовать дальше
Класс Phaser.Curves.Path — мощный инструмент для декларативного описания сложных траекторий. Комбинируя его с системой твинов, вы получаете полный контроль над перемещением любых игровых объектов по заданному маршруту. Для экспериментов попробуйте заменить lineTo() на splineTo() или ellipseTo() для создания изогнутых путей, управляйте скоростью анимации в зависимости от участка пути или создайте несколько независимых объектов, движущихся по одному пути с разной скоростью и фазой.
