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

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

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

Живой запуск

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

Исходный код


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

    create ()
    {
        let textbutton = this.add.text(50, 50, "Text", { font: '64px Courier' });

        textbutton.setInteractive();

        let x = 0;

        textbutton.on('pointerup', () => {
            console.log('clicked text object');
            this.add.text(100, 100 + 50 * x, "inserted", { font: '64px Courier' });
            x++;
        });

        var div = document.createElement('div');
        div.setAttribute("style", "color: white; font: 48px Arial;");
        div.innerText = "DOM Element";

        let el = this.add.dom(500, 80, div);

        el.addListener('click');

        el.on('click', (event) => {

            console.log('clicked dom thingy');

            el.destroy();

        });
    }
}

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

const game = new Phaser.Game(config);

Настройка проекта для работы с DOM

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

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

Ключевой параметр — dom.createContainer: true. Он указывает Phaser создать специальный контейнер для DOM-элементов и интегрировать его в игровой цикл рендеринга.

Создание и позиционирование DOM-элемента

DOM-элементы создаются в два этапа: сначала средствами JavaScript создается нативный HTML-элемент, затем он добавляется на сцену через фабрику this.add.dom.

// 1. Создаем нативный HTML-элемент
var div = document.createElement('div');
div.setAttribute("style", "color: white; font: 48px Arial;");
div.innerText = "DOM Element";

// 2. Добавляем его на сцену Phaser
let el = this.add.dom(500, 80, div);

Метод this.add.dom(x, y, element) принимает координаты и созданный элемент. Phaser поместит его в контейнер и будет управлять его позицией (x, y) относительно сцены. Стилизация элемента полностью контролируется через обычный CSS или атрибут style.

Обработка событий: DOM vs Игровые объекты

Phaser предоставляет единую систему событий для игровых объектов (Sprite, Text, Image). Для DOM-элементов используется отдельный механизм прослушивания нативных событий.

**Для обычного текстового объекта Phaser:**

let textbutton = this.add.text(50, 50, "Text", { font: '64px Courier' });
textbutton.setInteractive();
textbutton.on('pointerup', () => {
    console.log('clicked text object');
});

Здесь используется метод setInteractive(), чтобы объект реагировал на ввод, и система событий Phaser (pointerup).

**Для DOM-элемента:**

el.addListener('click');
el.on('click', (event) => {
    console.log('clicked dom thingy');
    el.destroy();
});

Сначала для элемента нужно зарегистрировать слушатель определенного типа события с помощью addListener('click'). Только после этого можно подписаться на это событие через el.on('click', ...). Это отдельный API, специфичный для DOM-объектов Phaser.

Уничтожение DOM-объектов

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

el.on('click', (event) => {
    el.destroy();
});

Вызов el.destroy() удалит объект из сцены и освободит связанные с ним ресурсы. Важно не путать этот метод с прямым удалением через div.remove() — это может привести к утечкам памяти внутри Phaser.

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

Интеграция DOM-элементов — мощный инструмент для создания гибридных интерфейсов в играх на Phaser. Главное помнить о двух разных системах обработки событий. Для экспериментов попробуйте: создать сложную HTML-форму поверх игры, анимировать DOM-элемент через el.setPosition в update() или сделать кастомный курсор мыши из DOM-элемента, который следует за pointer.