О чем этот пример
Работая над игровым интерфейсом, часто нужно сделать кликабельной не простую кнопку, а целую группу объектов — например, иконку с подсветкой и текстом. Phaser Container позволяет объединять объекты, но по умолчанию он не интерактивен. В этой статье разберем, как назначить контейнеру собственную хит-область (hit area), чтобы обрабатывать события наведения и клика для всей группы сразу. Этот подход упрощает создание сложных интерактивных элементов, таких как меню, инвентарь или кастомизируемые кнопки.
Версия 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('buttonBG', 'assets/sprites/button-bg.png');
this.load.image('buttonText', 'assets/sprites/button-text.png');
}
create ()
{
const bg = this.add.image(0, 0, 'buttonBG');
const text = this.add.image(0, 0, 'buttonText');
const container = this.add.container(400, 300, [ bg, text ]).setAngle(-30);
container.setInteractive(new Phaser.Geom.Circle(0, 0, 60), Phaser.Geom.Circle.Contains);
this.tweens.add({
targets: container,
angle: 30,
duration: 3000,
ease: 'Sine.easeOut',
yoyo: true,
repeat: -1
});
this.tweens.add({
targets: text,
alpha: 0.5,
duration: 1000,
ease: 'Sine.easeOut',
yoyo: true,
repeat: -1
});
container.on('pointerover', () =>
{
bg.setTint(0x44ff44);
});
container.on('pointerout', () =>
{
bg.clearTint();
});
container.once('pointerup', function ()
{
this.destroy();
});
// Just to display the hit area, not actually needed to work
const graphics = this.add.graphics();
graphics.lineStyle(2, 0x00ffff, 1);
graphics.strokeCircle(container.x, container.y, container.input.hitArea.radius);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#010101',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Создание и настройка контейнера
В Phaser Container — это специальный игровой объект, который выступает в роли родителя для других объектов. Все дочерние элементы наследуют трансформации (позицию, угол, масштаб) контейнера. В примере мы создаем кнопку из двух изображений: фона (buttonBG) и текста (buttonText).
Сначала загружаем изображения в preload(), затем в create() создаем их как отдельные Image объекты с координатами (0, 0) относительно будущего контейнера.
const bg = this.add.image(0, 0, 'buttonBG');
const text = this.add.image(0, 0, 'buttonText');
Далее создаем сам контейнер, передавая ему координаты на сцене (400, 300) и массив дочерних элементов. Сразу поворачиваем его на -30 градусов для наглядности.
const container = this.add.container(400, 300, [ bg, text ]).setAngle(-30);
На этом этапе контейнер отображается, но не реагирует на действия мыши.
Назначение интерактивности и хит-области
Ключевой момент — метод setInteractive(). Он делает любой игровой объект, включая контейнер, чувствительным к событиям ввода. По умолчанию хит-область совпадает с прямоугольными границами объекта, что для повернутого контейнера неудобно. Мы можем задать свою, более подходящую геометрию.
В примере используется круглая хит-область (Phaser.Geom.Circle) с центром в (0, 0) относительно контейнера и радиусом 60 пикселей. Второй параметр — функция проверки попадания точки в эту область (Phaser.Geom.Circle.Contains).
container.setInteractive(new Phaser.Geom.Circle(0, 0, 60), Phaser.Geom.Circle.Contains);
Теперь события pointerover, pointerout и pointerup будут срабатывать, когда курсор находится в пределах этого круга, независимо от реальной формы дочерних спрайтов.
Обработка событий и визуальная обратная связь
После назначения интерактивности можно подписаться на события. В примере реализована стандартная логика для кнопки:
- При наведении (pointerover) фон окрашивается в зеленый оттенок с помощью setTint.
- При уходе курсора (pointerout) tint сбрасывается.
- При клике (pointerup) контейнер и все его дети уничтожаются методом destroy(). Обратите внимание на once, который гарантирует, что обработчик сработает только один раз.
container.on('pointerover', () => {
bg.setTint(0x44ff44);
});
container.on('pointerout', () => {
bg.clearTint();
});
container.once('pointerup', function () {
this.destroy();
});
Для наглядности в примере также добавлена анимация с помощью tweens: контейнер плавно качается, а текст мерцает. Это демонстрирует, что интерактивность и твины работают независимо и не конфликтуют.
Визуализация хит-области (для отладки)
При разработке полезно видеть границы области взаимодействия. Хотя для работы функционала это не требуется, пример показывает, как отрисовать контур хит-области с помощью графики (Graphics).
Радиус круга берется из свойства container.input.hitArea.radius, а его мировые координаты — из container.x и container.y. Это важно, потому что хит-область задавалась в локальных координатах контейнера.
const graphics = this.add.graphics();
graphics.lineStyle(2, 0x00ffff, 1);
graphics.strokeCircle(container.x, container.y, container.input.hitArea.radius);
Такой подход помогает точно настроить зону клика, особенно при использовании сложной геометрии вроде Polygon или Ellipse.
Что попробовать дальше
Использование Container с кастомной хит-областью — мощный паттерн для создания сложных интерактивных элементов в Phaser. Вы можете объединять спрайты, текст, частицы и анимации в единый кликабельный объект. Для экспериментов попробуйте: задать хит-область в форме Phaser.Geom.Rectangle для кнопок-прямоугольников; сделать область меньше визуальных элементов для более точного клика; или динамически менять hitArea в зависимости от состояния объекта, создавая эффект "активной зоны".
