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

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

Версия 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('goblin', 'assets/pics/goblin.png');
        this.load.image('spider', 'assets/pics/spider.png');
    }

    create ()
    {
        const goblin = this.add.image(400, 100, 'goblin').setInteractive({ draggable: true });

        const element = this.add.dom(400, 300, 'div', 'background-color: rgba(255, 255, 0, 0.5); width: 300px; height: 200px; font: 48px Arial; font-weight: bold', 'Phaser 3');

        element.node.draggable = 'true';

        element.addListener('drag');

        element.on('drag', event => {

            console.log('dom move', event);

        });

        goblin.on('drag', (pointer, dragX, dragY) => {

            goblin.setPosition(dragX, dragY);
            this.children.bringToTop(goblin);

        });

    }
}

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

const game = new Phaser.Game(config);

Подготовка и настройка DOM-контейнера

Для работы с DOM в Phaser 3 необходимо включить соответствующую опцию в конфигурации игры. Без этого система DOM не будет инициализирована.

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

Ключевой параметр dom.createContainer: true указывает движку создать скрытый контейнер для всех DOM-элементов, которые будут добавлены в сцену. Этот контейнер автоматически масштабируется и позиционируется в соответствии с игровым холстом.

Далее, в методе preload() загружаются два изображения, которые будут использоваться как спрайты. Обратите внимание на установку базового URL для загрузчика, что позволяет указывать относительные пути к ресурсам.

Создание DOM-элемента и настройка перетаскивания

DOM-элементы создаются с помощью метода this.add.dom(). Этот метод возвращает специальный игровой объект, который оборачивает нативный HTML-элемент и позволяет управлять им в контексте сцены.

const element = this.add.dom(400, 300, 'div', 'background-color: rgba(255, 255, 0, 0.5); width: 300px; height: 200px; font: 48px Arial; font-weight: bold', 'Phaser 3');

Параметры метода: 1. X и Y координаты (400, 300). 2. Тип создаваемого элемента ('div'). 3. Строка стилей CSS. 4. Внутренний текст элемента ('Phaser 3').

Чтобы элемент можно было перетаскивать, нужно установить нативному DOM-узлу свойство draggable. Доступ к узлу осуществляется через свойство node.

element.node.draggable = 'true';

Затем элементу необходимо добавить слушатель для события 'drag' с помощью addListener(). Само событие обрабатывается через метод on().

element.addListener('drag');
element.on('drag', event => {
    console.log('dom move', event);
});

Пока что этот обработчик лишь логирует событие в консоль. Автоматического перемещения DOM-элемента при перетаскивании в Phaser нет — его нужно реализовывать вручную, обрабатывая данные события (например, event.pageX и event.pageY).

Перетаскивание спрайта и управление порядком отрисовки

В отличие от DOM-элементов, спрайты в Phaser имеют встроенную поддержку перетаскивания через систему Input. Сначала создаем спрайт и настраиваем его как перетаскиваемый.

const goblin = this.add.image(400, 100, 'goblin').setInteractive({ draggable: true });

Метод setInteractive({ draggable: true }) активирует для спрайта обработку событий ввода, в частности, перетаскивания.

Обработчик события drag для спрайта получает от движка целевые координаты (dragX, dragY), куда нужно переместить объект.

goblin.on('drag', (pointer, dragX, dragY) => {
    goblin.setPosition(dragX, dragY);
    this.children.bringToTop(goblin);
});

Здесь происходит две важные вещи: 1. Спрайт перемещается на новые координаты методом setPosition(). 2. Спрайт принудительно поднимается на верхний слой отрисовки с помощью this.children.bringToTop(goblin). Это гарантирует, что перетаскиваемый объект всегда будет виден поверх других объектов, включая DOM-элементы, и визуально подтверждает действие пользователя.

Ключевые различия в обработке событий

Пример наглядно демонстрирует разницу в парадигмах обработки событий для нативных DOM-элементов и игровых объектов Phaser.

* **DOM-события:** Являются нативными браузерными событиями. Phaser лишь проксирует их через свою систему. Для их обработки требуется явно добавлять слушатели через addListener(). Данные события (объект event) содержат стандартные браузерные свойства. * **События игровых объектов:** Это события внутренней системы Input Phaser. Они срабатывают только на объектах, сделанных интерактивными через setInteractive(). Обработчики этих событий получают специфичные для Phaser параметры, такие как указатель (pointer) и рассчитанные координаты (dragX, dragY), что сильно упрощает реализацию перетаскивания.

Важно понимать, что DOM-элементы живут в отдельном слое над или под игровым холстом (в зависимости от настройки depth). Их события не всплывают через игровой мир Phaser, и наоборот.

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

Интеграция DOM в Phaser 3 — мощный инструмент, но требующий понимания двух параллельных систем событий. Для простого перетаскивания спрайтов используйте встроенную систему Input Phaser. Для работы с DOM будьте готовы работать с нативными браузерными событиями. Экспериментируйте: попробуйте реализовать полноценное перетаскивание DOM-элемента, обрабатывая event.pageX/Y и меняя его свойство style.transform. Добавьте больше интерактивных DOM-виджетов (кнопки, поля ввода) и свяжите их состояние с игровой логикой, создавая сложные гибридные интерфейсы для ваших игр.