О чем этот пример
Одна из частых задач в аркадных играх — заставить объект двигаться к заданной точке на экране, будь то клик мыши или координата противника. Phaser Arcade Physics предоставляет для этого простой метод `moveToObject`, но он не останавливает объект автоматически по прибытии. В этой статье мы разберем, как реализовать движение с плавной остановкой в нужной позиции, что критически важно для механик следования за курсором, преследования врагов или перемещения по пути.
Версия 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('flower', 'assets/sprites/flower-exo.png');
this.load.image('cursor', 'assets/sprites/drawcursor.png');
}
create ()
{
this.source = this.physics.add.image(100, 300, 'flower');
this.target = new Phaser.Math.Vector2();
const cursor = this.add.image(0, 0, 'cursor').setVisible(false);
this.distanceText = this.add.text(10, 10, 'Click to set target', { fill: '#00ff00' });
this.input.on('pointerdown', (pointer) =>
{
this.target.x = pointer.x;
this.target.y = pointer.y;
// Move at 200 px/s:
this.physics.moveToObject(this.source, this.target, 200);
cursor.copyPosition(this.target).setVisible(true);
});
}
update ()
{
// 4 is our distance tolerance, i.e. how close the source can get to the target
// before it is considered as being there. The faster it moves, the more tolerance is required.
const tolerance = 4;
// const tolerance = 200 * 1.5 / this.game.loop.targetFps;
const distance = Phaser.Math.Distance.BetweenPoints(this.source, this.target);
if (this.source.body.speed > 0)
{
this.distanceText.setText(`Distance: ${distance}`);
if (distance < tolerance)
{
this.source.body.reset(this.target.x, this.target.y);
}
}
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade'
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и объектов
В методе preload загружаем два спрайта: основной объект (flower) и визуальный маркер цели (cursor). В create инициализируем физический спрайт source, который будет двигаться, и создаем вектор target для хранения координат цели.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('flower', 'assets/sprites/flower-exo.png');
this.load.image('cursor', 'assets/sprites/drawcursor.png');
}
create ()
{
this.source = this.physics.add.image(100, 300, 'flower');
this.target = new Phaser.Math.Vector2();
const cursor = this.add.image(0, 0, 'cursor').setVisible(false);
}
Задание цели и запуск движения
Обрабатываем клик мыши, чтобы установить новую цель. При событии pointerdown координаты указателя сохраняются в вектор target. Затем вызывается ключевой метод this.physics.moveToObject, который задает скорость тела source так, чтобы оно двигалось прямо к цели с указанной скоростью (200 пикселей в секунду). Маркер cursor становится видимым в точке цели для наглядности.
this.input.on('pointerdown', (pointer) =>
{
this.target.x = pointer.x;
this.target.y = pointer.y;
// Move at 200 px/s:
this.physics.moveToObject(this.source, this.target, 200);
cursor.copyPosition(this.target).setVisible(true);
});
Контроль прибытия и остановка
Метод moveToObject только задает скорость, но не следит за достижением цели. Поэтому логику остановки мы реализуем в update. Каждый кадр вычисляется текущее расстояние между source и target с помощью Phaser.Math.Distance.BetweenPoints.
Ключевая идея — использовать **допуск (tolerance)**. Из-за дискретной природы обновления кадров объект может «проскочить» точку цели. Допуск определяет, насколько близко объект должен подойти, чтобы считаться прибывшим. В примере он равен 4 пикселям. Если расстояние меньше допуска и скорость тела больше нуля, мы сбрасываем позицию и скорость тела, используя this.source.body.reset.
update ()
{
const tolerance = 4;
const distance = Phaser.Math.Distance.BetweenPoints(this.source, this.target);
if (this.source.body.speed > 0)
{
if (distance < tolerance)
{
this.source.body.reset(this.target.x, this.target.y);
}
}
}
Вызов reset устанавливает тело в указанные координаты и обнуляет его скорость, что приводит к мгновенной и точной остановке.
Настройка физики и запуск игры
Для работы примера необходимо активировать Arcade Physics в конфигурации игры. Убедитесь, что в настройках physics указан default: 'arcade'.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade'
},
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Мы реализовали базовый паттерн движения к точке с контролируемой остановкой. Для экспериментов попробуйте: изменить скорость движения и адаптивный допуск (закомментированная строка tolerance = 200 * 1.5 / this.game.loop.targetFps), добавить плавное замедление перед остановкой, или заставить объект обходить препятствия, комбинируя этот подход с collider. Этот механизм — основа для AI врагов, управления персонажем по клику или любых задач точечного наведения.
