О чем этот пример
При разработке игр часто возникают ситуации, когда необходимо сделать кликабельными только непрозрачные части спрайта. Например, у сложного игрового персонажа или предмета неправильной формы. Обычное прямоугольное ограничивающее тело (bounding box) для взаимодействия здесь не подходит — оно будет регистрировать события даже при наведении на прозрачные края изображения. Phaser предоставляет мощный механизм Pixel Perfect, который позволяет настраивать интерактивность с точностью до пикселя, основываясь на значении альфа-канала. Это делает взаимодействие с игровыми объектами более точным и интуитивно понятным для игрока.
Версия 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('logo', 'assets/sprites/phaser3-logo.png');
this.load.image('logoAlpha', 'assets/sprites/phaser3-logo-alpha.png');
}
create ()
{
// This sprite is clickable on any pixel that has an alpha value >= 1
const sprite1 = this.add.sprite(400, 200, 'logo').setInteractive({ pixelPerfect: true });
// This sprite is clickable on any pixel that has an alpha value >= 100 (i.e. the left side of the sprite)
const sprite2 = this.add.sprite(400, 400, 'logoAlpha').setInteractive(this.input.makePixelPerfect(100));
const text = this.add.text(10, 10, 'Click either of the sprites', { font: '16px Courier', fill: '#000000' });
this.input.on('pointermove', () =>
{
text.setText('');
});
sprite1.on('pointermove', (pointer, x, y, event) =>
{
text.setText('Moved over Sprite 1');
event.stopPropagation();
});
sprite2.on('pointermove', (pointer, x, y, event) =>
{
text.setText('Moved over Sprite 2');
event.stopPropagation();
});
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#efefef',
scene: Example
};
const game = new Phaser.Game(config);
Что такое Pixel Perfect и зачем он нужен?
Pixel Perfect (пиксельная точность) — это режим интерактивности в Phaser, при котором событие (например, клик или наведение) срабатывает только при взаимодействии с непрозрачными пикселями спрайта. Механика проверяет альфа-значение (прозрачность) конкретного пикселя под курсором.
Это особенно полезно для: * Спрайтов сложной, не прямоугольной формы (звезды, кристаллы, персонажи). * UI-элементов с закругленными краями или внутренними прозрачными областями. * Создания более отзывчивого и «честного» геймплея, когда игрок взаимодействует именно с видимой частью объекта.
Без этой настройки Phaser использует прямоугольную область (bounding box) всего изображения, включая прозрачные пиксели по краям.
Базовое использование: `pixelPerfect: true`
Самый простой способ включить пиксельную точность — передать объект конфигурации в метод setInteractive(). Установите опцию pixelPerfect в true. В этом режиме событие сработает только если альфа-канал пикселя под курсором имеет значение больше или равное 1 (то есть пиксель не полностью прозрачный).
const sprite1 = this.add.sprite(400, 200, 'logo').setInteractive({ pixelPerfect: true });
В примере выше sprite1 (обычный логотип Phaser) будет реагировать на наведение курсора только по его видимой части. Это поведение по умолчанию для пиксельной проверки.
Расширенная настройка: `makePixelPerfect(alphaTolerance)`
Иногда стандартного порога в 1 альфа-значения бывает недостаточно. Например, если у вашего спрайта есть полупрозрачные градиенты или мягкие тени, и вы хотите, чтобы они тоже считались «кликабельными». Для тонкой настройки используется метод this.input.makePixelPerfect().
Этот метод создает специальный объект конфигурации, который также передается в setInteractive(). Единственный аргумент метода — alphaTolerance (порог прозрачности).
const sprite2 = this.add.sprite(400, 400, 'logoAlpha').setInteractive(this.input.makePixelPerfect(100));
В примере sprite2 использует изображение logoAlpha, у которого левая часть более прозрачная. Установив alphaTolerance: 100, мы говорим движку: «Считай пиксель активным, если его альфа-значение >= 100». Таким образом, наведение на левую, более прозрачную половину спрайта не вызовет события.
Обработка событий и остановка всплытия (stopPropagation)
В примере показана обработка события pointermove (движение курсора). Обратите внимание на два уровня обработки:
1. **Глобальный обработчик на this.input**: очищает текст при любом движении мыши по холсту.
2. **Локальные обработчики на спрайтах**: выводят сообщение о наведении на конкретный спрайт.
Ключевой момент — вызов event.stopPropagation() внутри обработчиков спрайтов.
sprite1.on('pointermove', (pointer, x, y, event) => {
text.setText('Moved over Sprite 1');
event.stopPropagation(); // Останавливаем всплытие события
});
Этот вызов предотвращает «всплытие» события до глобального обработчика this.input.on('pointermove', ...). Без него сообщение на спрайте моментально очистилось бы глобальным обработчиком, и мы бы ничего не увидели. stopPropagation() — стандартный прием для управления потоком событий в сложных сценах.
Что попробовать дальше
Pixel Perfect в Phaser — это простой, но мощный инструмент для повышения качества взаимодействия в игре. Он избавляет от проблем с «невидимыми» прозрачными областями и делает клики более точными.
**Идеи для экспериментов:**
* Создайте спрайт-пазл, где собирать можно только непрозрачные фрагменты.
* Реализуйте кастомный UI с кнопками неправильной формы, используя разные значения alphaTolerance для разных состояний (наведение, отжатие).
* Скомбинируйте пиксельную точность с другими типами хитбоксов (например, setInteractive(new Phaser.Geom.Circle(...), Phaser.Geom.Circle.Contains)), чтобы создать сложную логику взаимодействия.
