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

В игровых проектах часто требуется реализовать интерактивное взаимодействие с объектами, например, перетаскивание карт, пазлов или UI-элементов. Phaser 3 предоставляет мощную встроенную систему ввода, которая позволяет легко добавлять drag-and-drop функционал. В этой статье мы разберем, как настроить перетаскивание для множества объектов одновременно, что особенно полезно для карточных игр, редакторов уровней или инвентарей.

Версия 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 ()
    {
        //  Create a stack of random cards

        const frames = this.textures.get('cards').getFrameNames();

        let x = 100;
        let y = 100;

        for (let i = 0; i < 64; i++)
        {
            const image = this.add.image(x, y, 'cards', Phaser.Math.RND.pick(frames)).setInteractive();

            this.input.setDraggable(image);

            x += 4;
            y += 4;
        }

        //  Grab everything under the pointer

        this.input.topOnly = false;

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

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

        });

    }
}

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

const game = new Phaser.Game(config);

Подготовка ассетов и создание стопки карт

В методе preload загружается атлас с картами, который содержит все необходимые кадры (frames). Важно использовать setBaseURL для указания базового пути к ресурсам.

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 мы сначала получаем список всех кадров из текстуры 'cards' с помощью getFrameNames. Затем в цикле создаем 64 карты, каждая со случайным кадром, используя Phaser.Math.RND.pick. Карты позиционируются со смещением, образуя стопку.

const frames = this.textures.get('cards').getFrameNames();

let x = 100;
let y = 100;

for (let i = 0; i < 64; i++)
{
    const image = this.add.image(x, y, 'cards', Phaser.Math.RND.pick(frames)).setInteractive();

    this.input.setDraggable(image);

    x += 4;
    y += 4;
}

Ключевой момент: каждый объект image делается интерактивным через setInteractive(), а затем регистрируется как перетаскиваемый с помощью this.input.setDraggable(image). Без этого перетаскивание не будет работать.

Настройка захвата нескольких объектов под курсором

По умолчанию система ввода Phaser обрабатывает только верхний объект под указателем (свойство topOnly равно true). Для реализации перетаскивания всей стопки карт необходимо изменить это поведение.

this.input.topOnly = false;

Установка topOnly в false позволяет событиям ввода (например, drag) обрабатываться всеми объектами под курсором, а не только самым верхним. Это особенно важно, когда объекты перекрываются, как в нашей стопке карт.

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

Событие 'drag' генерируется, когда пользователь перемещает зажатый объект. Мы подписываемся на это событие, чтобы обновлять позицию объекта в реальном времени.

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

Колбэк функция получает четыре аргумента: - pointer: объект указателя (мышь или касание). - gameObject: конкретный игровой объект, который в данный момент перетаскивается. - dragX, dragY: новые координаты X и Y, куда следует переместить объект.

Внутри колбэка мы просто присваиваем эти координаты свойствам `xиyобъекта, обеспечивая его плавное движение за курсором. Так какtopOnlyравноfalse`, это событие будет вызываться для всех перетаскиваемых объектов под указателем одновременно, позволяя двигать несколько карт разом.

Конфигурация игры и запуск

Основная конфигурация игры задается в объекте config. Здесь мы указываем тип рендерера, родительский элемент, размеры холста и сцену.

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

const game = new Phaser.Game(config);

Использование Phaser.WEBGL обеспечивает аппаратное ускорение графики. Убедитесь, что в HTML есть элемент с id='phaser-example', куда будет встроена игра. После создания экземпляра Phaser.Game сцена автоматически инициализируется, вызывая методы preload и create.

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

Реализация перетаскивания нескольких объектов в Phaser 3 сводится к трем основным шагам: создание интерактивных объектов с setDraggable, отключение topOnly для множественного захвата и обработка события drag. Этот подход открывает возможности для создания сложных интерактивных интерфейсов и игровых механик. Для экспериментов попробуйте добавить физические тела объектам, чтобы они сталкивались при перетаскивании, или реализовать ограничение зоны перетаскивания. Также можно изменить z-index объектов для динамического порядка отрисовки.