О чем этот пример

При разработке игр часто возникают ситуации, когда необходимо сделать кликабельными только непрозрачные части спрайта. Например, у сложного игрового персонажа или предмета неправильной формы. Обычное прямоугольное ограничивающее тело (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)), чтобы создать сложную логику взаимодействия.