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

При создании игр часто возникает задача сделать спрайт кликабельным. По умолчанию Phaser использует для этого ограничивающий прямоугольник (bounding box), что может приводить к ложным срабатываниям, если у спрайта прозрачные области. Механика пиксель-перфект (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' });

        sprite1.on('pointerdown', () =>
        {

            text.setText('Clicked Sprite 1');

        });

        sprite2.on('pointerdown', () =>
        {

            text.setText('Clicked Sprite 2');

        });
    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    backgroundColor: '#efefef',
    scene: Example
};

const game = new Phaser.Game(config);

Зачем нужен пиксель-перфект?

По умолчанию, при вызове .setInteractive() без параметров, Phaser делает спрайт интерактивным, используя его ограничивающий прямоугольник. Это означает, что клик будет засчитан, даже если вы попали в прозрачную область спрайта, что часто нежелательно.

Например, для звезды неправильной формы с прозрачным фоном игрок может кликнуть "мимо" и все равно активировать ее. Пиксель-перфект решает эту проблему, проверяя альфа-значение (прозрачность) конкретного пикселя, по которому был произведен клик.

// Стандартный интерактивный спрайт (реагирует на весь прямоугольник)
const sprite = this.add.sprite(x, y, 'star').setInteractive();

// Пиксель-перфект спрайт (реагирует только на непрозрачные пиксели)
const preciseSprite = this.add.sprite(x, y, 'star').setInteractive({ pixelPerfect: true });

Базовое использование: `pixelPerfect: true`

Самый простой способ включить пиксель-перфект проверку — передать объект конфигурации { pixelPerfect: true } в метод .setInteractive(). В этом режиме Phaser будет считать пиксель "кликабельным", если его альфа-значение больше или равно 1 (то есть пиксель не полностью прозрачный).

В примере из исходника первый спрайт sprite1 использует именно этот подход.

const sprite1 = this.add.sprite(400, 200, 'logo').setInteractive({ pixelPerfect: true });

sprite1.on('pointerdown', () => {
    text.setText('Clicked Sprite 1');
});

Теперь sprite1 будет реагировать на клик только если игрок попал в любую часть логотипа, где есть цвет (альфа >= 1).

Расширенная настройка с порогом альфа-канала

Иногда требуется более гибкий контроль. Например, вы хотите, чтобы область клика была еще меньше, реагируя только на достаточно "плотные" пиксели. Для этого Phaser предоставляет метод this.input.makePixelPerfect(alphaTolerance).

Этот метод создает объект конфигурации для .setInteractive(), который устанавливает пользовательский порог альфа-канала. Пиксель считается "попавшим", если его альфа-значение **больше или равно** указанному порогу.

// Пиксель-перфект с кастомным порогом альфа = 100
const configObject = this.input.makePixelPerfect(100);
const sprite2 = this.add.sprite(400, 400, 'logoAlpha').setInteractive(configObject);

В исходном примере sprite2 использует изображение logoAlpha, которое имеет градиент прозрачности. С порогом в 100 кликабельными станут только левые, более непрозрачные части логотипа. Правая, более прозрачная часть, на клики реагировать не будет.

Как это работает внутри?

Когда вы включаете пиксель-перфект, Phaser при обработке события указателя (мыши или касания) выполняет дополнительные шаги: 1. Определяет координаты клика относительно спрайта. 2. Извлекает данные пикселя (включая альфа-канал) из текстуры спрайта по этим координатам. 3. Сравнивает альфа-значение этого пикселя с порогом (по умолчанию 1 или заданным вами). 4. Событие pointerdown срабатывает только в случае прохождения проверки.

Важно помнить, что эта проверка требует дополнительных вычислений, поэтому ее стоит использовать точечно для важных объектов, а не для всех спрайтов в сцене.

Практические советы и нюансы

1. **Производительность**: Пиксель-перфект проверка дороже, чем проверка по прямоугольнику. Не применяйте ее ко всем объектам без необходимости. 2. **Масштабирование и поворот**: Проверка работает с текущим кадром текстуры. Если вы масштабируете (setScale) или поворачиваете спрайт, Phaser берет исходный пиксель из текстуры и применяет к нему преобразования. Это работает корректно. 3. **Анимации**: Для анимированных спрайтов проверка будет проводиться по пикселям текущего отображаемого кадра анимации. 4. **Область хитбокса**: Пиксель-перфект можно комбинировать с настройкой хитбокса через hitArea. Сначала проверяется попадание в hitArea, и только затем — пиксель-перфект проверка.

// Комбинация хитбокса и пиксель-перфект
const sprite = this.add.sprite(x, y, 'complexShape').setInteractive(
    { 
        hitArea: new Phaser.Geom.Circle(0, 0, 50),
        hitAreaCallback: Phaser.Geom.Circle.Contains,
        pixelPerfect: true // Дополнительная точная проверка
    }
);

Что попробовать дальше

Пиксель-перфект интерактивность — мощный инструмент для создания точного и приятного пользовательского взаимодействия в играх на Phaser. Он избавляет от ложных кликов по прозрачным областям, делая интерфейс более отзывчивым и предсказуемым. **Идеи для экспериментов:** 1. Создайте карту с островами сложной формы. Сделайте каждый остров кликабельным с pixelPerfect: true, чтобы игрок не мог кликнуть "в воду" вокруг острова. 2. Поэкспериментируйте с порогом alphaTolerance для спрайтов с эффектами полупрозрачности (дым, призраки). Это позволит кликать только по их ядру. 3. Реализуйте пазл, где части можно перетаскивать. Используйте пиксель-перфект, чтобы захватывать деталь было удобно, даже если у нее ажурные края.