О чем этот пример

В играх часто требуется, чтобы объекты не просто телепортировались, а плавно и реалистично двигались к цели. 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, для создания движения в вязкой среде.