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

Стандартное перетаскивание в Phaser 3 работает 'из коробки', но часто возникает вопрос: как корректно перетаскивать объекты, которые были повёрнуты или масштабированы? В примере показано, что API инпут-системы Phaser корректно обрабатывает трансформации объекта. Вам не нужно вручную пересчитывать координаты — система `drag` уже предоставляет правильные значения `dragX` и `dragY`. Это полезно для создания интерактивных панелей, карт, пазлов или инвентаря, где элементы могут быть произвольно трансформированы.

Версия 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.atlas('cards', 'assets/atlas/cards.png', 'assets/atlas/cards.json');
    }

    create ()
    {
        const image = this.add.image(400, 300, 'cards', 'clubsKing');

        //  Rotate it
        image.setAngle(45);

        //  Scale it
        image.setScale(2);

        //  Make interactive
        image.setInteractive();

        //  Make draggable
        this.input.setDraggable(image);

        this.input.on('dragstart', function (pointer, gameObject)
        {

            this.children.bringToTop(gameObject);

        }, this);

        this.input.on('drag', (pointe, gameObject, dragX, dragY) =>
        {

            gameObject.x = dragX;
            gameObject.y = dragY;

        });

    }
}

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example
};

const game = new Phaser.Game(config);

Подготовка и трансформация объекта

В методе create создаётся спрайт, после чего к нему применяются два ключевых преобразования: поворот на 45 градусов и увеличение масштаба в 2 раза. Эти операции изменяют визуальное представление, но не ломают систему взаимодействия.

const image = this.add.image(400, 300, 'cards', 'clubsKing');
image.setAngle(45);
image.setScale(2);

Далее объекту необходимо добавить интерактивность и разрешить перетаскивание. Без этого система ввода не будет на него реагировать.

image.setInteractive();
this.input.setDraggable(image);

Обработка начала перетаскивания

При старте перетаскивания (dragstart) часто требуется визуально выделить активный объект, например, переместив его на передний план. В примере для этого используется метод this.children.bringToTop(gameObject). Обратите внимание, что обработчик привязан к контексту сцены (this) с помощью третьего аргумента.

this.input.on('dragstart', function (pointer, gameObject) {
    this.children.bringToTop(gameObject);
}, this);

Эта строчка гарантирует, что перетаскиваемая карта будет поверх других элементов, если они есть.

Обработка процесса перетаскивания

Самое важное происходит в обработчике события drag. Система ввода Phaser автоматически рассчитывает корректные координаты (dragX, dragY) с учётом всех применённых к объекту трансформаций (поворота и масштаба). Вам остаётся только присвоить их объекту. В примере используется стрелочная функция, которая автоматически сохраняет контекст сцены.

this.input.on('drag', (pointer, gameObject, dragX, dragY) => {
    gameObject.x = dragX;
    gameObject.y = dragY;
});

Благодаря этой логике объект будет плавно и точно следовать за курсором, как если бы он не был повёрнут или увеличен.

Конфигурация игры и сцены

Весь функционал упакован в конфигурацию игры, где указывается тип рендерера, размеры холста и класс основной сцены.

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example
};

const game = new Phaser.Game(config);

Что попробовать дальше

Phaser 3 предоставляет отличную абстракцию для перетаскивания, которая избавляет разработчика от ручных расчётов геометрии. Для экспериментов попробуйте

  1. Добавить физическое тело объекту и посмотреть, как перетаскивание взаимодействует с движком физики (Arcade или Matter)
  2. Реализовать 'привязку' объекта к сетке при отпускании
  3. Создать контейнер с несколькими трансформированными объектами и реализовать логику выбора поверхностного объекта под курсором