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

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

Версия 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.spritesheet('balls', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
    }

    create ()
    {
        this.input.mouse.disableContextMenu();

        this.input.on('pointerdown', function (pointer)
        {

            if (pointer.leftButtonDown() && pointer.rightButtonDown())
            {
                this.add.image(pointer.x, pointer.y, 'balls', 0);
            }
            else if (pointer.leftButtonDown())
            {
                this.add.image(pointer.x, pointer.y, 'balls', 1);
            }
            else if (pointer.rightButtonDown())
            {
                this.add.image(pointer.x, pointer.y, 'balls', 2);
            }

        }, this);

        this.input.on('pointerup', pointer =>
        {

            // console.log('up - left:', pointer.leftButtonDown(), 'right:', pointer.rightButtonDown(), 'event', pointer.event.button);

            if (pointer.event.button === 0)
            {
                console.log('Left button released');
            }
            else if (pointer.event.button === 1)
            {
                console.log('Middle button released');
            }
            else if (pointer.event.button === 2)
            {
                console.log('Right button released');
            }

        });
    }
}

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

const game = new Phaser.Game(config);

Блокировка контекстного меню и загрузка ассетов

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

Загружаем спрайтшит с шариками, который будет использоваться для визуальной обратной связи. Кадры в этом спрайтшите соответствуют разным типам кликов.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.spritesheet('balls', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
}

В методе create первым делом отключаем контекстное меню:

this.input.mouse.disableContextMenu();

Обработка события pointerdown: визуальная индикация

Событие 'pointerdown' генерируется в момент нажатия кнопки мыши (или касания). В обработчике этого события мы определяем, какая именно кнопка была нажата, и отображаем соответствующий кадр спрайтшита в координатах указателя.

Phaser предоставляет методы leftButtonDown() и rightButtonDown() у объекта pointer, которые возвращают true, если соответствующая кнопка удерживается нажатой в момент события.

Код логики: - Если нажаты одновременно левая и правая кнопки — показываем шарик с индексом 0 (красный). - Если нажата только левая кнопка — шарик с индексом 1 (зеленый). - Если нажата только правая кнопка — шарик с индексом 2 (синий).

this.input.on('pointerdown', function (pointer)
{
    if (pointer.leftButtonDown() && pointer.rightButtonDown())
    {
        this.add.image(pointer.x, pointer.y, 'balls', 0);
    }
    else if (pointer.leftButtonDown())
    {
        this.add.image(pointer.x, pointer.y, 'balls', 1);
    }
    else if (pointer.rightButtonDown())
    {
        this.add.image(pointer.x, pointer.y, 'balls', 2);
    }
}, this);

Обратите внимание на передачу контекста this в качестве третьего аргумента в this.input.on. Это гарантирует, что внутри функции-обработчика this будет указывать на текущую сцену (экземпляр Example), что необходимо для вызова this.add.image.

Обработка события pointerup: отладка в консоли

Событие 'pointerup' происходит при отпускании кнопки мыши. В данном примере оно используется не для графической обратной связи, а для отладки — вывода информации в консоль браузера.

Здесь демонстрируется альтернативный способ определения кнопки — через свойство pointer.event.button. Это нативное свойство объекта события мыши DOM: - `0` — левая кнопка. - `1` — средняя кнопка (колесико). - `2` — правая кнопка.

this.input.on('pointerup', pointer =>
{
    if (pointer.event.button === 0)
    {
        console.log('Left button released');
    }
    else if (pointer.event.button === 1)
    {
        console.log('Middle button released');
    }
    else if (pointer.event.button === 2)
    {
        console.log('Right button released');
    }
});

В закомментированной строке показан пример более детального логирования состояния кнопок через методы pointer.leftButtonDown() и прямой доступ к событию. Использование стрелочной функции (=>) в этом обработчике автоматически сохраняет контекст сцены, поэтому передавать this третьим аргументом не требуется.

Конфигурация игры: глобальная настройка контекстного меню

Помимо отключения контекстного меню через this.input.mouse.disableContextMenu(), Phaser позволяет сделать это на уровне конфигурации игры. Это более глобальный и надежный способ, который применяется ко всем сценам.

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    disableContextMenu: true, // Ключевой параметр
    scene: Example
};

Установка свойства disableContextMenu: true в объекте конфигурации гарантирует, что контекстное меню браузера будет заблокировано с момента инициализации игры, и вам не придется вызывать метод отключения в каждой сцене.

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

Phaser предлагает два основных способа работы с кнопками мыши: через удобные методы объекта pointer (такие как leftButtonDown()) и через прямое обращение к нативному событию (pointer.event). Первый способ более абстрагирован и безопасен, второй дает доступ к специфическим деталям. Для отладки сложного взаимодействия, особенно при использовании комбинаций кнопок, крайне полезно логирование в событии 'pointerup'. Экспериментируйте: попробуйте добавить обработку средней кнопки (колесика) при событии 'pointerdown' или реализовать систему, где зажатие левой кнопки активирует одно действие, а последующее нажатие правой — модифицирует его (комбо-действие).