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

При разработке игр часто возникает потребность в сложном управлении, где разные кнопки мыши выполняют разные действия. Например, левая кнопка — атаковать, правая — защищаться, а их одновременное нажатие — использовать особое умение. Встроенный API 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.spritesheet('balls', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
    }

    create ()
    {
        const graphics = this.add.graphics();

        let color = 0xffff00;
        const thickness = 2;
        const alpha = 1;

        //  Events

        let sx = 0;
        let sy = 0;
        let draw = false;

        //  Stop the right-click from triggering the context menu
        //  You can also set this in the game config
        this.input.mouse.disableContextMenu();

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

            sx = pointer.x;
            sy = pointer.y;
            draw = true;

            if (pointer.leftButtonDown() && pointer.rightButtonDown())
            {
                color = 0x00ffff;
            }
            else if (pointer.leftButtonDown())
            {
                color = 0xffff00;
            }
            else if (pointer.rightButtonDown())
            {
                color = 0x00ff00;
            }

        });

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

            draw = false;

        });

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

            if (draw && pointer.noButtonDown() === false)
            {
                graphics.clear();
                graphics.lineStyle(thickness, color, alpha);
                graphics.strokeRect(sx, sy, pointer.x - sx, pointer.y - sy);
            }

        });
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и отключение контекстного меню

Первым делом в методе create() создается объект Graphics для рисования примитивов. Ключевой момент — отключение стандартного контекстного меню браузера, которое появляется при клике правой кнопкой мыши. Это можно сделать двумя способами: через глобальную конфигурацию игры или непосредственно для объекта мыши.

// Способ 1: В конфиге игры (используется в примере)
const config = {
    // ... другие настройки
    disableContextMenu: true
};
// Способ 2: Через API в методе create()
this.input.mouse.disableContextMenu();

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

Обработка события нажатия (pointerdown)

Основная логика определения кнопки заложена в обработчике события 'pointerdown'. Событие передает объект pointer, который содержит методы для проверки состояния кнопок: leftButtonDown(), rightButtonDown(), middleButtonDown().

Код фиксирует начальные координаты (sx, sy) и в зависимости от комбинации нажатых кнопок выбирает цвет для рисования.

this.input.on('pointerdown', pointer => {
    sx = pointer.x;
    sy = pointer.y;
    draw = true; // Разрешаем рисование

    if (pointer.leftButtonDown() && pointer.rightButtonDown()) {
        color = 0x00ffff; // Голубой для обеих кнопок
    } else if (pointer.leftButtonDown()) {
        color = 0xffff00; // Желтый для левой
    } else if (pointer.rightButtonDown()) {
        color = 0x00ff00; // Зеленый для правой
    }
});

Обратите внимание на порядок условий: проверка на одновременное нажатие идет первой. Если поменять условия местами, логика сломается, так как при одновременном нажатии leftButtonDown() вернет true и будет выбран цвет только для левой кнопки.

Обработка движения с зажатой кнопкой (pointermove)

Рисование происходит динамически, при движении мыши с зажатой кнопкой. Обработчик 'pointermove' проверяет флаг draw и то, что хотя бы одна кнопка все еще нажата (с помощью pointer.noButtonDown() === false).

this.input.on('pointermove', pointer => {
    if (draw && pointer.noButtonDown() === false) {
        graphics.clear(); // Очищаем предыдущий рисунок
        graphics.lineStyle(thickness, color, alpha);
        // Рисуем прямоугольник от начальной точки до текущей
        graphics.strokeRect(sx, sy, pointer.x - sx, pointer.y - sy);
    }
});

Важно очищать (clear()) графику перед каждым новым кадром рисования, иначе все предыдущие прямоугольники останутся на экране. Метод strokeRect рисует только контур фигуры.

Завершение рисования (pointerup)

Когда пользователь отпускает кнопку мыши, срабатывает событие 'pointerup'. Его обработчик очень прост — он сбрасывает флаг draw, что останавливает процесс рисования в обработчике pointermove.

this.input.on('pointerup', () => {
    draw = false;
});

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

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

Использование методов pointer.leftButtonDown() и pointer.rightButtonDown() дает вам полный контроль над вводом с мыши, позволяя создавать сложные и многоуровневые интерфейсы взаимодействия. Для экспериментов попробуйте

  1. Добавить реакцию на среднюю кнопку мыши (middleButtonDown())
  2. Реализовать «ластик» при зажатой определенной клавише клавиатуры
  3. Изменить логику так, чтобы цвет прямоугольника менялся динамически при переключении нажатых кнопок во время движения мыши