О чем этот пример
Хотите добавить в игру сложные интерфейсы, формы или динамический текст с полной поддержкой CSS? Стандартные текстовые объекты Phaser имеют ограничения по стилизации. Выход — использование DOM-элементов прямо на игровом холсте. Это открывает доступ ко всем возможностям HTML и CSS, сохраняя привязку элемента к игровой сцене, физике и системам анимации. В этой статье мы разберем пример, где контейнер объединяет DOM-элемент с обычным игровым объектом (прямоугольником) и анимирует их как единое целое. Вы научитесь создавать гибридные интерфейсы, которые идеально вписываются в геймплей.
Версия 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 div = document.createElement('div');
div.style = 'background-color: rgba(0,255,0,0.2); width: 250px; height: 100px; font: 48px Arial; font-weight: bold';
div.innerText = 'Phaser 3';
const container = this.add.container(400, 300);
const element = this.add.dom(0, 0, div);
const rect = this.add.rectangle(0, 0, 16, 16, 0xff00ff);
// var element = this.add.dom(400, 300, div).setOrigin(0);
// var rect = this.add.rectangle(400, 300, 16, 16, 0xff00ff);
container.add([ element, rect ]);
this.tweens.add({
targets: container,
angle: 360,
duration: 12000,
loop: -1
});
this.tweens.add({
targets: element,
duration: 3000,
_angle: 360,
scaleX: 2,
scaleY: 2,
ease: 'Sine.easeInOut',
loop: -1,
yoyo: true
});
}
}
const config = {
type: Phaser.AUTO,
scale: {
_mode: Phaser.Scale.WIDTH_CONTROLS_HEIGHT,
parent: 'phaser-example',
width: 800,
height: 600
},
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
dom: {
createContainer: true
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка конфигурации
Ключ к работе с DOM — правильная конфигурация игры. В объекте config необходимо явно указать, что Phaser должен создать контейнер для DOM-элементов. Без этого система add.dom не будет работать.
Также в этом примере используется особый режим масштабирования Phaser.Scale.WIDTH_CONTROLS_HEIGHT, который автоматически подстраивает высоту холста под ширину родительского элемента, сохраняя пропорции.
const config = {
type: Phaser.AUTO,
scale: {
_mode: Phaser.Scale.WIDTH_CONTROLS_HEIGHT,
parent: 'phaser-example',
width: 800,
height: 600
},
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
dom: {
createContainer: true // Важно! Активирует подсистему DOM
},
scene: Example
};
const game = new Phaser.Game(config);
Создание и стилизация DOM-элемента
В методе create мы создаем обычный HTMLDivElement с помощью стандартного Web API. Его можно стилизовать любым CSS — в примере задан полупрозрачный зеленый фон, размеры и жирный шрифт.
Этот элемент пока существует отдельно от игрового мира. Чтобы встроить его в сцену Phaser, используется фабричный метод this.add.dom(x, y, element). Он возвращает специальный игровой объект типа DOMElement, который оборачивает наш div и управляет его позицией, видимостью и трансформациями внутри игры.
const div = document.createElement('div');
div.style = 'background-color: rgba(0,255,0,0.2); width: 250px; height: 100px; font: 48px Arial; font-weight: bold';
div.innerText = 'Phaser 3';
const element = this.add.dom(0, 0, div);
Объединение объектов в контейнер
Одна из мощнейших фич примера — использование Container. Контейнер (this.add.container) — это объект, который группирует другие игровые объекты, позволяя трансформировать их (двигать, вращать, масштабировать) как одно целое.
В контейнер помещаются и DOM-элемент element, и обычный прямоугольник rect, созданный через this.add.rectangle. Изначально они позиционируются в точке (0,0) относительно центра контейнера, который мы разместили в (400,300).
const container = this.add.container(400, 300);
const rect = this.add.rectangle(0, 0, 16, 16, 0xff00ff);
container.add([ element, rect ]);
Совместная и независимая анимация
Теперь можно анимировать всю группу объектов. Первый твин вращает весь контейнер на 360 градусов за 12 секунд, и DOM-элемент с прямоугольником вращаются вместе, как приклеенные.
Второй твин применяется точечно к самому объекту element. Обратите внимание на префикс нижнего подчеркивания в свойстве _angle. Для DOM-элементов некоторые стандартные свойства твинов требуют такого префикса. Этот твин независимо от контейнера масштабирует и вращает только DOM-блок, создавая сложную составную анимацию.
this.tweens.add({
targets: container,
angle: 360,
duration: 12000,
loop: -1
});
this.tweens.add({
targets: element,
duration: 3000,
_angle: 360, // Специальное свойство для DOM-элемента
scaleX: 2,
scaleY: 2,
ease: 'Sine.easeInOut',
loop: -1,
yoyo: true
});
Система координат и точка вращения
Важно понимать иерархию координат. DOM-элемент и прямоугольник добавлены в контейнер со смещением (0,0). Это значит, их локальная точка (0,0) привязана к позиции контейнера в мире (400,300). Все трансформации контейнера (например, вращение) происходят относительно его точки начала координат.
Если закомментировать создание контейнера и раскомментировать альтернативные строки в исходнике, объекты будут добавлены прямо на сцену с абсолютными координатами (400,300). В этом случае их нельзя будет сгруппировать для совместного вращения одним твином — они будут независимыми объектами в мире.
// Альтернатива без контейнера:
// var element = this.add.dom(400, 300, div).setOrigin(0);
// var rect = this.add.rectangle(400, 300, 16, 16, 0xff00ff);
Что попробовать дальше
Интеграция DOM-элементов через add.dom и их группировка в контейнерах — мощный прием для создания сложных UI внутри игр на Phaser 3. Вы получаете всю гибкость веб-технологий, не теряя преимуществ игрового движка.
**Идеи для экспериментов:**
1. Добавьте в div поле ввода (<input>) и обрабатывайте его события клавиатуры прямо в игре.
2. Используйте CSS-анимации или переходы (transition) внутри DOM-элемента одновременно с Phaser Tween.
3. Привяжите DOM-элемент к физическому телу (через контейнер) и посмотрите, как он будет вести себя при столкновениях.
4. Создайте сложный HUD с несколькими стилизованными блоками и анимируйте их появление через контейнеры.
