О чем этот пример
В динамичных 2D-играх камера часто движется, вращается и зумирует, следуя за игроком. Но элементы интерфейса (HUD), такие как шкалы здоровья или индикаторы, должны оставаться стабильными на экране. В этой статье разберем, как использовать объект `Stamp` в Phaser для создания HUD, который полностью игнорирует преобразования камеры и всегда отображается в одном месте экрана. Это практичный подход к разделению игрового мира и пользовательского интерфейса.
Версия 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('space3', 'assets/skies/space3.png');
this.load.atlas('hud', 'assets/ui/dark-ui.png', 'assets/ui/dark-ui.json');
this.load.atlas('atlas', 'assets/atlas/megaset-1.png', 'assets/atlas/megaset-1.json');
}
create ()
{
const halfWidth = this.scale.width / 2;
const halfHeight = this.scale.height / 2;
// Create a background image that won't move with the camera,
// but will still rotate with perspective.
this.add.image(halfWidth, halfHeight, 'space3').setScale(2).setScrollFactor(0.05);
const total = 10;
for (let i = 0; i < total; i++)
{
const depth = 1 / (total - i);
this.add.image(Phaser.Math.Between(0, this.scale.width), Phaser.Math.Between(0, this.scale.height), 'atlas', 'titan-mech')
.setScale(depth)
.setScrollFactor(depth);
}
// Add a HUD using Stamps which don't react to Camera transforms at all.
const barHeight = this.scale.height - 32;
this.add.stamp(halfWidth, barHeight, 'hud', 'track-empty');
this.bar = this.add.stamp(halfWidth, barHeight, 'hud', 'track-red');
}
update (time, delta)
{
this.bar.scaleX = Math.sin(time / 200);
this.cameras.main
.setScroll(Math.cos(time / 1000) * 400, Math.sin(time / 1000) * 200)
.setRotation(Math.sin(time / 500) * 0.1)
.setZoom(1 + Math.sin(time / 2000) * 0.1);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое Stamp и зачем он нужен?
Stamp — это специальный игровой объект в Phaser, который не подвержен влиянию преобразований камеры. В отличие от обычных Image или Sprite, которые могут двигаться, вращаться и масштабироваться вместе с камерой, Stamp всегда отображается в фиксированных координатах экрана. Это делает его идеальным для элементов HUD, которые должны быть постоянно видны игроку.
В исходном примере мы создаем фоновое изображение, которое немного движется с камерой (параллакс-эффект), и несколько спрайтов, которые полностью следуют за камерой. А для HUD используем Stamp, чтобы панель оставалась на месте.
Настройка сцены и загрузка ресурсов
В методе preload() загружаются необходимые ресурсы: фон, атлас для интерфейса и атлас для игровых объектов. Атласы позволяют эффективно хранить множество текстур в одном изображении.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('space3', 'assets/skies/space3.png');
this.load.atlas('hud', 'assets/ui/dark-ui.png', 'assets/ui/dark-ui.json');
this.load.atlas('atlas', 'assets/atlas/megaset-1.png', 'assets/atlas/megaset-1.json');
}
Создание игрового мира с параллакс-эффектом
В create() сначала добавляем фоновое изображение. Важный момент: мы устанавливаем setScrollFactor(0.05). Это означает, что фон будет двигаться с камерой, но только на 5% от её перемещения, создавая эффект глубины (параллакс).
Затем в цикле создаем несколько спрайтов мехов. Каждому присваивается свой depth (глубина), который влияет на масштаб и scrollFactor. Объекты с меньшим depth (дальние) меньше масштабированы и меньше двигаются с камерой, что усиливает ощущение 3D-пространства.
const total = 10;
for (let i = 0; i < total; i++)
{
const depth = 1 / (total - i);
this.add.image(Phaser.Math.Between(0, this.scale.width), Phaser.Math.Between(0, this.scale.height), 'atlas', 'titan-mech')
.setScale(depth)
.setScrollFactor(depth);
}
Создание статичного HUD с помощью Stamp
Здесь происходит ключевое действие. Мы создаем два объекта Stamp для HUD: пустую дорожку и заполняющую её красную полосу. Они добавляются методом this.add.stamp(). Координаты рассчитываются так, чтобы HUD был закреплен внизу экрана.
Поскольку это Stamp, они полностью игнорируют любые будущие преобразования камеры (прокрутку, вращение, зум) и всегда остаются на одном месте экрана. Красная полоса сохраняется в свойстве this.bar для дальнейшей анимации.
const barHeight = this.scale.height - 32;
this.add.stamp(halfWidth, barHeight, 'hud', 'track-empty');
this.bar = this.add.stamp(halfWidth, barHeight, 'hud', 'track-red');
Анимация камеры и HUD в update()
В методе update() происходят две ключевые анимации. Во-первых, масштаб красной полосы HUD по оси X (this.bar.scaleX) меняется по синусоиде, создавая эффект пульсации. Это демонстрирует, что Stamp можно анимировать независимо от камеры.
Во-вторых, камера активно преобразуется: она движется по кругу (setScroll), вращается (setRotation) и слегка зумирует (setZoom). Все эти изменения затрагивают фон и спрайты мехов, но абсолютно не влияют на Stamp-объекты HUD.
update (time, delta)
{
this.bar.scaleX = Math.sin(time / 200);
this.cameras.main
.setScroll(Math.cos(time / 1000) * 400, Math.sin(time / 1000) * 200)
.setRotation(Math.sin(time / 500) * 0.1)
.setZoom(1 + Math.sin(time / 2000) * 0.1);
}
Что попробовать дальше
Использование Stamp — это чистый и эффективный способ создать неподвижный HUD в Phaser. Он полностью изолирован от хаотичного движения игровой камеры, что критически важно для удобства игрока. Для экспериментов попробуйте: создать сложный HUD из нескольких Stamp-объектов (иконки, кнопки), добавить им интерактивность через setInteractive, или использовать этот подход для статичных элементов меню паузы, которые должны появляться поверх всего.
