О чем этот пример
Использование DOM-элементов внутри игрового мира Phaser открывает новые возможности для создания гибридных интерфейсов, где стандартный HTML/CSS сочетается с игровой графикой и физикой. В этой статье мы разберем, как создавать динамические DOM-объекты, анимировать их в 3D-пространстве и реализовать плавное управление камерой для навигации по большой сцене. Этот подход полезен для создания интерактивных меню, HUD-элементов с современными CSS-эффектами или нестандартных визуальных представлений прямо внутри игрового контекста.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
controls;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('einstein', 'assets/pics/ra-einstein.png');
}
create ()
{
const smileys = [ '😀','😁','😂','🤣','😃','😄','😅','😆','😉','😊','😋','😎','😍','😘','😗','😙','😚','️🙂','🤗','🤩','🤔','🤨','😐','😑','😶','🙄','😏','😣','😥','😮','🤐','😯','😪','😫','😴','😌','😛','😜','😝','🤤','😒','😓','😔','😕','🙃','🤑','😲','☹️','🙁','😖','😞','😟','😤','😢','😭','😦','😧','😨','😩','🤯','😬','😰','😱','😳','🤪','😵','😡','😠','🤬','😷','🤒','🤕','🤢','🤮','🤧','😇','🤠','🤡','🤥','🤫','🤭','🧐','🤓','😈','👿','👹','👺','💀','👻','👽','🤖','💩','😺','😸','😹','😻','😼','😽','🙀','😿','😾' ];
// Create a bunch of random divs all over the place
let sf = 0.5;
let px = 64;
for (let i = 1; i <= 100; i++)
{
const x = Phaser.Math.Between(100, 2000);
const y = Phaser.Math.Between(100, 2000);
const element = this.add.dom(x, y, 'div', `font-size: ${px}px`, Phaser.Utils.Array.GetRandom(smileys)).setScrollFactor(sf);
element.setPerspective(800);
element.rotate3d.set(Math.random(), Math.random(), Math.random(), 0);
this.tweens.add({
targets: element.rotate3d,
w: 180,
duration: 2000,
ease: 'Sine.easeInOut',
loop: -1,
yoyo: true
});
if (i % 50 === 0)
{
sf += 0.1;
px += 32;
}
}
this.cameras.main.setBounds(0, 0, 4000, 4000);
const cursors = this.input.keyboard.createCursorKeys();
const controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
zoomIn: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q),
zoomOut: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E),
acceleration: 0.06,
drag: 0.0005,
maxSpeed: 1.0
};
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
}
update (time, delta)
{
this.controls.update(delta);
}
}
const config = {
type: Phaser.AUTO,
backgroundColor: '#2d2d2d',
scale: {
_mode: Phaser.Scale.FIT,
parent: 'phaser-example',
width: 800,
height: 600
},
dom: {
createContainer: true
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ресурсов
Класс Example расширяет Phaser.Scene. В методе preload() устанавливается базовый URL для загрузки и загружается одно изображение, хотя в данном примере оно не используется — вся графика создается через DOM.
Важный момент: для работы с DOM-элементами в конфигурации игры необходимо включить соответствующую опцию.
dom: {
createContainer: true
}
Без этого контейнера DOM-элементы не будут добавлены на страницу.
Создание массива DOM-элементов
В методе create() определяется массив смайликов — это обычные текстовые эмодзи. Далее в цикле создается 100 DOM-объектов.
Ключевой метод для добавления DOM-ноды — this.add.dom(x, y, element, style, content). Он принимает координаты, имя HTML-элемента (например, 'div'), строку стилей и содержимое.
const element = this.add.dom(x, y, 'div', `font-size: ${px}px`, Phaser.Utils.Array.GetRandom(smileys)).setScrollFactor(sf);
Здесь Phaser.Utils.Array.GetRandom(smileys) случайным образом выбирает смайлик из массива. Метод .setScrollFactor(sf) определяет, насколько элемент будет привязан к движению камеры. Фактор 0.5 означает, что элемент будет двигаться вполовину медленнее камеры, создавая эффект параллакса.
Каждые 50 элементов фактор прокрутки (sf) и размер шрифта (px) увеличиваются, создавая градацию размеров и скоростей.
3D-анимация элементов
Phaser позволяет применять к DOM-элементам простые 3D-трансформации через CSS. Метод setPerspective(800) устанавливает точку схода для 3D-преобразований.
У каждого элемента есть свойство rotate3d — это объект Phaser.Math.Vector4, представляющий ось вращения (x, y, z) и угол (w). Изначально ось задается случайным вектором, а угол равен 0.
element.rotate3d.set(Math.random(), Math.random(), Math.random(), 0);
Для анимации используется система твинов Phaser. Твин нацелен на свойство `w(угол) вектораrotate3d` и плавно меняет его от 0 до 180 градусов и обратно, создавая бесконечное покачивание элемента.
this.tweens.add({
targets: element.rotate3d,
w: 180,
duration: 2000,
ease: 'Sine.easeInOut',
loop: -1,
yoyo: true
});
Phaser автоматически преобразует изменение вектора rotate3d в CSS-свойство transform: rotate3d().
Настройка камеры и границ мира
Поскольку элементы разбросаны по большой области (до 2000 пикселей), необходимо задать границы для камеры, чтобы она могла по ним перемещаться.
this.cameras.main.setBounds(0, 0, 4000, 4000);
Это определяет прямоугольную область мира размером 4000x4000 пикселей, за пределы которой камера выйти не сможет.
Плавное управление камерой с клавиатуры
Для плавного, инерционного управления камерой используется встроенный класс Phaser.Cameras.Controls.SmoothedKeyControl. Сначала создается объект конфигурации, который связывает клавиши с действиями камеры.
const controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
zoomIn: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q),
zoomOut: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E),
acceleration: 0.06,
drag: 0.0005,
maxSpeed: 1.0
};
Клавиши стрелок отвечают за движение, Q и E — за зум. Параметры acceleration (ускорение), drag (сопротивление) и maxSpeed (максимальная скорость) тонко настраивают 'физику' движения камеры, делая его приятным и нерезким.
Экземпляр контрола создается и сохраняется в свойстве сцены, а в методе update() каждый кадр вызывается его метод update(delta), передающий дельту времени для плавных расчетов движения.
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
update (time, delta)
{
this.controls.update(delta);
}
Что попробовать дальше
Комбинация DOM-элементов с игровым движком позволяет легко интегрировать веб-технологии в игровой процесс. Вы можете экспериментировать: замените смайлики на сложные HTML-виджеты с CSS-анимациями, привяжите вращение элементов к положению камеры или скорости игрока, создайте интерактивный DOM-объект, реагирующий на клики. Используйте SmoothedKeyControl не только для камеры, но и для управления объектами, где требуется плавный, инерционный разгон и торможение.
