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

При интеграции стандартных HTML-элементов в игровую сцену Phaser может возникнуть необходимость контролировать их визуальный приоритет относительно других игровых объектов, таких как спрайты или изображения. По умолчанию DOM-элементы, добавленные через `this.add.dom()`, могут перекрывать графику игры, что не всегда желательно. Эта статья покажет, как управлять порядком отрисовки (z-index) DOM-элементов, используя встроенные методы Phaser, чтобы гармонично сочетать веб-интерфейсы с игровым миром. Вы узнаете, как гарантировать, что элементы интерфейса, например, панели или кнопки, корректно отображаются поверх или под игровыми объектами.

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

    create ()
    {
        const div1 = document.createElement('div');
        div1.style = 'background-color: lime; width: 220px; height: 100px; font: 48px Arial; font-weight: bold';
        div1.innerText = 'Phaser 3';

        const div2 = document.createElement('div');
        div2.style = 'background-color: yellow; width: 220px; height: 100px; font: 48px Arial; font-weight: bold';
        div2.innerText = 'Phaser 3';

        const element1 = this.add.dom(300, 0, div1);
        const element2 = this.add.dom(400, 0, div2);

        element1.setDepth(2);

        this.tweens.add({
            targets: [ element1, element2 ],
            y: 600,
            angle: 200,
            duration: 3000,
            scaleX: 2,
            ease: 'Sine.easeInOut',
            loop: -1,
            yoyo: true
        });

        this.add.image(400, 300, 'einstein');
    }
}

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

const game = new Phaser.Game(config);

Настройка проекта и загрузка ресурсов

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

В методе preload загружается изображение, которое будет использоваться в сцене как фоновый или игровой объект. Обратите внимание, что setBaseURL задает базовый путь для загрузки ресурсов.

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    dom: {
        createContainer: true // Ключевая опция для активации DOM-контейнера
    },
    scene: Example
};
preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('einstein', 'assets/pics/ra-einstein.png');
}

Создание и добавление DOM-элементов на сцену

В методе create создаются два стандартных HTML-элемента div. Им задаются стили CSS для визуального оформления. Эти элементы являются чисто веб-компонентами и не являются частью графического контекста Canvas/WebGL Phaser по умолчанию.

Метод this.add.dom(x, y, element) интегрирует созданный HTML-элемент в игровое дерево отображения Phaser. Он возвращает объект DOMElement, который уже можно анимировать и которому можно задавать глубину. Первые два аргумента — это начальные координаты элемента на сцене.

const div1 = document.createElement('div');
div1.style = 'background-color: lime; width: 220px; height: 100px; font: 48px Arial; font-weight: bold';
div1.innerText = 'Phaser 3';

const div2 = document.createElement('div');
div2.style = 'background-color: yellow; width: 220px; height: 100px; font: 48px Arial; font-weight: bold';
div2.innerText = 'Phaser 3';

const element1 = this.add.dom(300, 0, div1);
const element2 = this.add.dom(400, 0, div2);

Управление глубиной (Depth) DOM-элементов

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

Метод setDepth(value) позволяет явно задать значение глубины для объекта DOMElement. Объекты с большим значением depth отрисовываются поверх объектов с меньшим значением. В примере element1.setDepth(2) гарантирует, что этот элемент будет поверх изображения Эйнштейна, которое по умолчанию имеет глубину 0.

Важно: второй элемент (element2) не получает явного setDepth. Его положение в порядке отрисовки относительно других объектов будет определяться порядком добавления в сцену и его значением глубины по умолчанию.

element1.setDepth(2);
this.add.image(400, 300, 'einstein'); // Этот image имеет глубину 0 по умолчанию.

Анимация DOM-элементов средствами Phaser

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

В примере создается твин, который одновременно воздействует на оба DOM-элемента (element1 и element2). Он анимирует их свойства `y(вертикальное положение),angle(вращение) иscaleX(масштаб по горизонтали). Параметрыloopиyoyo` создают непрерывную возвратно-поступательную анимацию.

this.tweens.add({
    targets: [ element1, element2 ], // Массив целевых объектов для анимации
    y: 600,
    angle: 200,
    duration: 3000,
    scaleX: 2,
    ease: 'Sine.easeInOut',
    loop: -1, // Бесконечный цикл
    yoyo: true // Анимация в прямом и обратном направлении
});

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

Использование this.add.dom() и setDepth() открывает мощный канал интеграции динамического HTML/CSS-контента в игровой процесс на Phaser. Вы можете создавать сложные интерфейсы, меню или эффекты, которые плавно анимируются и корректно взаимодействуют с графикой игры. Для экспериментов попробуйте: изменять значение глубины у element2, чтобы управлять тем, какой div поверх другого; анимировать другие CSS-свойства через твины (например, opacity или backgroundColor с помощью плагинов); или создать сложную композицию, где DOM-элементы служат интерактивными виджетами поверх игрового мира.