О чем этот пример
При создании браузерных игр или интерактивных приложений на Phaser часто возникает необходимость интегрировать стандартные HTML-элементы в игровой мир с физикой. Однако, при добавлении физического тела к DOM-объекту, его размер может не соответствовать визуальным границам. Эта статья разбирает пример, который демонстрирует потенциальную проблему с определением размера тела для DOM-элементов в Arcade Physics и показывает, как вручную управлять этим параметром для корректных столкновений.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
element;
player;
cursors;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('block', 'assets/sprites/crate32.png');
this.load.html('body', 'assets/html/arcade-body.html');
}
create ()
{
// var data = this.cache.html.get('body');
const domElement = this.add.dom(400, 0).createFromCache('body')
.setOrigin(0);
this.physics.add.existing(domElement, false);
this.cursors = this.input.keyboard.createCursorKeys();
this.element = this.add.dom(300, 200, 'div', 'font-size: 96px', '💩')
.setOrigin(0);
this.physics.add.existing(this.element, false);
this.element.body.setCollideWorldBounds(true);
const element = this.add.dom(100, 100, 'div', 'font-size: 96px; background-color: #FFFFFF', '💩').setOrigin(0);
this.physics.add.existing(element, false);
element.body.setCollideWorldBounds(true)
// .setSize(element.width, element.height);
// console.log(element.displayWidth, element.displayHeight);
const box = this.physics.add.image(100, 100, 'block');
}
update ()
{
return;
this.element.body.setVelocity(0);
if (this.cursors.left.isDown)
{
this.element.body.setVelocityX(-300);
}
else if (this.cursors.right.isDown)
{
this.element.body.setVelocityX(300);
}
if (this.cursors.up.isDown)
{
this.element.body.setVelocityY(-300);
}
else if (this.cursors.down.isDown)
{
this.element.body.setVelocityY(300);
}
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
backgroundColor: '#0072bc',
scale: {
mode: Phaser.Scale.ScaleModes.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
},
width: 600,
height: 450,
dom: {
createContainer: true
},
physics: {
default: 'arcade',
arcade: {
debug: true
}
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ресурсов
В классе сцены Example определены свойства для хранения ссылок на DOM-элемент, игрока (который не используется в итоговой версии) и управления с клавиатуры.
В методе preload() загружаются два ключевых ресурса: спрайт в виде блока и HTML-шаблон. Обратите внимание, что базовый URL меняется на репозиторий с примерами Phaser.
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('block', 'assets/sprites/crate32.png');
this.load.html('body', 'assets/html/arcade-body.html');
Создание DOM-элементов с физикой
В методе create() происходит основная работа. Сначала из кеша загружается и добавляется в мир DOM-элемент с помощью this.add.dom. К этому элементу сразу добавляется физическое тело через this.physics.add.existing(). Параметр false указывает, что это не StaticBody.
Затем создаётся второй DOM-элемент — большой смайлик. Для него также создаётся физическое тело, и оно настраивается на столкновение с границами мира.
this.element = this.add.dom(300, 200, 'div', 'font-size: 96px', '💩').setOrigin(0);
this.physics.add.existing(this.element, false);
this.element.body.setCollideWorldBounds(true);
Создаётся третий, аналогичный элемент, и рядом с ним — обычный спрайт-блок для визуального сравнения.
const element = this.add.dom(100, 100, 'div', 'font-size: 96px; background-color: #FFFFFF', '💩').setOrigin(0);
this.physics.add.existing(element, false);
element.body.setCollideWorldBounds(true);
const box = this.physics.add.image(100, 100, 'block');
Проблема размера Arcade Body
Ключевая проблема, которую иллюстрирует этот пример, заключается в автоматическом определении размеров физического тела для DOM-элементов. Phaser может некорректно вычислить width и height для таких объектов.
В исходном коде есть закомментированная строка:
// .setSize(element.width, element.height);
Именно она является решением. Метод setSize() физического тела (Arcade.Body) позволяет вручную задать его ширину и высоту, чтобы они соответствовали визуальным границам DOM-элемента.
Без этой настройки тело может быть слишком большим или маленьким, что приведёт к некорректной работе столкновений, даже если debug: true в настройках физики будет включен. Отладочная отрисовка покажет реальные границы тела, которые могут не совпадать с контентом на экране.
Настройка игры и физики
Конфигурация игры включает обязательные настройки для работы с DOM и Arcade Physics.
Параметр dom.createContainer: true в конфиге активирует создание отдельного контейнера для DOM-элементов, что необходимо для их корректной работы внутри канваса Phaser. Во-вторых, в расширенных настройках Arcade Physics включён режим отладки (debug: true). В этом режиме физические тела отображаются цветными контурами, что бесценно для поиска проблем, подобных описанной в статье.
dom: {
createContainer: true
},
physics: {
default: 'arcade',
arcade: {
debug: true
}
}
Зачем нужен пустой update()?
Метод update() в примере содержит только инструкцию return;, что делает его пустым. Однако, ниже этого оператора закомментирован код, который реализует управление первым DOM-элементом (this.element) с клавиатуры.
if (this.cursors.left.isDown) {
this.element.body.setVelocityX(-300);
}
Этот код — наглядная инструкция, как можно управлять физическим телом, присоединённым к DOM-объекту, используя стандартный подход Phaser. Поскольку в данном примере цель — продемонстрировать проблему размера, а не управление, логика обновления отключена. Но она служит хорошим шаблоном для ваших экспериментов.
Что попробовать дальше
Главный вывод: при использовании DOM-элементов с Arcade Physics в Phaser 3 всегда проверяйте размер их физического тела в отладочном режиме. Если он не совпадает с визуальными границами, используйте метод body.setSize(width, height) для ручной коррекции. Для экспериментов попробуйте
- раскомментировать строку с
setSizeи увидеть, как изменится отладочный контур - убрать
return;изupdate()и поуправлять элементом с клавиатуры - создать столкновения (
collider) между DOM-элементами и обычными спрайтами, чтобы протестировать интерактивность
