О чем этот пример
Phaser позволяет интегрировать HTML-элементы прямо в игровой мир и наделять их физическими свойствами, как обычные спрайты. Это открывает уникальные возможности для создания гибридных интерфейсов, интерактивных меню или игровых объектов, чей внешний вид удобнее верстать средствами HTML и CSS. В этой статье мы разберем, как превратить DOM-узел в физическое тело, управлять его коллизиями и взаимодействовать с другими игровыми объектами.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
group;
element;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.html('smalldiv', 'assets/text/smallDiv.html');
this.load.image('ball', 'assets/sprites/shinyball.png');
}
create ()
{
this.element = this.add.dom(400, 300).createFromCache('smalldiv');
this.physics.add.existing(this.element, false);
this.group = this.physics.add.group({
key: 'ball',
frameQuantity: 30,
immovable: true
});
Phaser.Actions.PlaceOnRectangle(this.group.getChildren(), new Phaser.Geom.Rectangle(84, 84, 616, 416));
this.element.body.setOffset(-(this.element.displayWidth / 2), -(this.element.displayHeight / 2));
this.element.body.setVelocity(100, 200);
this.element.body.setBounce(1, 1);
this.element.body.setCollideWorldBounds(true);
}
update ()
{
this.physics.world.collide(this.element, this.group);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
dom: {
createContainer: true
},
physics: {
default: 'arcade',
arcade: {
debug: false
}
},
scene: Example
};
const game = new Phaser.Game(config);
Загрузка HTML-ассетов и настройка проекта
Перед началом работы необходимо убедиться, что проект Phaser сконфигурирован для работы с DOM. Для этого в конфигурации игры нужно включить опцию dom.createContainer. Без этого система DOM Phaser не будет инициализирована.
Также нужно загрузить HTML-файл как ассет. Phaser позволяет кэшировать HTML-структуры и создавать из них элементы в сцене.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
dom: {
createContainer: true // Обязательная настройка для работы с DOM
},
physics: {
default: 'arcade',
arcade: { debug: false }
},
scene: Example
};
В методе preload мы загружаем HTML-файл и изображение для других объектов.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.html('smalldiv', 'assets/text/smallDiv.html'); // Загрузка HTML-шаблона
this.load.image('ball', 'assets/sprites/shinyball.png');
}
Создание DOM-элемента и добавление физики
В методе create мы создаем DOM-элемент из закэшированного шаблона и помещаем его в координаты (400, 300). Ключевой шаг — добавление к этому элементу физического тела с помощью this.physics.add.existing. Второй параметр false указывает, что тело не будет статическим (immovable), то есть элемент сможет двигаться под воздействием сил и столкновений.
create ()
{
// Создание DOM-элемента из кэша 'smalldiv' в центре сцены
this.element = this.add.dom(400, 300).createFromCache('smalldiv');
// Наделение элемента физическим телом Arcade Physics
this.physics.add.existing(this.element, false);
}
После этого this.element становится полноценным физическим объектом, и мы можем обращаться к его свойству body для настройки.
Настройка физического тела DOM-объекта
По умолчанию точка привязки (origin) DOM-элемента находится в его центре, но физическое тело может быть смещено относительно этой точки. Чтобы коллизии работали корректно относительно визуальных границ элемента, нужно настроить offset (смещение) тела.
Также мы задаем начальную скорость, упругость (отскок) и включаем столкновение с границами мира.
// Корректировка смещения физического тела относительно центра элемента
this.element.body.setOffset(-(this.element.displayWidth / 2), -(this.element.displayHeight / 2));
// Задание начальной скорости по осям X и Y
this.element.body.setVelocity(100, 200);
// Установка коэффициента упругости (1 = идеальный отскок)
this.element.body.setBounce(1, 1);
// Включение столкновений с границами игрового мира
this.element.body.setCollideWorldBounds(true);
Создание группы статичных объектов для столкновений
Чтобы продемонстрировать взаимодействие, создадим группу из 30 спрайтов-шаров и расположим их по прямоугольной сетке. Группа создается как immovable: true, чтобы шары не двигались при столкновении с DOM-элементом.
// Создание физической группы из 30 неподвижных шаров
this.group = this.physics.add.group({
key: 'ball',
frameQuantity: 30,
immovable: true
});
// Расположение всех детей группы по прямоугольнику с отступами от краев
Phaser.Actions.PlaceOnRectangle(
this.group.getChildren(),
new Phaser.Geom.Rectangle(84, 84, 616, 416)
);
Обработка столкновений в игровом цикле
В методе update, который вызывается каждый кадр, необходимо проверять столкновения между DOM-элементом и группой шаров. Для этого используется метод this.physics.world.collide. Он автоматически обрабатывает физику столкновения между двумя объектами (или объектом и группой), учитывая их скорость, массу и другие параметры.
update ()
{
this.physics.world.collide(this.element, this.group);
}
Благодаря этому DOM-элемент будет отскакивать от шаров, создавая сложную траекторию движения.
Что попробовать дальше
Интеграция DOM-элементов в физический мир Phaser — мощный инструмент для создания динамических интерфейсов и гибридной графики. Вы можете экспериментировать: сделать DOM-элемент управляемым с клавиатуры, изменить его HTML-содержимое в реальном времени в ответ на столкновения или создать сложную цепочку взаимодействий между DOM-объектами и спрайтами. Помните, что для производительности стоит использовать такие элементы умеренно, так как их отрисовка отличается от Canvas/WebGL-рендеринга.
