О чем этот пример
Интеграция 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-виджетов (кнопки, поля ввода) и свяжите их состояние с игровой логикой, создавая сложные гибридные интерфейсы для ваших игр.
