О чем этот пример
В играх часто требуется, чтобы объекты двигались не по прямой, а по сложным, изогнутым траекториям: снаряды описывают дугу, враги патрулируют по заданному маршруту, камера плавно следует за героем. Ручной расчёт таких путей — трудоёмкая задача. В Phaser 3 для этого есть мощный инструмент — `Phaser.Curves.Path`. Эта статья покажет, как создавать составные пути, комбинируя различные типы кривых — сплайны, линии, эллипсы и кривые Безье — для реализации сложного и плавного движения объектов на сцене.
Версия 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() };
this.path = new Phaser.Curves.Path(50, 500);
this.path.splineTo([ 164, 446, 274, 542, 412, 457, 522, 541, 664, 464 ]);
this.path.lineTo(700, 300);
this.path.lineTo(600, 350);
this.path.ellipseTo(200, 100, 100, 250, false, 0);
this.path.cubicBezierTo(222, 119, 308, 107, 208, 368);
this.path.ellipseTo(60, 60, 0, 360, true);
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);
Подготовка сцены и создание Path
Вся работа с путями начинается с создания экземпляра класса Phaser.Curves.Path. Он выступает в роли контейнера, к которому мы будем последовательно добавлять сегменты разных типов.
create () {
this.graphics = this.add.graphics();
this.follower = { t: 0, vec: new Phaser.Math.Vector2() };
this.path = new Phaser.Curves.Path(50, 500);
}
В этом коде мы создаём объект Graphics для визуализации пути на сцене. Объект follower будет хранить текущую позицию на пути (значение `tот 0 до 1) и вектор для вычисления координат. КонструкторPath` принимает начальные координаты (x, y) — точку, от которой начнётся построение первого сегмента.
Собираем путь из сегментов
Сила Path в возможности комбинирования разных типов кривых. Методы добавления сегментов всегда продолжают путь от конечной точки предыдущего сегмента.
this.path.splineTo([ 164, 446, 274, 542, 412, 457, 522, 541, 664, 464 ]);
this.path.lineTo(700, 300);
this.path.lineTo(600, 350);
Метод splineTo создаёт плавную кривую (сплайн), проходящую через массив точек. Далее lineTo добавляет прямолинейные сегменты до указанных координат.
this.path.ellipseTo(200, 100, 100, 250, false, 0);
this.path.cubicBezierTo(222, 119, 308, 107, 208, 368);
this.path.ellipseTo(60, 60, 0, 360, true);
ellipseTo рисует дугу эллипса. Первые два параметра — радиус по X и Y. Следующие два — начальный и конечный угол в градусах. Параметр clockwise задаёт направление обхода. cubicBezierTo добавляет кубическую кривую Безье, определяемую двумя контрольными точками и конечной точкой.
Анимируем движение по пути
Для перемещения объекта по пути используется параметр `t` (нормализованное расстояние от 0 до 1). Его анимацию удобно поручить встроенному твинеру Phaser.
this.tweens.add({
targets: this.follower,
t: 1,
ease: 'Sine.easeInOut',
duration: 4000,
yoyo: true,
repeat: -1
});
Этот твин плавно изменяет свойство `tобъектаfollowerот 0 до 1 за 4 секунды с плавным ускорением и замедлением (Sine.easeInOut). Опцииyoyo: trueиrepeat: -1` заставляют объект ходить по пути туда и обратно бесконечно.
Визуализация и получение координат
В методе update мы выполняем две ключевые операции: рисуем сам путь и вычисляем текущие координаты нашего "последователя" (follower).
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);
}
Сначала очищаем холст Graphics и задаём стиль линии. Метод path.draw(graphics) отрисовывает весь составной путь. Затем ключевой метод path.getPoint(t, vector) вычисляет координаты точки на пути для заданного значения `tи записывает их в переданный векторthis.follower.vec`. Эти координаты мы используем, чтобы нарисовать красный кружок, символизирующий движущийся объект.
Практическое применение: враги и кат-сцены
В реальном проекте вместо рисования кружка вы будете обновлять позицию спрайта.
// В create()
this.sprite = this.physics.add.image(50, 500, 'enemy');
// В update()
this.path.getPoint(this.follower.t, this.follower.vec);
this.sprite.setPosition(this.follower.vec.x, this.follower.vec.y);
Такой подход идеален для: * **Патрулирования врагов:** Задайте замкнутый путь для стража. * **Траекторий снарядов:** Создайте дугу для метательного оружия. * **Движения камеры:** Для плавных пролётов в кат-сценах анимируйте позицию камеры, используя точку на пути. * **Сложных анимаций UI:** Движение иконок, бонусов по заданной траектории.
Что попробовать дальше
Phaser.Curves.Path — это мощный и гибкий конструктор для создания сложных траекторий из простых сегментов. Вы можете проектировать маршруты любой формы, комбинируя примитивы. Для экспериментов попробуйте: создать замкнутый путь для патруля, изменить параметры эллипса и Безье для получения новых форм, привязать к пути не спрайт, а саму камеру для кинематографичных переходов или управлять значением `t` не твинером, а вводом с клавиатуры для ручного контроля движения.
