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

В Phaser обработка событий мыши по интерактивным объектам — это основа для создания отзывчивого геймплея. По умолчанию система событий оптимизирована для работы только с верхним объектом в стеке отображения, что не всегда удобно. В этой статье мы разберем, как управлять этим поведением, чтобы обрабатывать нажатия и отпускания кнопки мыши для всех объектов под курсором. Это особенно полезно для создания сложных интерфейсов, инвентарей или объектов с перекрывающейся графикой.

Версия 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('eye', 'assets/pics/lance-overdose-loader-eye.png');
    }

    create ()
    {
        const sprite1 = this.add.sprite(400, 300, 'eye').setInteractive();
        const sprite2 = this.add.sprite(450, 350, 'eye').setInteractive();
        const sprite3 = this.add.sprite(500, 400, 'eye').setInteractive();

        //  If you disable topOnly it will fire events for all objects the pointer is over, regardless of place on the display list
        this.input.setTopOnly(false);

        //  Events

        this.input.on('gameobjectdown', (pointer, gameObject) =>
        {
            gameObject.setTint(0xff0000);
        });

        this.input.on('gameobjectup', (pointer, gameObject) =>
        {
            gameObject.clearTint();
        });
    }
}

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

const game = new Phaser.Game(config);

Настройка интерактивных спрайтов

Первым шагом является создание интерактивных объектов в сцене. В Phaser любой объект, у которого вызван метод .setInteractive(), может обрабатывать события ввода, такие как клики мыши или касания.

const sprite1 = this.add.sprite(400, 300, 'eye').setInteractive();
const sprite2 = this.add.sprite(450, 350, 'eye').setInteractive();
const sprite3 = this.add.sprite(500, 400, 'eye').setInteractive();

Здесь мы создаем три спрайта с одинаковым изображением 'eye' и делаем каждый из них интерактивным. Теперь они могут реагировать на действия пользователя. Обратите внимание, что спрайты расположены с небольшим смещением, поэтому они частично перекрывают друг друга.

Управление режимом обработки событий: topOnly

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

this.input.setTopOnly(false);

Вызов this.input.setTopOnly(false) отключает этот режим. После этого события gameobjectdown и gameobjectup будут генерироваться для каждого интерактивного объекта, находящегося под указателем в момент действия, независимо от его позиции в списке отображения.

Обработка событий нажатия и отпускания

Основная логика реакции на действия пользователя реализуется через слушатели событий. В данном примере мы отслеживаем момент нажатия кнопки мыши на объекте и момент её отпускания.

this.input.on('gameobjectdown', (pointer, gameObject) =>
{
    gameObject.setTint(0xff0000);
});

При возникновении события 'gameobjectdown' (кнопка мыши нажата над объектом) функция-обработчик получает указатель pointer и сам объект gameObject. Внутри обработчика мы вызываем gameObject.setTint(0xff0000), чтобы залить спрайт красным цветом. Это дает визуальную обратную связь.

this.input.on('gameobjectup', (pointer, gameObject) =>
{
    gameObject.clearTint();
});

Событие 'gameobjectup' происходит, когда кнопка мыши отпущена над тем же объектом. Обработчик сбрасывает tint, возвращая спрайту исходный цвет. Важно, что при topOnly: false эти события будут вызваны для всех перекрывающихся спрайтов под курсором.

Практические аспекты и порядок событий

Когда topOnly установлен в false, порядок вызова обработчиков соответствует порядку объектов в списке отображения сцены (по умолчанию — порядок добавления). В нашем примере, если кликнуть в область пересечения всех трех спрайтов, сначала станет красным sprite1 (добавлен первым, находится "ниже"), затем sprite2, и, наконец, sprite3 (добавлен последним, находится "сверху").

Такое поведение позволяет реализовать сложную логику, например, одновременный выбор нескольких карт в руке игрока или активацию нескольких элементов интерфейса одним кликом.

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

Управление режимом topOnly и использование событий gameobjectdown/gameobjectup предоставляет гибкий контроль над взаимодействием в вашей игре. Для экспериментов попробуйте изменить порядок добавления спрайтов, использовать разные изображения или добавить логику, которая будет считать, сколько объектов было затронуто одним кликом. Это открывает двери к созданию более глубоких и интерактивных игровых механик.