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

При разработке игр с нестандартной графикой — например, спрайтами непрямоугольной формы — часто возникает задача сделать перетаскивание интуитивным. Обычный прямоугольный хитбокс заставляет игрока цепляться за пустые области, что выглядит неестественно. Пример демонстрирует, как в 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 draggable from any pixel that has an alpha value >= 1
        const sprite1 = this.add.sprite(400, 200, 'logo').setInteractive({ pixelPerfect: true, draggable: true });

        //  This sprite is draggable from any pixel that has an alpha value >= 120 (i.e. the left side of the sprite)
        const sprite2 = this.add.sprite(400, 400, 'logoAlpha').setInteractive({ pixelPerfect: true, alphaTolerance: 120, draggable: true });

        sprite1.on('drag', function (pointer, dragX, dragY)
        {

            this.x = dragX;
            this.y = dragY;

        });

        sprite2.on('drag', function (pointer, dragX, dragY)
        {

            this.x = dragX;
            this.y = dragY;

        });
    }
}

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

const game = new Phaser.Game(config);

Загрузка ресурсов: подготовка двух спрайтов

В методе preload загружаются два изображения. Ключевое отличие — наличие альфа-канала (прозрачности). Первый спрайт (logo) имеет чёткие, полностью непрозрачные границы. Второй (logoAlpha) — градиентную прозрачность, где левая часть почти непрозрачна, а правая постепенно исчезает.

this.load.image('logo', 'assets/sprites/phaser3-logo.png');
this.load.image('logoAlpha', 'assets/sprites/phaser3-logo-alpha.png');

Создание интерактивных объектов: настройка pixelPerfect

В методе create создаются два спрайта. Оба делаются интерактивными и перетаскиваемыми с помощью setInteractive. Ключевой параметр — pixelPerfect: true. Он активирует проверку альфа-канала пикселя под курсором.

Для первого спрайта используется настройка по умолчанию. Перетащить его можно только за пиксели с альфой >= 1 (т.е., полностью непрозрачные).

Для второго спрайта добавляется параметр alphaTolerance: 120. Это пороговое значение альфа-канала (от 0 до 255). Теперь цепляться можно за пиксели с альфой >= 120, что включает в себя левую, более плотную часть спрайта с градиентом.

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

Обработка события drag: обновление позиции

На каждый спрайт вешается обработчик события drag. Это событие генерируется при каждом перемещении мыши или касания во время перетаскивания. В функцию-обработчик передаются указатель (pointer) и рассчитанные координаты (dragX, dragY), куда следует переместить центр спрайта.

Код внутри обработчика прост: он обновляет координаты `xиyспрайта, заставляя его следовать за курсором. Обратите внимание, что в функции используетсяthis`, который ссылается на сам спрайт, так как используется обычная function declaration.

sprite1.on('drag', function (pointer, dragX, dragY) {
    this.x = dragX;
    this.y = dragY;
});

Конфигурация игры: базовая настройка

Объект config определяет основные параметры игры: тип рендерера (Phaser.AUTO), контейнер в DOM, размеры холста, цвет фона и стартовую сцену. Это стандартная конфигурация для большинства примеров Phaser.

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

Инициализация игры происходит созданием нового экземпляра Phaser.Game с этой конфигурацией.

const game = new Phaser.Game(config);

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

Использование pixelPerfect и alphaTolerance позволяет создавать точные и приятные механики перетаскивания для сложных графических элементов. Это избавляет игрока от раздражения при взаимодействии с «невидимыми» частями спрайта. Для экспериментов попробуйте

  1. Применить этот подход к спрайту-призраку с низкой общей непрозрачностью
  2. Динамически менять alphaTolerance в зависимости от состояния объекта (например, чтобы повреждённый предмет было сложнее взять)
  3. Комбинировать pixelPerfect с другими параметрами setInteractive, такими как hitArea, для создания сложных составных хитбоксов