О чем этот пример
В играх часто требуется, чтобы объекты не просто телепортировались, а плавно и реалистично двигались к цели. Phaser 3 предоставляет для этого мощный инструмент `accelerateToObject`. Эта статья покажет, как заставить спрайт с физикой динамично ускоряться к точке клика, создавая интуитивное и приятное управление или поведение врагов. Вы научитесь контролировать ускорение и останавливать объекты.
Версия 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 ()
{
const flower = this.physics.add.image(100, 300, 'flower');
flower.body
.setBounce(1, 1)
.setCollideWorldBounds(true)
.setMaxSpeed(300);
const cursor = this.add.image(0, 0, 'cursor').setVisible(false);
this.add.text(10, 10, 'Click to set target', { fill: '#00ff00' });
this.input.on('pointerdown', (pointer) =>
{
cursor.copyPosition(pointer).setVisible(true);
flower.body.stop();
// Accelerate toward target at 100px per second per second.
this.physics.accelerateToObject(flower, cursor, 100);
// See <move and stop at position.js> for stopping near a target.
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: { debug: true }
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и создание физического объекта
В начале примера в методе preload загружаются два изображения: цветок (основной объект) и курсор (цель). Ключевой момент происходит в create.
Сначала создаётся физический спрайт цветка. Метод this.physics.add.image добавляет его в мир Arcade Physics, что даёт доступ к свойствам тела (body). Далее настраивается его поведение.
const flower = this.physics.add.image(100, 300, 'flower');
flower.body
.setBounce(1, 1)
.setCollideWorldBounds(true)
.setMaxSpeed(300);
* setBounce(1, 1): Задаёт упругость 1 (полное отражение) по осям X и Y. Объект будет отскакивать от границ мира без потери скорости.
* setCollideWorldBounds(true): Включает столкновения с границами игрового мира.
* setMaxSpeed(300): Устанавливает максимальную скорость в 300 пикселей в секунду. Это важное ограничение, чтобы объект не разгонялся бесконечно под действием ускорения.
Реакция на клик и установка цели
Затем создаётся невидимый спрайт курсора, который будет служить визуальным маркером и физической целью для движения. Основная логика привязана к событию клика (pointerdown).
const cursor = this.add.image(0, 0, 'cursor').setVisible(false);
this.input.on('pointerdown', (pointer) => {
cursor.copyPosition(pointer).setVisible(true);
flower.body.stop();
this.physics.accelerateToObject(flower, cursor, 100);
});
При клике:
1. cursor.copyPosition(pointer).setVisible(true): Курсор перемещается в точку клика и становится видимым.
2. flower.body.stop(): Мгновенно обнуляет текущую скорость цветка. Это нужно, чтобы каждое новое ускорение начиналось с чистого листа, а не складывалось с предыдущим вектором движения.
3. this.physics.accelerateToObject(flower, cursor, 100): Это ядро примера. Функция рассчитывает вектор ускорения от текущей позиции flower к позиции cursor. Второй параметр (100) — это значение ускорения в пикселях в секунду за секунду (px/s²). Объект будет плавно набирать скорость по направлению к цели.
Как работает accelerateToObject и важные нюансы
accelerateToObject не перемещает объект мгновенно и не гарантирует остановку в точке цели. Она непрерывно применяет силу ускорения в направлении цели, пока её не отменят. Без ограничения setMaxSpeed объект мог бы разгоняться бесконечно.
Код в примере содержит комментарий, отсылающий к другому решению для точной остановки. accelerateToObject идеальна для поведения типа "преследование", где объект постоянно корректирует курс. Если нужно подъехать и точно остановиться в точке, логику нужно дополнять: например, отслеживать расстояние до цели и вызывать stop() при его достижении.
// Внутри игрового цикла или обработчика событий:
if (Phaser.Math.Distance.Between(flower.x, flower.y, cursor.x, cursor.y) < 5) {
flower.body.stop();
}
Этот фрагмент кода (не из исходника) иллюстрирует идею: когда дистанция становится меньше 5 пикселей, движение прекращается.
Что попробовать дальше
Метод accelerateToObject — это простой способ добавить инерционное, физически правдоподобное движение к объекту. Для экспериментов попробуйте изменить значение ускорения (третий параметр), чтобы сделать разгон более резким или плавным. Добавьте несколько объектов, ускоряющихся к одной цели, или реализуйте "умное" преследование, где ускорение применяется только когда игрок находится в поле зрения. Также интересно будет совместить этот метод с другими силами, например, с setDrag, для создания движения в вязкой среде.
