О чем этот пример
В игровой разработке часто требуется перемещать объекты по заданному пути, будь то полёт снаряда, движение камеры или патрулирование врага. Вручную рассчитывать координаты на каждом кадре — неэффективно. Phaser предоставляет геометрический инструмент `Phaser.Geom.Line.getPoint()`, который делает это за вас. Эта статья покажет, как с помощью всего одного метода создавать плавные и предсказуемые перемещения объекта по линии, что идеально подходит для траекторий, прогресс-баров или интерполяции значений.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
a = 0;
point;
line;
graphics;
create ()
{
this.graphics = this.add.graphics({ lineStyle: { width: 2, color: 0x00ff00 }, fillStyle: { color: 0xff0000 }});
this.line = new Phaser.Geom.Line(100, 100, 600, 500);
this.point = new Phaser.Geom.Rectangle(0, 0, 16, 16);
}
update ()
{
this.a += 0.005;
if (this.a > 1)
{
this.a = 0;
}
this.line.getPoint(this.a, this.point);
this.graphics.clear();
this.graphics.lineStyle(2, 0x00ff00);
this.graphics.strokeLineShape(this.line);
this.graphics.fillStyle(0xff00ff);
this.graphics.fillRect(this.point.x - 8, this.point.y - 8, this.point.width, this.point.height);
}
}
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Принцип работы: параметрическое представление линии
Метод getPoint(a, point) использует параметрическое уравнение линии. Параметр `a` — это нормализованное значение (от 0 до 1), где 0 соответствует начальной точке линии, 1 — конечной, а 0.5 — точке ровно посередине.
Метод не создаёт новый объект точки, а записывает вычисленные координаты (x, y) в переданный ему объект. Это эффективно для производительности, так как избегает лишнего создания объектов в цикле update().
// line - объект Phaser.Geom.Line
// point - объект с полями x и y (например, Phaser.Geom.Rectangle)
this.line.getPoint(this.a, this.point);
Разбор примера: от линии до движущейся точки
В предоставленном примере создаются линия и прямоугольник, который будет двигаться по ней.
create ()
{
this.graphics = this.add.graphics({ lineStyle: { width: 2, color: 0x00ff00 }, fillStyle: { color: 0xff0000 }});
this.line = new Phaser.Geom.Line(100, 100, 600, 500);
this.point = new Phaser.Geom.Rectangle(0, 0, 16, 16);
}
Интересно, что в качестве точки используется Phaser.Geom.Rectangle. Это возможно, потому что getPoint() ожидает любой объект с полями `xиy, и прямоугольник им соответствует. В цикле обновления мы увеличиваем параметрa` и получаем новую позицию.
update ()
{
this.a += 0.005;
if (this.a > 1) { this.a = 0; } // Сброс для зацикленной анимации
this.line.getPoint(this.a, this.point); // Ключевой вызов
}
Визуализация: рисуем линию и точку
После расчёта позиции необходимо очистить холст и нарисовать всё заново. Phaser.Graphics позволяет это делать с помощью методов clear(), lineStyle() и fillRect().
Обратите внимание, что координаты для отрисовки прямоугольника (fillRect) смещаются на половину его ширины и высоты. Это делается, чтобы центр прямоугольника совпадал с вычисленной точкой на линии.
this.graphics.clear();
this.graphics.lineStyle(2, 0x00ff00);
this.graphics.strokeLineShape(this.line);
this.graphics.fillStyle(0xff00ff);
// Рисуем прямоугольник так, чтобы его центр был в точке (point.x, point.y)
this.graphics.fillRect(this.point.x - 8, this.point.y - 8, this.point.width, this.point.height);
Практическое применение в играх
1. **Патрулирование:** Задайте линию между двумя точками и перемещайте спрайт врага по ней с помощью getPoint(). Изменив знак приращения `a`, вы заставите врага ходить туда-обратно.
2. **Плавные переходы:** Методом можно интерполировать не только позицию, но и другие значения. Например, цвет объекта от точки A (цвет1) до точки B (цвет2).
3. **Прогресс и таймеры:** Визуализируйте зарядку способности или отсчёт времени, рисуя линию, по которой движется индикатор.
// Пример для патрулирования туда-обратно
update() {
this.a += this.speed * this.direction; // direction = 1 или -1
if (this.a >= 1 || this.a <= 0) {
this.direction *= -1; // Меняем направление
}
this.path.getPoint(Phaser.Math.Clamp(this.a, 0, 1), this.sprite);
this.sprite.x = this.sprite.x; // Используем x из объекта point
this.sprite.y = this.sprite.y;
}
Что попробовать дальше
Метод Phaser.Geom.Line.getPoint() — это элегантное и производительное решение для задач, связанных с движением по прямой. Он избавляет от ручных вычислений и легко интегрируется в игровой цикл.
Для экспериментов попробуйте:
* Привязать к движущейся точке не квадрат, а спрайт игрока или частицу.
* Создать массив линий и заставить объект переходить с одной на другую, создавая ломаную траекторию.
* Использовать значение `aне линейно, а через easing-функции (Phaser.Math.Easing`) для более сложной скорости движения (ускорение, замедление).
