О чем этот пример
В разработке игр часто возникает необходимость интегрировать классические HTML-элементы в игровой интерфейс — для отображения текста, кнопок или сложных виджетов. Phaser предоставляет для этого мощный инструмент — систему DOM-элементов. Этот пример демонстрирует не только добавление HTML-элементов на игровой холст с помощью `add.dom()`, но и важный аспект управления жизненным циклом сцен через методы `sleep()` и `start()`. Понимание этих механизмов позволяет создавать сложные интерфейсы и эффективно управлять ресурсами при переключении между разными состояниями игры.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class SceneA extends Phaser.Scene
{
constructor ()
{
super('a');
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('einstein', 'assets/pics/ra-einstein.png');
}
create ()
{
const div = document.createElement('div');
div.style = 'background-color: lime; width: 220px; height: 100px; font: 48px Arial; font-weight: bold';
div.innerText = 'Scene A';
let e = this.add.dom(400, 200, div);
this.add.image(400, 300, 'einstein');
var i = 0;
this.input.on('pointerdown', () => {
if (i === 0)
{
e.destroy();
i++;
}
else if (i === 1)
{
this.scene.sleep();
}
});
}
}
class SceneB extends Phaser.Scene
{
constructor ()
{
super('b');
}
create ()
{
const div = document.createElement('div');
div.style = 'background-color: lime; width: 220px; height: 100px; font: 48px Arial; font-weight: bold';
div.innerText = 'Scene B';
this.add.dom(400, 400, div);
this.add.image(400, 300, 'einstein').setScale(0.5);
this.input.once('pointerdown', () => {
this.scene.start('a');
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
dom: {
createContainer: true
},
scene: [ SceneA, SceneB ]
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ресурсов
Код начинается с объявления двух сцен: SceneA и SceneB. Ключевой этап — метод preload() в SceneA. В нем задается базовый URL для загрузки и загружается изображение, которое будет использоваться как фон. Важно, что SceneB не загружает ресурсы, а использует уже загруженные в памяти, демонстрируя общий доступ к ассетам.
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, которому задаются стили (цвет, размер, шрифт) и текстовое содержимое. Затем этот элемент добавляется на игровой холст с помощью метода this.add.dom(x, y, element). Этот метод интегрирует HTML-элемент в координатную систему Phaser, позволяя позиционировать его как любой другой игровой объект.
const div = document.createElement('div');
div.style = 'background-color: lime; width: 220px; height: 100px; font: 48px Arial; font-weight: bold';
div.innerText = 'Scene A';
let e = this.add.dom(400, 200, div);
Управление объектами и обработка ввода
В SceneA также добавляется изображение и создается обработчик события клика (pointerdown). При первом клике DOM-элемент `eполностью удаляется из сцены и памяти с помощью методаdestroy(). Это важно для предотвращения утечек памяти. При втором клике вызываетсяthis.scene.sleep()`. Этот метод не уничтожает сцену, а переводит её в спящий режим: она останавливает все свои системы (ввод, время, обновления), но сохраняет все созданные объекты и их состояние в памяти.
this.input.on('pointerdown', () => {
if (i === 0)
{
e.destroy();
i++;
}
else if (i === 1)
{
this.scene.sleep();
}
});
Запуск сцены и переключение между ними
Сцена SceneB запускается первой (так как она указана второй в массиве scene конфига — это особенность примера). В её методе create() также создается свой DOM-элемент и добавляется уменьшенное изображение. По клику она запускает SceneA заново с помощью this.scene.start('a'). Метод start() останавливает и уничтожает текущую сцену (SceneB), а затем запускает или пробуждает целевую сцену (SceneA). Если SceneA была в спящем режиме, она будет разбужена со всеми своими объектами (кроме уничтоженных), что может быть полезно для быстрого переключения контекстов без повторной инициализации.
this.input.once('pointerdown', () => {
this.scene.start('a');
});
Конфигурация игры и поддержка DOM
Для работы с DOM-элементами в Phaser необходима специальная настройка в конфигурационном объекте игры. Нужно добавить свойство dom с параметром createContainer: true. Это указывает Phaser создать отдельный контейнер для DOM-элементов, обеспечивая их корректное отображение поверх или под WebGL/Canvas-рендерингом.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
dom: {
createContainer: true // Ключевая настройка для работы с DOM
},
scene: [ SceneA, SceneB ]
};
Что попробовать дальше
Этот пример наглядно показывает интеграцию HTML в игровой контекст Phaser и тонкости управления сценами. DOM-элементы открывают путь к использованию богатых возможностей HTML/CSS для UI, а методы sleep() и start() позволяют гибко управлять производительностью и состоянием приложения. Для экспериментов попробуйте
- Добавить анимацию DOM-элементу через CSS или Phaser Tween
- Создать кнопку
resume()для пробуждения усыплённой сцены - Использовать
scene.switch()для переключения между сценами без их остановки
