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

При разработке игр с перетаскиваемыми элементами часто возникает необходимость, чтобы перемещаемый объект отображался поверх остальных. Это важно для удобства игрока и визуальной ясности. В примере на Phaser мы реализуем механизм перетаскивания карт, где выбранная карта будет автоматически подниматься на верхний уровень отображения. Этот подход применим в карточных играх, пазлах, редакторах и любых интерфейсах с перекрывающимися элементами.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    constructor()
    {
        super();
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('bg', 'assets/skies/casinotable.png');
        this.load.atlas('cards', 'assets/atlas/cards.png', 'assets/atlas/cards.json');
    }

    create ()
    {
        this.add.image(400, 300, 'bg');

        this.add.text(16, 16, 'Bring to Top on Drag').setFontSize(24).setShadow(1, 1);

        //  Create a stack of random cards
        const frames = this.textures.get('cards').getFrameNames();

        Phaser.Utils.Array.Shuffle(frames);

        let x = 140;
        let y = 180;

        for (let i = 0; i < 22; i++)
        {
            const image = this.add.image(x, y, 'cards', frames[i]);

            image.setInteractive({ draggable: true });

            x += 14;
            y += 12;
        }

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

            //  This will bring the selected gameObject to the top of the list
            this.children.bringToTop(gameObject);

        }, this);

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

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

        });
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и загрузка ресурсов

В методе preload() мы загружаем фоновое изображение и атлас с картами. Атлас — это текстура, содержащая несколько изображений (спрайтов) в одном файле, что оптимизирует загрузку.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('bg', 'assets/skies/casinotable.png');
    this.load.atlas('cards', 'assets/atlas/cards.png', 'assets/atlas/cards.json');
}

Метод create() создает фон и заголовок, затем генерирует стопку карт. Для этого мы получаем список кадров (frames) из атласа 'cards' и перемешиваем его с помощью Phaser.Utils.Array.Shuffle. Это обеспечивает случайный порядок карт в стопке.

Создание интерактивных карт

В цикле создается 22 карты с небольшим смещением по осям X и Y, имитируя стопку. Каждая карта — объект Image, который мы делаем перетаскиваемым с помощью setInteractive({ draggable: true }).

for (let i = 0; i < 22; i++)
{
    const image = this.add.image(x, y, 'cards', frames[i]);
    image.setInteractive({ draggable: true });
    x += 14;
    y += 12;
}

Ключевой параметр draggable: true активирует встроенную поддержку перетаскивания в Phaser. Объекты автоматически реагируют на события drag, но для полного контроля мы добавим свои обработчики.

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

Событие dragstart срабатывает, когда игрок начинает перетаскивание. В его обработчике мы вызываем this.children.bringToTop(gameObject), чтобы переместить выбранный объект на верхний уровень в списке отображения сцены.

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

Метод bringToTop изменяет порядок рендеринга, гарантируя, что перетаскиваемая карта будет поверх всех других объектов в этой сцене. Контекст this передается как третий аргумент, чтобы мы могли обратиться к this.children внутри функции.

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

Событие drag происходит непрерывно при движении мыши или касании. Обработчик получает координаты dragX и dragY, которые соответствуют текущей позиции указателя. Мы просто присваиваем их свойствам `xиy` перетаскиваемого объекта.

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

Это обеспечивает плавное перемещение карты вслед за курсором. Phaser автоматически обрабатывает физику и коллизии, но в данном примере они не используются.

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

Мы реализовали базовый механизм перетаскивания с подъемом объекта на верхний слой. Этот подход прост, но эффективен для многих игровых сценариев. Для экспериментов попробуйте: добавить физические тела через this.physics.add.image для реалистичного движения, ограничить область перетаскивания или реализовать сброс карты в определенную зону с проверкой коллизий. Это отличная основа для карточных игр или редакторов уровней.